Lekce 4 - Tisk formuláře a ovládacích prvků Windows Forms
V minulém díle jsme dokončili programování formuláře a dnes ukáži jakým stylem vybrat ovládací prvky umístěné na formuláři. Ve druhém díle jsme vytvořili hlavičku funkce:
private void PrintControls(System.Windows.Forms.Control ctrl, int x0, int y0){}
Tuto funkci doplníme kódem:
using System.Diagnostics; //vytvoří setříděnou kolekci prvků a podle jejich skutečné polohy na formuláři private void PrintControls(System.Windows.Forms.Control ctrl, int x0, int y0) { int nbCtrl = ctrl.Controls.Count; int[] yPos = new int[nbCtrl]; System.Windows.Forms.Control[] controls = new System.Windows.Forms.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; } //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()); //pro testy } }
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) { PrintControls(ctrl, locationX, locationY); //Zde je volaná rekurze pro děti PrintOneControl(ctrl, ParentControlPrinting.AfterChilds, locationX, locationY, out scanForChildControls); } } }
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 bool scanForChildControls) { 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; } //Process other type of control, beginning at the end of the list (use last add for a type) //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 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.
//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í definované ovládací prvky v konstruktoru 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.
Funkce PrinTextBox
#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
Funkce PrintShape
#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
Ukázka funkce pro UserControl
#region "UserControl" //jediná funkce public volá se externě z FormPrintDemo (není dodělána). //fp.AddDelegateToPrintControlEx("PZ.Tisky.UserControl1", fp.PrintUserControl); /* 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 ==========
V Dalším díle pouze doplníme nové funkce ve třídě MyClass2 a jsme hotoví. Pokračování příště.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 96x (72.32 kB)
Aplikace je včetně zdrojových kódů v jazyce C#