C# - Egyedi property szerkesztő ablak készítése tetszőleges property-hez

forráskód letöltése
A Properties ablakban megfigyelhető, hogy némely property esetén, annak értékének szerkesztésekor megjelenik egy kis nyomógomb, melyre rákattintva egy ablak nyílik meg. Ebben az ablakban segítséget kapunk a property értékének megadásához. Más esetben a property-nél egy legördíthető lista jelenik meg és az ott felsorolt értékekből választhatjuk ki a számunkra megfelelőt.
Saját komponenseink, kontroljaink fejlesztése közben a property-k létrehozásakor elláthatjuk azokat saját property szerkesztő ablakkal, ahol tetszőleges módon valósíthatjuk meg az adatbevitelt.
A mellékelt példa két részből áll: a TestControl könyvtárban található projektben készítünk egy új kontrolt, melynek egyetlen új property-éhez egy property szerkesztő ablakot is létrehozunk. A TestApp könyvtárban lévő projektben a kontrol felhasználására készült példa.
Kezdjük tehát a kontrol elkészítésével. Ennek különösebb feladata nem lesz, hiszen most a cél a property szerkesztő ablak létrehozásának bemutatása és nem egy valós kontrol készítése.
A kontrolt a UserControl1.cs-ben találjuk. Létrehozása a hagyományos módon történik.
  public UserControl1()
  {
    InitializeComponent();
A konstruktorban megváltoztatjuk a kontrol stílusát. Ehhez a SetStyle függvényt kell meghívnunk. Első paraméterként ControlStyles felsorolt típus egyik elemét adhatjuk meg, mellyel előírjuk, hogy melyik tulajdonságot szeretnénk ki, illetve bekapcsolni. Ez utóbbit a második paraméterben megadott logikai érték szabályozza.
Ezek alapján az Opaque stílus bekapcsolásával azt érjük el, hogy a kontrol háttérterülete nem lesz törölve az adott háttérszínnel. Ennek az lesz a következménye, hogy a kontrol átlátszó lesz azokon a helyeken, ahová nem rajzolunk. Ennek akkor van haszna, ha pont ez a cél, vagy ha a kontrol teljes területét mi magunk rajzoljuk ki. Ez esetben felesleges lenne a törlés amúgy is.
    SetStyle(ControlStyles.Opaque, true);
A ResizeRedraw stílus bekapcsolásával azt érjük el, hogy a kontrol méretének megváltoztatásakor automatikusan meghívásra kerül az újrarajzolásért felelős függvény.
    SetStyle(ControlStyles.ResizeRedraw, true);
}
Létrehozunk egy int típusú property-t, melynek adatát az FNumber változóban tároljuk. E property-nek annyi lesz a szerepe, hogy a benne tárolt számot rajzoljuk ki a kontrol területére.
    private int FNumber = 0;
A leglényegesebb feladatot a property létrehozásakor kell elvégeznünk: itt adhatjuk meg egy attribútum formájában, hogy az adott property-hez milyen típusú property szerkesztő osztályt rendelünk hozzá. Az Editor attribútumban két paramétert használunk: az elsőben megadjuk a property szerkesztő osztály típusát, a másodikban pedig annak ősosztályát, az UITypeEditor-t.
A NumberEditorClass osztály lesz a saját osztályunk, melyet a NumberEditorClass.cs-ben hozunk létre, de előbb nézzük még a kontrol további részeit.
    [
    Editor(typeof(NumberEditorClass), typeof(UITypeEditor))    
    ]
    public int Number
    {
      get { return FNumber; }
      set
      {
        FNumber = value;
        Invalidate();
      }
    }
Vissza van még a kontrol rajzolatának elkészítése, melyhez létrehozzuk a kontrol Paint eseményét.
Első lépésben kifestjük a kontrol teljes hátterét egy színátmenetes ecsettel.
    private void UserControl1_Paint_1(object sender, System.Windows.Forms.PaintEventArgs e)
    {       
      e.Graphics.FillRectangle(new LinearGradientBrush(this.ClientRectangle, Color.White, Color.Aqua, 45), this.ClientRectangle);
Rajzolunk egy keretet a kontrol köré a DrawRectangle hívásával.
      e.Graphics.DrawRectangle(new Pen(Color.Blue, 1), 0, 0, Width-1, Height-1);
Végül kiírjuk a property-ben megadott számot.
      e.Graphics.DrawString(FNumber.ToString(), new Font("Verdana", 10, FontStyle.Bold), new SolidBrush(Color.Black), 10, 10);
    }    
Nézzük most a NumberEditorClass.cs forrását, ahol elkészítjük az új property szerkesztő osztályt, melyet az UITypeEditor osztályból származtatunk.
  public class NumberEditorClass: System.Drawing.Design.UITypeEditor
  {
Ebben két függvény felülírására lesz szükségünk. Az egyik a GetEditStyle függvény, melyen keresztül megadhatjuk, hogy milyen típusú property szerkesztőt hozunk létre.
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) 
    {
      if (context != null && context.Instance != null) 
      {
Ezt a UITypeEditorEditStyle felsorolt típus Modal elemének választásával tehetjük meg, melynek hatására a property-nk szerkesztésekor a Properties ablakban megjelenik egy kis nyomógomb, melynek most még funkciója nem lesz.
        return UITypeEditorEditStyle.Modal;
      }
      return base.GetEditStyle(context);
    }  
Ahhoz, hogy e kis gomb funkcióval is rendelkezzen, felül kell írnunk az EditValue függvényt is. Ez akkor kerül meghívásra, amikor a property értékének szerkesztése megkezdődik. Ez lesz az a pillanat, amikor a szükséges ablakot megjeleníthetjük.
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) 
    {
      if (context != null && context.Instance != null && provider != null) 
      {
Az ablak megjelenítéséhez nem árt, ha rendelkezünk eggyel. A NumberForm.cs forrásban készítünk egy Windows Form-ot, melyet az adatbevitelhez használunk. Itt most létrehozunk ebből egy példányt.
        NumberForm nf = new NumberForm();
A property aktuális értékét megkapjuk a függvény value paraméterén keresztül, így ezt most átadjuk a Form számára.
        nf.NumberValue = (int)value;
Ezt követően megjelenítjük az ablakot.
        nf.ShowDialog();
Az ablak bezárása után pedig kiolvassuk a Form-on megadott értéket. Ezt adjuk majd a függvény visszatérési értékének, így ezzel megváltoztatjuk a property aktuális tartalmát.
        value = nf.NumberValue;
      }
      return value;
    }      
Utolsó lépésként a nézzük a NumberForm.cs-t. Ebben találunk egy alap Form-ot, melyen egy TextBox szolgál a property értékének bevitelére, valamint egy gomb a Form bezárására.
    private void button1_Click(object sender, System.EventArgs e)
    {
      Close();
    }
Szükségünk lesz még egy int típusú property-re is, melyen keresztül elérhetjük a Form-on kívülről is a TextBox aktuális értékét.
    public int NumberValue
    {
      get { return Int32.Parse(textBox1.Text); }
      set { textBox1.Text = value.ToString();  }
    }
Ezzel a kontrol elkészült, lefordítása után felvehetjük azt a ToolBox-ra, ha ott jobb gombbal kattintunk és a Customize ToolBox menüpontot választjuk.
A TestApp projektben ezek után feltehetjük az imént készített kontrolt és annak Number property-jére kattintva megjelenik mellette kis nyomógomb. Ha ezt választjuk, akkor pedig meg kell jelennie a NumberForm-nak, ahol elvégezhetjük a property értékének szerkesztését.