PAVLŮ ZDENĚK
FormPrint Popis a zdrojové texty Pavlů 9.12.2014
Knihovna (dll) FormPrint. Zdrojový kód ukázka ve Visual Studio 2010 Zdrojový kód ukázka ve Visual Studio 2013 Knihovna (dll) umožnuje rychlý tisk formulářů .NET a ovládacích prvků na výchozí tiskárně windows. Knihovna je určena pro programátory .Net Framework v C# , C++/cli a VB . Umožňuje vybrat ovládací prvky, které nebudou vytištěny.Možnost tisku vlastních UserControl. Umožňuje volbu velikosti stránky a okrajů stránky. V Demo ukázce je jak zdrojový kód v C# tak i ukázka tisku na pdf tiskárnu. Webová stránka demo - ukázka tisku na pdf tiskárnu.
Str. 1
Ukázka tvorby zdrojového kódu knihoven (dll) FormPrint. Vítejte u ukázky, která vás uvede do problematiky tisku formuláře a ovládacích prvků pod Framework .NET. Tisk je realizován pomocí dll knihoven pro jednotlivé formulářové prvky, lze tedy odeslat celý formulář na tiskárnu. Výhoda tohoto řešení je snadná vazba s uživatelským programem na bázi .NET, snadná rozšiřitelnost o další ovládací prvky pouze dopsání kódu pro další prvek. Nevýhodou je vyšší složitost. Náš vytištěný formulář bude mít následující podobu:
Ukázka je rozdělena na tři samostatné části:
Uživatelská část (exe program) je libovolný program psaný v C++/cli, C# nebo VB.NET. Druhou částí je knihovna dll, řídící část psaná v C#, ta zprostředkuje vazbu mezi uživatelskou částí a naším programem. Třetí část je knihovna dll, výkonná část, která realizuje vlastní tisk. Je napsaná v C++/clr:safe samozřejmě může být napsaná c C# nebo VB.NET.
Mnoho z vás jistě namítne, proč nelze všechny části spojit v jediný soubor.exe a z jakého důvodu používám dvě knihovny dll. Důvod je zcela prostý. Pokud veškerý kód bude realizován v jediném souboru, jeho použití je problematické a změna kódu je velmi obtížná. Toto rozdělení má výhody, že pokud chceme do programu přidat další ovládací prvek, nezměníme původní již napsaný kód knihoven.dll, ani vlastního souboru (exe). Proč je jedna knihovny psaná v C# a druhá v C++/clr? Jeden z důvodů je opomíjenost C++/clr. Pro činnost je úplně jedno, zda dll knihovny vytvoříme v C#, C++/clr nebo VB.NET. Chci ukázat jak psát pod C++/clr. Tato ukázka
Str. 2
neobsahuje bloky "try{....}, catch{.....}, finally{...}", a nejsou použity proměnné typu "var". Na konci této ukázky je zdojový text ve Visual Studiu 2010 a 2013.
Jak jsou vytvářeny formuláře ?
Každý formulář má vlastnost Size (Width,Height), ta znamená velikost formuláře v pixelech. Pokud má formulář vlastnost Padding (Left, Top, Right, Button), to je velikost vnitřní mezery, pozadí formuláře v mezeře je převážně vyplněno Color.White. Proto vyplníme jako první celý vnitřek touto barvou. My neznáme velikost Paddingu, který uživatel nastaví. Uložíme novou velikost formuláře, která bude zmenšená o oblast Paddingu. Nová velikost bude vyplněna barvou pozadí BackColor. Na tuto barvu pozadí nakreslíme obrázek pozadí, pokud existuje BackgroundImage, jeho velikost a tvar jsou určeny rozložením BacgroundImageLayout(None, Title, Center, Stretch, Zoom). Další krokem bude vykreslení obrázku Image. Pokud existuje jeho zarovnání, je dané vlastností ImageAlign (TopLeft, ........ BottomRight ). Provedeme úpravu písma Font a nastavíme jeho barvu vlastnost ForeColor. Pokud má ovládací prvek vlastnost Multiline (text rozdělen do více řádků), úprava textu se bude lišit od jednořádkového textu. Některé ovládací prvky mají vlastnost TextAlign (Left, Right, Center), pak provedeme formátování textu pomocí funkce HorizontalAlignment(TextAlign). Pokud mají vlastnost TextAlign(TopLeft ..... BottomRight), bude provedeno formátování textu pomocí funkce ContentAlignment(TextAlign). Pokud ovládací prvek nemá vlastnost Multiline nebo je tato vlastnost false, pak musíme ořezat text. Nesmí přesahovat velikost Width zobrazované plochy formuláře. Vykreslíme text. Vykreslíme ohraničení formuláře BorderStyle (FixedSingle, Fixed3D, None ), pokud existuje, v původní velikosti Size.
Začínáme. Pro tisk LineShape, OvalShape a RectangleShape musíme mít knihovnu pro VS 2010: Microsoft.VisualBasic.PowerPacks.Vs, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a Location: C:\Program Files\Reference Assemblies\Microsoft\VBPowerPacks\v10.0\ File : Microsoft.VisualBasic.PowerPacks.Vs.dll
Pokud tuto knihovnu nemáte, stáhneme ji z následujícího umístění: http://download.microsoft.com/download/A/D/E/ADEFBFFF-2165-4F63-BB29DCE891B95CC7/VisualBasicPowerPacksSetup.exe?amp;clcid=0x804 Pozor – VS 2013 tuto knihovnu neobsahuje, je třeba knihovnu doinstalovat!
Vytvoříme si tři projekty: VS -> Soubor -> Nový projekt -> Visual C# -> Windows -> Formulářová aplikace Windows (projekt exe). Pro VS 10 nastavíme .NET framework 4, název libovolný, na příklad FormPrintDemo. VS -> Soubor -> Přidat -> Nový projekt -> Visual C# -> Windows -> Knihovna tříd. Pro VS 10 nastavíme .NET framework 4, název libovolný, na příklad MyClass1.
Str. 3
VS -> Soubor -> Přidat -> Nový projekt -> Jiné jazyky -> Visual C++ -> CLR -> Class Libray. Pro VS 10 nastavíme .NET framework 4, název libovolný, na příklad MyClass2. Kostru naší demo aplikace máme hotovou. Pokud vlastníme jinou verzi Visual Studia, postupujeme podobně.
Úpravy v class Form1: Přesuneme se na Form1 (návrh) a nastavíme vlastnost Size (950;600). Do pravého dolního rohu přidáme Button a do textu napíšeme Tisk. Nad Button přidáme CheckBox a do textu napíšeme Rámeček. Dvakrát klikneme na Button a přejdeme do okna zobrazení kódu. Přesuneme se na Průzkumník řešení -> FormPrintDemo -> Závislosti projektu Přesuneme se na Průzkumník řešení -> FormPrintDemo -> Odkazy -> Přidat odkaz Označíme MyClass1 a MyClass2, viz obrázek. Přesuneme se na Průzkumník řešení -> MyClass1 -> Odkazy -> Přidat odkaz Přidáme odkaz na System.Windows.Forms.
Str. 4
Reference na FormPrintDemo.
Str. 5
Kód upravíme takto: //Přejdeme na MyClass1 - Class1 - Zobrazit kód using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; namespace FormPrintDemo { public class Class1 { public Class1(Control ctrl){} public bool Ohraniceni { get; set; } public void MyPrint(){} } } //Tato úprava je nutná pro doplnění naší exe aplikace platným kódem. //Přejdeme na FormPrintDemo -> Form1 -> Zobrazit kód using System; using System.Windows.Forms; namespace FormPrintDemo { public partial class Form1 : Form { private FormPrintDemo.Class1 fp; public Form1() { InitializeComponent();
//tím, že do kostruktoru třídy Class1 vložíme this předáme třídě //kopii třídy Form1 reference na knihovnu tříd fp = new FormPrintDemo.Class1(this); this.checkBox1.Checked = false; //platné ohraničení } //Kliknutím na Button se zahájí tisk private void button1_Click(object sender, EventArgs e) { this.Visible = true; fp.Ohraniceni = this.checkBox1.Checked; fp.MyPrint(); } }//class }//namespace Žádný další kód na tento formulář nebudeme psát. Zde máme hotovo, pouze budeme na formulář přidávat ovládací prvky. V kolekci System.Windows.Forms.Control jsou uloženy veškeré ovládací prvky a jejich nastavení včetně vlastností samotného formuláře.
Str. 6
První dll knihovna: Provedeme malou úpravu projektu, přejdeme do MyClass1 -> Properties -> AssemblyInfo -> Zobrazit kód. using System.Reflection; using System.Runtime.InteropServices; using System; //stávající kód ponechat //doplnit můžeme jako poslední řádek kódu. //Označuje, zda je prvek programu kompatibilní se specifikace CLS (Common Language). [assembly: CLSCompliant(true)] //doplněno Nyní začínáme psát kód do MyClass1 -> Zobrazit kód. Jako první vytvoříme sekci instančních proměnných třídy MyClass1. Pokud to půjde, vždy budeme používat generické kolekce třídy: List
. #region "sekce instančních proměnných" public enum ParentControlPrinting { BeforeChilds = 0, AfterChilds = 1 }
private List<string> deletePrintControl; private Rectangle RecRodic = new Rectangle(0, 0, 0, 0); private DelegateforControls delegateforControls = new DelegateforControls(); private List delegatesforControls; private Control control = null; private Graphics graphics = null ; #endregion #region "sekce delegátů, struktur" // Struktura DelegateforControls v generické kolekci List (šablona) private struct DelegateforControls { internal string Typ { get; set; } //Třída ovládacího prvku internal ControlPrinting PrintFunction { get; set;} //volaná funkce } //delegovat poskytování funkcí tisku podle typu public delegate void ControlPrinting ( Control control, ParentControlPrinting typePrint, int locationX, int locationY, out bool scanForChildControls ); #endregion
Upravíme odkazy ve třídě MyClass1: using using using using using using
Str. 7
System; System.Drawing.Printing; System.Linq; System.Windows.Forms; System.Collections.Generic; Pavluzd.Depict;
using System.Drawing; //zkrátíme using MVP = Microsoft.VisualBasic.PowerPacks; using SWF = System.Windows.Forms;
Vložíme funkci pro načtení delegáta: //Prvky folmuláře , které mají být vytištěny private void AddDelegateToPrintControl(string strType, ControlPrinting printFunction) { delegateforControls.Typ = strType; delegateforControls.PrintFunction = printFunction; delegatesforControls.Add(delegateforControls); } public Class1(System.Windows.Forms.Control ctrl){} //původní konstruktor //Doplníme konstruktor novým kódem : public Class1(System.Windows.Forms.Control ctrl) { this.control = ctrl; //tato kolekce slouží k vyloučení prvků z tisku deletePrintControl = new List<string>(); delegatesforControls = new List(); //naplní kolekci prvky, které budu v ukázce tisknout AddDelegateToPrintControl("MVP.ShapeContainer", PrintShape); AddDelegateToPrintControl("SWF.TextBox", PrintTextBox); //další prvky, které chci tisknout nutno doplnit programem na příklad: //AddDelegateToPrintControl("SWF.LinkLabel", PrintLinkLabel); //AddDelegateToPrintControl("SWF.ComboBox", PrintComboBox); //AddDelegateToPrintControl("SWF.CheckBox", PrintCheckBox); //AddDelegateToPrintControl("SWF.RadioButton", PrintRadioButton); //AddDelegateToPrintControl("SWF.DateTimePicker", PrintDateTimePicker); //AddDelegateToPrintControl("SWF.NumericUpDown", PrintNumericUpDown); //další možné ovládací prvky k tisku } Pokud se pokusíme provést příkaz VS -> Sestavení -> Sestavit řešení, získáme následující výstup: Sestavení: 1 úspěšně, 1 se nezdařilo, 1 aktuální, 0 přeskočeno Seznam chyb: Název PrintTextBox v aktuálním kontextu neexistuje. Název PrintShape v aktuálním kontextu neexistuje. Toto chybové hlášení je způsobeno voláním funkcí, které nejsou zadány. Prozatím vytvoříme hlavičky požadovaných funkci takto: //Funkce, které budou provádět tisk: #region "Tiskne TextBox" private void PrintTextBox(Control ctrl,ParentControlPrinting typePrint,int locationX, int locationY, out bool ScanForChildControls) { ScanForChildControls = false; }//PrintTextBox #endregion
Str. 8
#region "Tiskne LineShape, OvalShape a RectangleShape" private void PrintShape(Control ctrl,ParentControlPrinting typePrint,int locationX, int locationY, out bool ScanForChildControls) { ScanForChildControls = false; }//print Shape #endregion Pokud se pokusíme znovu provést příkaz VS -> Sestavení -> Sestavit řešení, bude výstup již následující: Sestavení: 2 úspěšně, 0 se nezdařilo, 1 aktuální, 0 přeskočeno
Úprava funkce MyPrint() Vylepšit tuto funkci můžeme pomocí PrintPreviewDialog, která je součástí VS. private bool landscape = true ; private bool printPreview = true; private Rectangle RealPageSetting = new Rectangle(100, 100, 960, 600); //Velikost k tisku private Rectangle PageSettingA4 = new Rectangle(0,0, 1169, 827); //Velikost A4 v pixelech //Spuštění tisku. Výpočet výchozí polohy a orientace //Definuje objekt odešle výstup do tiskárny při tisku z aplikace model //Windows Forms public void MyPrint() { using (PrintDocument pd = new PrintDocument()) { pd.DefaultPageSettings.Landscape = landscape; //Událost volána v případě potřeby výstup vytisknout aktuální stránku pd.PrintPage += PdPrintPage; //text v hlavičce formuláře pd.DocumentName = control.Text; if (printPreview) { //Představuje dialogové okno pole formuláře, který obsahuje //PrintPreviewControl k tisku z aplikace model Windows Forms. using (var pp = new PrintPreviewDialog()) { if (landscape) pp.ClientSize = new Size(580, 410); else pp.ClientSize = new Size(410, 580); pp.Document = pd; pp.WindowState = FormWindowState.Normal; pp.ShowDialog(); } } else { pd.Print(); //Spustí proces tisku dokumentu. } } }//MyPrint Událost PdPrintPage nám zahájí tisk formuláře a ovládacích prvků.
Str. 9
Nejprve musíme připravit k tisku formulář Form1 z naší (exe) aplikace a to bez ovládacích prvků. Provedeme přidání referencí jako v minulém dílu tentokrát ze třídy Class1 na Class2. Průzkumník řešení -> projekt MyClass1 -> pravé tlačítko myši -> přidat odkaz -> solution -> MyClass2. Dále Průzkumník řešení -> projekt MyClass1 -> pravé tlačítko myši -> přidat odkaz -> Framework -> Micr.Visual.Basic.
V generované události PdPrintPage nebudeme spouštět funkce odkazující na FormPrintDemo.Class2. Tyto funkce zpřístupníme později, jsou využívány i ovládacími prvky. Jedná se o funkce: FormPrintDemo.Class2.FnSetGraphics(graphics); // Reálná velikost stránky k tisku FormPrintDemo.Class2.FnRealPageSettingsBounds(RealPageSetting); //Image pozadí formuláře FormPrintDemo.Class2.FnDrawBachgRoundImage(control.BackgroundImage, control.BackgroundImageLayout, RealPageSetting); Výsledná funkce bude vypadat: private void PdPrintPage(object sender, PrintPageEventArgs ev) { graphics = ev.Graphics; //FormPrintDemo.Class2.FnSetGraphics(graphics); //reálná velikost stránky k tisku //FormPrintDemo.Class2.FnRealPageSettingsBounds(RealPageSetting); graphics.Clip = new Region(PageSettingA4); //barva formuláře Form using (SolidBrush brush = new SolidBrush(control.BackColor)) graphics.FillRectangle(brush, RealPageSetting); //Image pozadí formuláře //FormPrintDemo.Class2.FnDrawBachgRoundImage(control.BackgroundImage, //control.BackgroundImageLayout, RealPageSetting); //vykreslí platné ohraničení stránky pro návrh if (Ohraniceni) { using (Pen pen = new Pen(Color.Red)) graphics.DrawRectangle(pen, RealPageSetting); Rectangle rec = Rectangle.Inflate(PageSettingA4, -2, -2); using (Pen pena = new Pen(Color.Black)) graphics.DrawRectangle(pena, rec); } graphics.ResetClip(); //tisk nadpisu formuláře , velikost písma , podtržení písma if (!String.IsNullOrEmpty(control.Text)) { using (StringFormat sf = new StringFormat()) { sf.LineAlignment = StringAlignment.Near; sf.Alignment = StringAlignment.Center; using (SolidBrush brush = new SolidBrush(Color.Black)) graphics.DrawString(control.Text, control.Font, brush, RealPageSetting, sf); } } PrintControls(control, RealPageSetting.X, RealPageSetting.Y );
Str. 10
//Nebudou vytištěny další stránky ev.HasMorePages = false; }//PdPrintPage Volaná funkce PrintControls(control, RealPageSetting.X, RealPageSetting.Y); bude připravovat k tisku ovládací prvky a bude volaná rekurzí. Vytvoříme hlavičku této funkce a provedeme sestavení našich projektů. private void PrintControls(SWF.Control ctrl, int x0, int y0){} V okně Výstup: Sestavení bylo úspěšně dokončeno. Sestavení: 2 úspěšně, 0 se nezdařilo, 1 aktuální, 0 přeskočeno.
Malé odbočení: Každý ovládací prvek je třída. Na formulář můžeme ovládací prvek přetáhnout pomocí VS -> panel nástrojů nebo programově. //programově using System.Collections.Generic; public partial class Form1 : Form { private static TextBox tb = new TextBox(); //vytvoří text box //nebo pole TextBoxu private List tbp = new List(3){tb,tb,tb}; } Rozdíl je ve vytváření prvků z doplňků balíčku Visual Basic. using MVB = Microsoft.VisualBasic.PowerPacks; public partial class Form1 : Form { private MVB.ShapeContainer mShape private MVB.LineShape line1 = new private MVB.LineShape line2 = new private MVB.OvalShape oval1 = new //konstruktor shapeContainer.Parent = this; line1.Parent = shapeContainer; line2.Parent = shapeContainer; oval1.Parent = shapeContainer; }
= new MVB.ShapeContainer(); MVB.LineShape(); MVB.LineShape(); MVB.OvalShape();
Z těchto důvodů je dekódování tisku rozdílné.
Trochu teorie a použitých názvů: Pokud se podíváme na hotovou konstrukci třídy Class1, jedná se o návrhový vzor továrny na objekty. Co jsou návrhové vzory? Návrhový vzor je způsob postupu, dle kterého upravíme náš postup při tvorbě programu.
Str. 11
Výhody tovární metody: Skrývá složitost tvorby od spotřebitele. Zajišťuje, že logika vytvoření objektu bude na jednom místě. Pomáhá přizpůsobit tvorbu, aniž byste rušili logiku spotřebitele. Přináší logické oddělení tvorby a použití.
Rekurze: Rekurzi používáme, když nějaká metoda volá sama sebe. Uvedu jednoduchý příklad výpočtu faktoriálu. private long Faktorial(int x) { if(x==0) return 1; else return x*Faktorial(x-1); }
Názvy:
Kontejner je v našem případě kolekce, do které budeme vkládat ovládací prvky. Rodič je náš formulář, dítě ovládací prvek nakreslený na našem formuláři. Pokud ovládací prvek je kontejnerem pro další ovládací prvky, pak formulář je prarodič, na něm nakreslený ovládací prvek je rodič a nejvýše položený nakreslený prvek je dítě.
Připravíme si naši knihovnu C++/CLR:safe pro pokračování. V tabulce je ukázka vlivu přepínače C++/CLR na možnosti programátora. Zde nebudu vysvětlovat jednotlivé možnosti, jsou dostatečně popsané na webu.
Příprava (dll) knihovny Class2: Ve VS otevřeme průzkumník řešení a myší najedeme na MyClass2. Pravé tlačítko myši otevřeme vlastnosti. Tlačítkem Add.New Reference... přidáme Microsoft.VisualBasic.Power.Packs. Stejně přidáme i System.Windows.Forms a přidáme System.Drawing, viz obrázek.
Str. 12
Znovu přejdeme na Stránky vlastností MyClass2 a přejdeme na Vlastnosti Konfigurace a v poli Common Language Runtime Support naklikneme: Safe MSIL Common Language Runtime Support (/clr:safe). Stránky vlastností MyClass2 musí být nastaveny v Konfiguraci na Všechny konfigurace. Funkce FnSetGraphics(Graphics^ gr) a FnRealPageSettingsBounds(Rectangle RealPageSetting) jsou statické. To znamená, že je pouze jedna instance této funkce bez ohledu kolikrát je třída použita. V průzkumníku řešení vidíme, že máme Header Files - MyClass2.h a Source Files MyClass2.cpp. Zde doporučuji psát kód programu jak do hlavičkového souboru i do source souboru, není to sice nutné, při větším projektu ztratíte přehled a váš program bude špatně čitelný. Do Header souboru zapisujeme pouze hlavičky funkcí.
Str. 13
Nastavení kompilátoru .
Programujeme v knihovně MyClass2: Přejdeme do knihovny MyClass1 na funkci PdPrintPage(....), zrušíme komentář u FnSetGraphics(graphics), zkopírujeme tuto funkci do schránky, otevřeme MyClass2 a vložíme do Class2. Upravíme hlavičkový soubor: #pragma once
//hlavička může být načtena pouze 1x velmi důležité
namespace FormPrintDemo { public ref class Class2 //v C# -> public class Class2 { public: Class2(); //konstruktor void static FnSetGraphics(Graphics^ gr); //hlavička funkce private: Graphics^ graphics; }; }//namespace Upravíme Source soubor MyClass2.cpp #include "stdafx.h" #include "MyClass2.h" namespace FormPrintDemo { Class2::Class2(){}; //konstruktor
Str. 14
//volaná funkce void Class2::FnSetGraphics(Graphics^ gr) { graphics = gr; //tělo funkce }; } Doplníme reference na knihovny v Stdafx.h, budou se kompilovat pouze 1x. #pragma once #using <System.dll> #using <System.Windows.Forms.dll> using namespace System; using namespace System::Drawing; using namespace System::Windows::Forms; using namespace Microsoft::VisualBasic::PowerPacks; using namespace System::Drawing::Drawing2D; //pro zjednodušení zápisu funkcí namespace sd = System::Drawing ; namespace swf = System::Windows::Forms; namespace sri = System::Runtime::InteropServices; Stejný postup bude u funkce FnRealPageSettingsBounds(RealPageSetting); a také u funkce: FnDrawBachgRoundImage(control.BackgroundImage,control.BackgroundImageLayout,RealPageSetting);. Vytvoříme další hlavičkový a source soubor MyClassImage.h a MyClassImage.cpp. Hlavičkový soubor MyClass2.h bude vypadat takto slouží pro tisk Image: #pragma once #include "MyClassImage.h" namespace FormPrintDemo { public ref class Class2 : MyClassImage //Class2 dědí z MyClassImage { public: Class2(); void static FnSetGraphics(Graphics^ gr); void static FnRealPageSettingsBounds(Rectangle RealPageSetting); void static FnDrawBachgRoundImage(Image ^image, swf::ImageLayout imagelayout, Rectangle rec ); private: static Graphics^ graphics; static Rectangle realPageSetting; }; } Source soubor MyClass2.cpp: #include "stdafx.h" #include "MyClass2.h" namespace FormPrintDemo { Class2::Class2(){}; //konstruktor
Str. 15
void {
Class2::FnSetGraphics(Graphics^ gr) graphics = gr;
}; void Class2::FnRealPageSettingsBounds(Rectangle RealPageSetting) { realPageSetting = RealPageSetting; }; void Class2::FnDrawBachgRoundImage(Image ^image, swf::ImageLayout imagelayout, Rectangle rec ) { if(image == nullptr){return;} //v C# -> null DrawBachgRoundImageA(graphics, image, rec, imagelayout ); }; }//namespace
Obrázek na pozadí pro ovládací prvky: Upravíme soubor MyClassImage.h #pragma once namespace FormPrintDemo { public ref class MyClassImage { protected : void DrawBachgRoundImageA(Graphics^ gr, Image^ image, Rectangle rec, swf::ImageLayout layout ); private: Brush^ DrawBachgRoundImage(Graphics^ gr, Image^ image, Rectangle rec, swf::ImageLayout layout); Image^ backgroundImage; ImageLayout imageLayout; Image^ image; }; } Upravíme soubor MyClassImage.cpp #include "stdafx.h" #include "MyClassImage.h" namespace FormPrintDemo { void MyClassImage::DrawBachgRoundImageA(Graphics^ gr, Image^ image, Rectangle rec, swf::ImageLayout layout) { Brush ^imageBrush = DrawBachgRoundImage(gr, image, rec, layout); if (imageBrush != nullptr) { gr->FillRectangle(imageBrush, rec); delete imageBrush; } }//DrawBachgRoundImageA Brush^ MyClassImage::DrawBachgRoundImage(Graphics^ graphics, Image^ image, Rectangle rec, swf::ImageLayout layout) { this->backgroundImage = (Image^) image;
Str. 16
TextureBrush ^textureBrush = safe_cast(nullptr); if (this->backgroundImage != nullptr) { double num1 = safe_cast<double>(rec.X); double num2 = safe_cast<double>(rec.Y); int num3 = 0; int num4 = 0; Image ^image = this->backgroundImage; WrapMode wrapMode = WrapMode::Clamp; Bitmap ^bitmap = safe_cast(nullptr); this->imageLayout = layout; switch (this->imageLayout) { case ImageLayout::Tile: wrapMode = WrapMode::Tile; break; case ImageLayout::Center: num3 = rec.Width - this->backgroundImage ->Width; if (num3 > 0) num1 = safe_cast<double>(rec.X) + safe_cast<double>(num3) / 2.0; num4 = rec.Height - this->backgroundImage ->Height; if (num4 > 0) { num2 = safe_cast<double>(rec.Y) + safe_cast<double>(num4) / 2.0; break; } else break; case ImageLayout::Stretch: bitmap = gcnew Bitmap(rec.Width, rec.Height); graphics = Graphics::FromImage(safe_cast(bitmap)); graphics->DrawImage(image, 0, 0, bitmap ->Width, bitmap->Height); delete graphics; break; case ImageLayout::Zoom: double num5 = (double)(rec.Width) / safe_cast<double>(image->Width); double num6 = (double)(rec.Height) / (double)(image->Height); if (num5 > num6) { num1 = (double)rec.X + (num5 - num6) * (double)(image->Width) / 2.0; bitmap = gcnew Bitmap(int) (Math::Round(double)(image->Width) * num6)), rec.Height); } else {
Str. 17
num2 = safe_cast<double>(rec.Y) + (num6 - num5) * safe_cast<double> (image->Height) / 2.0; bitmap = gcnew Bitmap(rec.Width,safe_cast(image->Height) * num5))); } graphics = Graphics::FromImage(safe_cast(bitmap)); graphics->DrawImage(image, 0, 0, bitmap>Width, bitmap->Height); delete graphics; break; }//switch if (bitmap != nullptr) { textureBrush = gcnew TextureBrush(safe_cast(bitmap), wrapMode); delete bitmap; } else textureBrush = gcnew TextureBrush(image, wrapMode); Matrix ^transform = textureBrush->Transform; transform->Translate(safe_cast(num1), safe_cast(num2)); textureBrush->Transform = transform; } return safe_cast(textureBrush); } } Poslední úprava před testem bude v Class1 v PdPrintPage(......) tato : //Image pozadí formuláře (volaná funkce není statická) FormPrintDemo.Class2 class2 = new Class2(); class2.FnDrawBachgRoundImage(control.BackgroundImage, control.BackgroundImageLayout, RealPageSetting); Provedeme sestavení, pokud jsme programovali bezchybně: Sestavení bylo úspěšně dokončeno. Nové sestavení všeho: 3 úspěšně, 0 se nezdařilo, 0 přeskočeno
Test formuláře Formulář nyní otestujeme: Můžeme měnit barvu pozadí formuláře Můžeme zadat Image pozadí (BackgroundImage) Můžeme měnit rozložení obrázku pozadí (BackgroundImageLayout -> None, Tile, Center, Zoom, Stretch) Můžeme zapnou nebo vypnout zobrazení ohraničení na stránce Není možné vysvětlovat činnost kódu FnDrawBachgRoundImage(.....), rozbor kódu by zabral samostatný výklad.
Str. 18
V předchozí části jsme vytvořili hlavičku funkce: private void PrintControls(SWF.Control ctrl, int x0, int y0){} Tuto funkci doplníme kódem, pro výběr prvků pro tisk na našem formuláři: using System.Diagnostics; //vytvoří setříděnou kolekci prvků a podle jejich skutečné polohy na formuláři private void PrintControls(SWF.Control ctrl, int x0, int y0) { int nbCtrl = ctrl.Controls.Count; int[] yPos = new int[nbCtrl]; System.Windows.Forms.Control[] controls = new SWF.Control[nbCtrl]; //načte prvky do kolekce for (int i = 0; i < nbCtrl; i++) { controls[i] = ctrl.Controls[i]; yPos[i] = ctrl.Controls[i].Location.Y; //uloží pozici Y } //setřídí prvky podle souřadnice Y System.Array.Sort(yPos, controls); //Debug.Print("\r\n"); //Pro testy for (int i = 0; i < nbCtrl; i++) { // skutečná poloha prvků jsou započítány okraje tištěného formuláře PrintControl(controls[i], x0 + controls[i].Location.X, y0 + controls[i].Location.Y); //Debug.Print(i.ToString() + " " + controls[i].Name.ToString()); } } //PrintControls
Str. 19
Pro testy doplníme záhlaví naší třídy System.Diagnostics a naši funkci Debug.Print(.....) a budeme sledovat výstup naší funkce. Tato funkce je volána pomocí rekurze, počet průchodů je dán rozložením a počtem uživatelských prvků. Pro názornost ukázky je použita plná verze programu. Nejlépe vystihuje funkci tato ukázka. Stojí za povšimnutí pouze jeden průchod funkcí pro shapeContainer.
// funkce rozdělí prvky na rodiče a jejich děti // v případě dětí je provedena rekurze private void PrintControl(Control ctrl, int locationX, int locationY) { if (ctrl.Visible) { bool scanForChildControls = false; // Vytisknout ovládací prvky na formuláři PrintOneControl(ctrl, ParentControlPrinting.BeforeChilds, locationX, locationY, out scanForChildControls); // Vytisknout obsažené ovládací prvky (děti) if (scanForChildControls) { //Zde je volaná rekurze pro děti PrintControls(ctrl, locationX, locationY); PrintOneControl(ctrl, ParentControlPrinting.AfterChilds, locationX, locationY,out scanForChildControls); } } }//PrintControl
Str. 20
Další naše funkce vyloučí uživatelské prvky, které nechceme tisknout a generuje funkce k tisku. private void PrintOneControl(Control ctrl, ParentControlPrinting typePrint, int locationX, int locationY, out boolscanForChildControls) { scanForChildControls = true; string s = ctrl.GetType().ToString(); bool founded = false; //vylučuji vybrané uživatelské prvky z tisku if (deletePrintControl.Any(sType => s.IndexOf("." + sType, StringComparison.Ordinal) >= 0)) { founded = true; scanForChildControls = false; } //Musíme začít -> prvý do kolekce -> prvý k tisku if (!founded) for (int i = delegatesforControls.Count - 1; i >= 0; i--) // od konce na začátek { DelegateforControls d = (DelegateforControls)delegatesforControls[i]; if (s.EndsWith(d.Typ)) { d.PrintFunction(ctrl, typePrint, locationX, locationY, out scanForChildControls); //Debug.Print(d.Typ); break; } } } Tabulka ukazuje jak se generujeme funkce pro tisk. Pokud se dobře díváte, některé funkce jsou tu dvakrát a na našem formuláři je tento prvek pouze jednou. Pokud má uživatelský prvek vlastnost na sebe vázat jiné uživatelské prvky (například GroupBox, UserControl, TabControl, PictureBox), další druhý průchod pro tisk je blokován pomocí testu (enum ParentControlPrinting) v kódu pro daný uživatelský prvek. Příklad: if (typePrint == ParentControlPrinting.BeforeChilds) { MyClass2.PrintPictureBox(pb, x1, y1, width, height); } Poslední funkce nám musí ořezat přesahující části ovládacího prvku (dítěte) uloženého na rodiči, například GroupBox, Panel a podobně, na kterých je uložen TextBox, který přesahuje přes rodiče.
Str. 21
//ořeže velikost těchto dětských prvků o část, které přesahují rodiče private void TrimChild(Control ctrl, ref int x1, ref int y1 , out int width ,out int height ) { width = ctrl.Width; height = ctrl.Height; if (ctrl.Parent == this.control) { RecRodic = new Rectangle(x1, y1, width, height); return; } else { Rectangle rec = new Rectangle(x1, y1, ctrl.Width, ctrl.Height); Rectangle rr = Rectangle.Intersect(rec, RecRodic); x1 = rr.Left; y1 = rr.Top; width = rr.Width; height = rr.Height; } } Poslední funkce naší třídy Class1 již volají v konstruktoru definované ovládací prvky Class1. Hlavičky těchto funkcí jsme již vytvořili při definování konstruktoru. Nyní je doplníme kódem. Tento je stejný pro ovládací prvky, které nemohou být kontejnerem pro další prvky. Ovládací prvky, které jsou kontejnerem pro další prvky, mají v kódu přidány 2 řádky, důvody jsem uvedl při výkladu poslední tabulky. #region "Tiskne TextBox" private void PrintTextBox(Control ctrl,ParentControlPrinting typePrint, int x1, int y1,out bool ScanForChildControls) { //prvek nemůže mít kontejner to znamená nelze na něho uložit další //ovládací prvek ScanForChildControls = false; int width = 0; int height = 0; TrimChild(ctrl, ref x1, ref y1, out width, out height); TextBox tb = (TextBox)ctrl; //Voláme funkci tisku pro TextBox MyClass2.FnPrintTextBox(tb, x1, y1, width, height); }//PrintTextBox #endregion #region "Tiskne LineShape OvalShape RectangleShape" private void PrintShape(Control ctrl,ParentControlPrinting typePrint,int x1, int y1, out bool ScanForChildControls) { ScanForChildControls = false; int width = 0; int height = 0; TrimChild(ctrl, ref x1, ref y1, out width, out height); Kreslivbshape pPack = new Kreslivbshape(); pPack.PrintShape(graphics, ctrl, x1, y1 , RealPageSetting); } #endregion #region "Tiskne UserControl" //jediná funkce public volá se externě z FormPrintDemo (není dodělána. //fp.AddDelegateToPrintControlEx("PZ.Tisky.UserControl1", //fp.PrintUserControl);
Str. 22
public void PrintUserControl(Control ctrl,ParentControlPrinting typePrint,int x1, int y1,out bool scanForChildControls) { scanForChildControls = true; //má kontejner, může vázat další prvky int width = 0; int height = 0; TrimChild(ctrl, ref locationX, ref locationY, out width, out height); UserControl usc = (UserControl)ctrl; if (typePrint == ParentControlPrinting.BeforeChilds) { classTisk.FnPrintUserControl(usc, locationX, locationY, width, height); } } Do Class1 nebudeme již psát žádný další kód. Musíme napsat hlavičky do dll třídy MyClass2 a zkusíme provést kompilaci.
Dopsání kódu do MyClass2. //header soubor MyClass2.h a dopíšeme funkci public : void FnPrintTextBox(swf::TextBox^ tb,int x1, int y1 , int w1 , int h1); //vytvoříme novou třídu v header soubor MyClass2.h a dopíšeme funkci public ref class Kreslivbshape{ public: void PrintShape(Graphics^ gr, Control^ control,int x,int y, Rectangle rec); private: Graphics^ graphics; int count; int x0; int y0; };//Kresli prvky //source soubor MyClass2.cpp //tisk TextBox void Class2::FnPrintTextBox(swf::TextBox^ tb,int x1, int y1 , int w1 , int h1) { }; //tisk LineShape OvalShape RectangleShape void Kreslivbshape::PrintShape(Graphics^ gr, Control ^control, int x, int y , Rectangle rec ) { }; Sestavíme řešení (provedeme kompilaci) v okně výstupu se zobrazí: Sestavení bylo úspěšně dokončeno. Nové sestavení všeho: 3 úspěšně, 0 se nezdařilo, 0 přeskočeno Doplníme knihovnu MyClass2 kódem pro tisk TexBoxu a LineShape. Hlavičkový soubor MyClass2.h bude obsahovat dvě třídy: //MyClass2.h #pragma once #include "MyClassImage.h" #include "MyTexty.h" // //Třída pro tisk TexBoxu a BachgRoundImage formuláře
Str. 23
//Class2 dědí MyClassImage public ref class Class2 : MyClassImage { public: Class2(); //konstruktor void static FnSetGraphics(Graphics^ gr); void static FnRealPageSettingsBounds(Rectangle RealPageSetting); void FnDrawBachgRoundImage(Image ^image, swf::ImageLayout imagelayout, Rectangle rec ); void FnPrintTextBox(swf::TextBox^ tb,int x1, int y1, int w1, int h1); private: static Graphics^ graphics; static Rectangle realPageSetting; Rectangle recpom ; bool FnuOrez(Graphics^ gr, int x1, int y1, int w1, int h1, Rectangle recmax ,Rectangle %recpom); Rectangle FnFillRectangle(Color backColor, Rectangle rec ); }; Další třída je pro tisk Shape ovládacích prvků. //MyClass2.h //Třída pro tisk LineShape , OvalShape a RectangleShape //v tvém kódu bude tato třída samostatná pro lepší přehled public ref class Kreslivbshape{ public: void PrintShape(Graphics^ gr,swf::Control ^control,int x,int y, Rectangle rec); private: void NactiPrvkyDoKolekce(ShapeContainer ^shapeContainer); static List<mvb::LineShape^> ^lineShape = gcnew List<mvb::LineShape^>(10); static List<mvb::OvalShape^> ^ovalShape = gcnew List<mvb::OvalShape^>(10); static List<mvb::RectangleShape^> ^rectangleShape = gcnew List<mvb::RectangleShape^>(10); void KresliLine(Graphics^ gr,int xx , int yy , int count); void KresliOval(Graphics^ gr,int xx , int yy, int count); void KresliRectangle(Graphics^ gr, int xx , int yy ,int count); int count; int x0; int y0; Rectangle recMax ; }; Source soubor MyClass2.cpp nejprve doplníme funkcí, která má za úkol ořezat prvek mimo tisknutelnou plochu: //MyClass2.cpp //Rectangle %recpom -> v C# je jako ref Rectangle recpom bool Class2::FnuOrez(Graphics^ gr, int x1, int y1, int w1, int h1, Rectangle recmax, Rectangle %recpom) { recpom = Rectangle(x1,y1,w1,h1) ; //Vrací průsečík obou rectangle. Pokud neexistuje vrací emty recpom = Rectangle::Intersect(recpom, recmax);
Str. 24
//Ovládací prvek je mimo tisknutelnou oblast if (recpom.IsEmpty){return false;} //zvětší rectangle o 1 pixel Rectangle rectTrim = Rectangle::Inflate(recpom, 1, 1); //Provede oříznutí plochy pro kreslení gr->Clip = gcnew Region(rectTrim); return true ; }; Další funkce bude tisknout pozadí plochy tvého ovládacího prvku. //MyClass2.cpp Rectangle Class2::FnFillRectangle(Color backColor, Rectangle rec ) { SolidBrush^ brush = nullptr; try { //prosor zmenšen o ohraničení BorderStyle Rectangle rectBackColor = Rectangle::Inflate(rec, -1, -1); SolidBrush^ brush = gcnew SolidBrush((Color) backColor); graphics->FillRectangle(brush, rectBackColor); return rectBackColor; } finally{delete brush;} }; Doplníme kódem funkci, kterou jsme vytvořili dříve . //MyClass2.cpp void Class2::FnPrintTextBox(swf::TextBox^ tb,int x1,int y1,int w1,int h1) { MyTextControls^ myTextControls = gcnew MyTextControls; if(FnuOrez(graphics, x1,y1,w1,h1,realPageSetting,this->recpom )) { Rectangle recback = FnFillRectangle((Color)tb->BackColor, this ->recpom); //vyplní pozadí if(tb->Multiline) { myTextControls->FntDrawText3(graphics,tb->Lines, (Color) tb->ForeColor, recback, tb->Font,(HorizontalAlignment) tb->TextAlign); } else { graphics->Clip = gcnew Region(this->recpom); myTextControls->FntDrawText2(graphics, tb->Text, (Color) tb->ForeColor, recback, tb->Font, (HorizontalAlignment) tb->TextAlign); graphics->ResetClip(); } //v této ukázce neřešen BorderStyle nastaven FixedSingle Pen^ penb = gcnew Pen(Color::Black,1 ); graphics->DrawRectangle(penb,this->recpom); } return; }; Pomocí návrháře vytvoříme nový soubor MyTexty.h a MyTexty.cpp .
Str. 25
//MyTexty.h #pragma once namespace FormPrintDemo { public ref class MyTextControls sealed { public: MyTextControls(); internal: void FntDrawText3(Graphics^ graphics,array<String^>^ text, Color foreColor, Rectangle rect, Font^ printFont,HorizontalAlignment ha); void FntDrawText2(Graphics^ graphics,String^ text, Color foreColor, Rectangle rect, Font^ printFont, HorizontalAlignment ha); private: StringFormat^ };//MyTextControls }//namespace
HaAlignment(swf::HorizontalAlignment ha);
Pro formátování textu je použita funkce HaAlignment(......) //MyTexty.cpp #pragma once #include "stdafx.h" #include "MyTexty.h" namespace FormPrintDemo { MyTextControls::MyTextControls(){}; //konstruktor StringFormat^ MyTextControls::HaAlignment(swf::HorizontalAlignment ha) { StringFormat^ sf = gcnew StringFormat( StringFormatFlags::NoClip ); switch (ha) { case HorizontalAlignment::Left: sf->Alignment = StringAlignment::Near; break; case HorizontalAlignment::Center: sf->Alignment = StringAlignment::Center; break; case HorizontalAlignment::Right: sf->Alignment = StringAlignment::Far; break; default: sf->Alignment = StringAlignment::Near; break; } return sf; };//HaAlignment } //namespace Další funkce řeší jednořádkový text (je zjednodušená)
Str. 26
//MyTexty.cpp void MyTextControls::FntDrawText2(Graphics^ graphics,String^ value, Color foreColor, Rectangle rect,Font^ printFont, HorizontalAlignment ha) { if(String::IsNullOrEmpty(value)){return;} float fHeigth = printFont->GetHeight(graphics); rect.Y = (int) (rect.Y + (rect.Height - fHeigth) / 2.0F); SolidBrush^ brush = gcnew SolidBrush(foreColor); StringFormat^ sf = HaAlignment(ha); graphics->DrawString(value, printFont, brush, rect, sf); if(brush != nullptr) {delete brush;} if(sf != nullptr) {delete sf;} }; Poslední funkce řeší multiline text (zjednodušená ) //MyTexty.cpp void MyTextControls::FntDrawText3(Graphics^ graphics,array<String^>^ text, Color foreColor,Rectangle rect, Font^ printFont,HorizontalAlignment ha) { int count = text->Length; if(count <= 0){return;} float fHeigth = printFont->GetHeight(graphics); rect.Y = (int) (rect.Y + fHeigth / 3.0F); StringFormat^ sf = HaAlignment(ha); SolidBrush^ brush = gcnew SolidBrush(foreColor); for ( int i = 0; i < count; i++ ) { graphics->DrawString(text[i], printFont, brush, rect, sf); rect.Y += (int) fHeigth ; } };
Dokončení tisku LineShape: Postupujeme opačně při dekódování uživatelských prvků Shape než v uvedené v ukázce při jejich vytváření. V demo ukáži tisk prvku LineShape a dekódování OvalShape a RectangleShape. Nejprve doplníme kód v funkci PrintShape(......) , kterou jsme vytvořili v minulé lekci. //MyClass2.cpp void Kreslivbshape::PrintShape(Graphics^ gr, swf::Control ^control, int x, int y , Rectangle rec ) { this->x0 = x; this->y0 = y; this->recMax = rec; //vytvořím kolekci a v této budou umístěny všechny Shape //uživatelské prvky mvb::ShapeContainer ^shapeContainer = safe_cast<mvb::ShapeContainer^>(control); //vysoká kvalita tisku gr->SmoothingMode = SmoothingMode::HighQuality; //načte prvky do kolekcí podle typu prvků NactiPrvkyDoKolekce(shapeContainer); this->count = lineShape->Count; if (this->count > 0) //kresli line KresliLine(gr, this->x0 , this->y0 , this->count); this->count = ovalShape->Count; if (this->count > 0) //kresli elipsu ,kruh
Str. 27
KresliOval(gr, this->x0 , this->y0, this->count); this->count = rectangleShape->Count; if (this->count > 0) //kreslí recangle KresliRectangle(gr, this->x0 , this->y0, this->count ); gr->SmoothingMode = SmoothingMode::Default; }; Další funkce provede třídění z kolekce ShapeContainer do kolekcí LineShape, RectangleShape a OvalShape. //MyClass2.cpp void Kreslivbshape::NactiPrvkyDoKolekce(ShapeContainer ^shapeContainer) { this->count = shapeContainer->Shapes->Count; //počet prvlů Shape v //kontejneru Type ^type = nullptr; String^ stringType = nullptr; Object^ shape = nullptr; //rozdělí Shape uživatelské prvky for (int i = 0; i < this->count; i++) { shape = shapeContainer->Shapes[i]; type = shape->GetType(); stringType = type->FullName; if (stringType->Equals("MVP.LineShape")) lineShape->Add(safe_cast<mvb::LineShape^>(shape)); else if (stringType->Equals("MVP.OvalShape")) ovalShape->Add(safe_cast<mvb::OvalShape^>(shape)); else if (stringType->Equals("MVP.RectangleShape")) rectangleShape>Add(safe_cast<mvb::RectangleShape^>(shape)); } //zmenší kolekci na daný počet prvků lineShape->TrimExcess(); ovalShape->TrimExcess(); rectangleShape->TrimExcess(); }; Dokončení: //MyClass2.cpp Tisk LineShape void Kreslivbshape::KresliLine(Graphics^ gr, int xx , int yy , int count) { int x1,x2,y1,y2 = 0; LineShape ^mlineShape = nullptr; for (int i = 0; i < count; i++) { mlineShape = lineShape[i]; x1 = mlineShape->X1 + xx; x2 = mlineShape->X2 + xx; y1 = mlineShape->Y1 + yy; y2 = mlineShape->Y2 + yy; gr->Clip = gcnew Region(recMax); Pen^ pen = gcnew Pen(mlineShape->BorderColor,(float) mlineShape->BorderWidth); pen->DashStyle = mlineShape->BorderStyle; gr->DrawLine(pen, x1, y1, x2, y2); delete pen; gr->ResetClip(); } lineShape->Clear();
Str. 28
mlineShape = nullptr; };//Kresli lineShape Pro pořádek musíme napsat prázdné funkce pro OvalShape a RectangleShape. Kód v těchto funkcích není předmětem této ukázky. //MyClass2.cpp void Kreslivbshape::KresliOval(Graphics^ graphics,int xx , int yy, int count) { //není v této ukázce //.......nějaký kód }; void Kreslivbshape::KresliRectangle(Graphics^ graphics, int xx , int yy ,int count) { //není v této ukázce //......nějaký kód };
Testování našeho výtvoru Nakonec nám zbývá otestování našeho programu. Přejdeme ve VS do správce konfigurace a nastavíme aktivní konfiguraci na Release u FormPrintDemo.exe, MyClass1.dll a MyClass2.dll. Po sestavení se v okně výstupu objeví: Sestavení bylo úspěšně dokončeno. Nové sestavení všeho: 3 úspěšně, 0 se nezdařilo, 0 přeskočeno Doplníme AssemblyInfo.css o název a ostatní assembly, totéž uděláme pro MyClass1 assemblyInfo.cs. Do třídy MyClass2.dll přidáme Assembly Resource File(.resx) s názevem MyClass2. V průzkumníku řešení v sekci Source Files (MyClass2) je nový soubor AssemblyInfo.cpp, který doplníme jako předešlé dva soubory assemblyInfo.cs. Znovu provedeme sestavení a otestujeme, zda jsme neudělali nějakou chybu.
Microsoft FxCop 10.0 Tento nástroj nám provádí statickou analýzu kódu. Poskytuje stovky pravidel, které provádějí různé typy analýz. Ve vyšších verzích VS je přímo součástí VS. Můžeme si tento nástroj nainstalovat z Microsoft windows SDK Instalaci FxCopu provedeme podle pokynů k instalaci. Doporučuji tento nástroj používat. Pokud načteme naše soubory (FormPrintDemo.exe, MyClass1.dll a MyClass2.dll) do FxCopu (viz obrázek), nepřestaneme se divit.
Str. 29
Opravíme červeně zabarvené chybové hlášky. Tuto chybu opravíme pokud náš kód podepíšeme mimo program není podepsaný. Assemblies should have valid strong names,myclass1.dll Assemblies should have valid strong names,myclass2.dll Assemblies should have valid strong names,FormPrintDemo.exe Zde doporučuji se řídit selským rozumem pro žluté doporučení. Nakonec podepíšeme sestavení vygenerovaným silným jménem pomocí utility sn.exe. Po podepsání jednotlivých tříd budou uvedené 3 chybové položky zlikvidovány. Vytvoříme instalační soubory a máme hotovo.
Str. 30
Závěr V této ukázce jsem se pokusil ukázat jak tisknout pod .NET. Výsledný tisk může vypadat takto:
Několik funkcí navíc Pokud vytváříte Demo verzi, můžete vykreslovat na formulář nebo uživatelské prvky různý barevný text. #if(DEBUG == true) /// <summary> /// Při demoverzi píše text pro tisk na formulář /// internal class DemoVerze { private List color = new List(11) ; internal DemoVerze() { color.Add(Color.Azure); color.Add(Color.LightCyan); color.Add(Color.Violet); color.Add(Color.LightGray); color.Add(Color.LightGreen); color.Add(Color.LightPink); color.Add(Color.LightSalmon); color.Add(Color.LightYellow); color.Add(Color.LightSteelBlue);
Str. 31
color.Add(Color.LightGoldenrodYellow); } internal void DrawRectanglesForm(Control controls, Graphics g) { Point pp = new Point(0, 0); List point = new List(8) { pp, pp, pp, pp,pp,pp,pp,pp }; point[0] point[1] point[2] point[3] point[4] point[5] point[6] point[7]
= = = = = = = =
new new new new new new new new
Point(230, Point(600, Point(230, Point(600, Point(970, Point(970, Point(230, Point(600,
230); 230); 600); 600); 230); 600); 940); 940);
// Vytvoření per, štětců a písem pro kresbu using (Font captionFont = new Font("Arial", 26)) { // Nastavení matice tak, aby se následující // kresby objevily ve středu PictureBoxu g.Clip = new Region(RealPageSettingsBounds); for (int j = 0; j < 8; j++) { g.TranslateTransform(point[j].X, point[j].Y, MatrixOrder.Prepend); // Režim vyhlazování nastavíme na HighQuality, // aby se nakreslily čisté linie g.SmoothingMode = SmoothingMode.HighQuality; // Nakreslení 10 obdélníků s nápisem a // pokaždé otočíme matici o 36 stupňů for (int i = 0; i < 10; i++) { using (Brush blackBrush = new SolidBrush(color[i])) { g.RotateTransform(36, MatrixOrder.Prepend); g.DrawString(" Demo ", captionFont, blackBrush, 25, 25); } } g.ResetTransform(); } // Vrátíme transformaci do původního stavu g.ResetClip(); g.SmoothingMode = SmoothingMode.Default; } } #endif
Str. 32
Takto může vypadat ukázka tisku Demo:
Důležité Pokud používáte VS 2013, je třeba doinstalovat VB-PoverPack a dodělat reference na toto assemblies.
Str. 33