Usercontrol mit List<customClass> Property
-
Hallo,
ich beschäftige mich gerade mit einem eigenen Usercontrol.
Wenn es dann mal fertig ist, soll es einfach im Designer von VS2010 zu benutzen und editieren sein.Und zwar handelt es sich um ein benutzerspezifisches Menü.
Das heist, der Benutzer soll einfach im Designer unter den Eigenschaften, beliebig neue Menüpunkte und Menüunterpunkte erstellen können.Ich bin jetzt soweit das ich eine Klasse MainItems und eine Klasse Subitems erstellt habe. Mein Usercontrol hat das Property List<MainItems> und die Klasse Mainitems hat wiederum eine List<SubItems>.
Soweit so gut.
Wenn ich nun im Designer die Property MainItems anwähle, öffnet sich ein neues Fenster wo ich die Einstellung des MainItems vornehmen kann. Allerdings wenn ich dann auf OK drücke, werden keine Information übernommen obwohl ich die Properties ändere.
Warum wird da nix gespeichert?Und zweitens, wie kann ich erreichen das, wenn die Daten im Designer übernommen werden, er dann automatisch z.b. ein Textlabel erstellt und dies dann auch im Designer angezeigt wird? Also beispielsweise erstelle ich im Designer den MainMenuItem "Datei", das dann auch tatsächlich ein Textlabel "Datei" erstellt und im Designer angeziegt wird.
Ich schätze ich muss dann irgentein Event in meinem CustumControl erstellen welches dann Feuert, wenn sich die Liste der Mainitems bzw. der Name des Items ändert. Aber sorgt der Designer auch dafür, dass das Event dann feuert und die Aufgaben (Code) ausgeführt wird?Das ganze zur Laufzeit erzeugen, ist soweit kein Problem. Aber ich hätte gerne die Möglichkeit das ich das Ganze Menü schon im Designer angezeigt bekomme.
Ich hoffe ihr versteht mein Anliegen!
Und hier noch ein bissle Code (wie schon gesagt ist noch zum testen):
namespace testMitUserControl { public partial class MenuList : UserControl { public List<MainItems> Items { get; set; } public MenuList() { InitializeComponent(); } } public class MainItems { public string Name { get; set; } public List<SubItems> SubItems { get; set; } } public class SubItems { public string Name{get; set;} } }
-
Versuchs mal so:
public partial class UserControl1 : UserControl { List<myItem> intern = new List<myItem>(); [System.ComponentModel.DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public List<myItem> myList { get { return intern; } set { intern = value; } } public UserControl1() { InitializeComponent(); } } [Serializable] public class myItem { public int myString { get; set; } }
-
Danke für den Tipp, hatte das so ähnlich gelöst und im jeweiligen Konstruktor die List<T> erst nocherzeugt. Also speichern klappt jetzt!
Aber wie kann ich zum Beispiel an der list<myItems> ein Event OnChangeItem erzeugen, welches dann automatisch das komplette menü aus allen Items erzeugt.
Also das erzeugen weiß ich ja schon, wie kann ich aber der Liste eine Event anhängen?Ich dachte da, ich erzeuge mir eine ein eigene List<T>:
class MyList<T> : List<T> { public event EventHandler OnAdd; public void Add(T item) { if (null != OnAdd) { OnAdd(this, null); } base.Add(item); } }
Aber das würde ja nur greifen wenn ein neues Item hinzukommt.
Wenn also zum Beispiel nur der Text eines Items geändert wird greift das nicht.Wie kann ich eine eine onItemChangeEvent richtig erzeugen?
und wenn ich das gemacht habe, wird das Event auch im Designer ausgeführt und ich sehe das tatsächliche Resultat?
-
Dafür gibt es die BindingList. Dann kannst Du auf Änderungen reagieren:
public BindingList<myItem> myList ...
myList.ListChanged += new ListChangedEventHandler(myList_ListChanged);
So kannst Du z.B. Invalidate() aufrufen um die Designer-Ansicht Deines Controls aktuell zu halten.
Aber ganz ehrlich, das kann nicht der richtige Weg sein. Nur wegen dem Designer nimmt man doch keine solchen Änderungen am Code in kauf. Ich benutze dieses Mistding sowieso nur noch selten.
An deiner Stelle würde ich mal nach weiteren Attributen googlen die sich auf den Designer auswirken.
-
Ich Danke dir!
Das ist genau das was ich gesucht habe!Ich möchte mich wenigstens einmal damit auseinander gesetzt haben und ich glaube das wird sich, jedenfalls wenn ich dieses Usercontrol fertiggestellt habe, für später lohnen.
Vielen Dank noch mal!
-
So, ich habe jetzt mein Userelement soweit überarbeitet und vieles klappt schon so wie es soll.
Allerdings habe ich derzeit noch ein ganz großes Problem:
Wenn ich im Designer ein Element der Liste hinzufüge kommt die Meldung:
Der Konstruktor für den Typ MenuMainItems konnte nicht gefunden werden.Das Problem ist, ich möchte gerne jedem Menüpunkt eine ID vergeben um später einfacher danach suchen zu können oder damit beim Erstellen gleich als Name "Subitem {1}" erscheint. Um das zu erreichen hat die Klasse den Konstruktor
MenuMainItems(int id);
Im normalen programm würde ich einfach mittels
myList.Add(new MenuMainItems(myList.Count));
die Sache abhaken. Aber der Visual Studio Designer akzeptiert anscheinend nur parameterlose Konstruktoren.
Meine nächste Idee war, die Binding Klasse abzuleiten und die Add-Methode zu überschreiben und dann dort wieder mitmyList.Add(new MenuMainItems(myList.Count));
die Objekte erzeugen. Aber das Klappt auch nicht. Der Visual Studio Designer geht nicht in diese Methode rein. Stattdessen liefert er wieder diese Fehlermeldung beim Debuggen. Wie kann ich das Problem lösen?
Gibt es vielleicht eine Möglichkeit, dem Designer zu verraten wie er neue Elemente in der Liste erzeugen soll? Ich meine es gibt ja die [DefaultBindingProperty] Eigenschaft, aber ich wüsste nicht wie ich das auf einer BindingList<T> mit Parametern anwenden sollte.Zum Schluss hab ich noch ein letztes Anliegen. Ich möchte gerne das meine Menü-SubItems später ein Klick-Event, nach Außen hin sichtbar, haben. So das ich im jeweiligen Projekt einfach sagen kann, was passieren soll wenn ich auf ein Subitem klicke. Ich habe dazu meiner MenuSubItems-Klasse einen
public delegate void OnClickedHandler(object sender, EventArgs e); public event OnClickedHandler Clicked;
hinzugefügt. Allerdings sehe ich im Designer nur die Properties und keine Events. Wie muss ich dort vorgehen?
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; namespace MenuList { public enum ButtonState { isActive, isNotActive }; public enum KindOfPanel { MainItem, SubItem}; public partial class MenuList : UserControl { //erzeugt Haupt und Untermenüpunkte //Diese Punkte werden in Form von "MenuPanels", einer //abgeleiteten Klasse von "Panels()" gespeichert //Es gibt eine Itemliste, in ihr sind alle MenuMainItems gespeichert //( public BindingList<MenuMainItems> _items ) //Jedes MenuMainItem hat wiederrum ein Liste mit Subitems // public BindingList<MenuSubItems> _items // Jeder dieser Klassen erzeugt Panels mit den jeweiligen gewünschten // Properties public BindingList<MenuMainItems> _items = new BindingList<MenuMainItems>(); [System.ComponentModel.DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public BindingList<MenuMainItems> Items { get { return _items;} set{ _items = value;} } int count; public MenuList() { InitializeComponent(); count = 0; MenuItems = new List<MenuPanels>(); Items.ListChanged += new ListChangedEventHandler(Items_ListChanged); Items.AddingNew += new AddingNewEventHandler(Items_AddingNew); Items.AddNew(); } private void Items_AddingNew(object sender, AddingNewEventArgs e) { e.NewObject = new MenuMainItems(count); count++; } private List<MenuPanels> MenuItems; public void drawMenu() { this.Controls.Clear(); MenuItems.Clear(); foreach (MenuMainItems X in this.Items) { MenuItems.Add(getMainMenuItemAsCompletePanel(X)); if (X.MenuPanel.ButtonState == ButtonState.isActive) { foreach(MenuSubItems Y in X.SubItems) { MenuItems.Add(getSubMenuItemAsCompletePanel(Y)); } } } this.Controls.AddRange(MenuItems.ToArray()); Invalidate(); } private Point getMenuItemLocation() { Point myLoc = new Point(0, 0); for (int i = 0; i < this.MenuItems.Count; i++) { myLoc.Y += MenuItems[i].Size.Height; } return myLoc; } private MenuPanels getMainMenuItemAsCompletePanel(MenuMainItems item) { //System.Windows.Forms.Panel panelMainMenuItem = new Panel(); item.MenuPanel.BackgroundImage = global::MenuList.Properties.Resources.MainMenuButton_Off; item.MenuPanel.Location = getMenuItemLocation(); item.MenuPanel.Size = new System.Drawing.Size(162, 54); item.MenuPanel.MouseEnter += new EventHandler(Panel_MouseEnter); item.MenuPanel.MouseLeave += new EventHandler(Panel_MouseLeave); item.MenuPanel.Click += new EventHandler(PanelMainMenuItems_Click); Label myTextOnItem = new Label(); myTextOnItem.Text = item.Text; myTextOnItem.ForeColor = Color.White; myTextOnItem.Font = new System.Drawing.Font("Tahoma", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); myTextOnItem.Location = new Point(0, 0); myTextOnItem.BackColor = Color.Transparent; item.MenuPanel.Controls.Add(myTextOnItem); return item.MenuPanel; } private void PanelMainMenuItems_Click(object sender, EventArgs e) { MenuPanels myPanel = (MenuPanels)sender; if (myPanel.ButtonState == ButtonState.isActive) { myPanel.ButtonState = ButtonState.isNotActive; } else { myPanel.ButtonState = ButtonState.isActive; } this.drawMenu(); } private void Panel_MouseEnter(object sender, EventArgs e) { MenuPanels myPanel = (MenuPanels)sender; if (myPanel.KindOfPanel == KindOfPanel.MainItem) { myPanel.BackgroundImage = global::MenuList.Properties.Resources.MainMenuButton_On; } else { myPanel.BackgroundImage = global::MenuList.Properties.Resources.subitem_on; } } private void Panel_MouseLeave(object sender, EventArgs e) { MenuPanels myPanel = (MenuPanels)sender; if (myPanel.KindOfPanel == KindOfPanel.MainItem) { myPanel.BackgroundImage = global::MenuList.Properties.Resources.MainMenuButton_Off; } else { myPanel.BackgroundImage = global::MenuList.Properties.Resources.subitem_off; } } private MenuPanels getSubMenuItemAsCompletePanel(MenuSubItems Item) { Item.MenuPanel.BackgroundImage = global::MenuList.Properties.Resources.subitem_off; Item.MenuPanel.Location = getMenuItemLocation(); Item.MenuPanel.Size = new System.Drawing.Size(162, 33); Item.MenuPanel.MouseEnter += new EventHandler(Panel_MouseEnter); Item.MenuPanel.MouseLeave += new EventHandler(Panel_MouseLeave); Label myTextOnItem = new Label(); myTextOnItem.Text = Item.Text; myTextOnItem.Font = new System.Drawing.Font("Tahoma", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); myTextOnItem.Location = new Point(20, 10); myTextOnItem.ForeColor = Color.Black; myTextOnItem.BackColor = Color.Transparent; Item.MenuPanel.Controls.Add(myTextOnItem); return Item.MenuPanel; } private void Items_ListChanged(object sender, ListChangedEventArgs e) { drawMenu(); } } [Serializable] public class MenuMainItems { private MenuPanels _panel; public MenuMainItems(int id) //: this( { _panel = new MenuPanels(KindOfPanel.MainItem,id); _name = string.Format("Hauptitem {0}", id); _text = string.Format("HauptitemText {0}", id); } public BindingList<MenuSubItems> _items = new BindingList<MenuSubItems>(); private string _name; private string _text; public BindingList<MenuSubItems> SubItems { get { return _items; } set { _items = value; } } public string Name { get { return _name; } set { _name = value; } } public string Text { get { return _text; } set { _text = value; } } public MenuPanels MenuPanel { get { return _panel; } }//set { _panel = value; } } } [Serializable] public class MenuSubItems { private MenuPanels _panel; MenuSubItems(int id) { _panel = new MenuPanels(KindOfPanel.SubItem,id); _panel.Click += new EventHandler(OnClicked); } private string _name; private string _text; public delegate void OnClickedHandler(object sender, EventArgs e); public event OnClickedHandler Clicked; protected virtual void OnClicked(object sender, EventArgs e) { if (Clicked != null) { Clicked(sender,e); // Notify Subscribers } } public string Name { get { return _name; } set { _name = value; } } public string Text { get { return _text; } set { _text = value; } } public MenuPanels MenuPanel { get { return _panel; } }//set { _panel = value; } } } [Serializable] public class MenuPanels : Panel { int _panelID; public MenuPanels(KindOfPanel kind, int id) { _panelID = id; _kind = kind; _state = global::MenuList.ButtonState.isNotActive; } private ButtonState _state; private KindOfPanel _kind; public KindOfPanel KindOfPanel { get { return _kind; } set { _kind = value; } } public ButtonState ButtonState { get { return _state; } set { _state = value; } } public int PanelID { get { return _panelID; } } } }