Benutzer mit den meisten Antworten
die usercontrol Eigenschaftenliste bei PropertyGrid als eine combobox anzeigen lassen

Frage
-
Hallo,
ich habe einen UserControl geschrieben. Er hat eine "Feldname"-Eigenschaft als string . Diese soll im View Design unter PropertyGrid aus einer ComboBox-Liste ausgewählt werden können. Die Items für diese ComboBox will ich dynamisch auffüllen.
Wie kann man in einem usercontrol die Eigenschaften als items einer combobox anzeigen lassen? So, dass man die ComboBox aufklappen kann und einen Wert auswählen.
Danke!
Antworten
-
Hallo,
Du könntest einen UITypeEditor verwenden. Etwa so:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; using System.Drawing.Design; using System.Windows.Forms.Design; namespace UiTypeEditTest { public partial class UserControl1 : UserControl { private List<string> _list = new List<string>() { "one", "two", "three" }; [Browsable(false)] public List<string> StringList { get { return _list; } set { _list = value; } } public UserControl1() { InitializeComponent(); } [Editor(typeof(MyDropDownEditor), typeof(UITypeEditor))] public string MyProperty { get; set; } } class MyDropDownEditor : UITypeEditor { private string _value; private IWindowsFormsEditorService _edsv = null; private ListBox _list = new ListBox(); public MyDropDownEditor() { _list.SelectedValueChanged += _list_SelectedValueChanged; } void _list_SelectedValueChanged(object sender, EventArgs e) { _value = _list.SelectedItem.ToString(); _edsv.CloseDropDown(); } public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; } public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { _list.Items.Clear(); _list.Items.AddRange((context.Instance as UserControl1).StringList.ToArray()); _value = (string)value; _edsv = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); _edsv.DropDownControl(_list); return _value; } } }
Wenn Du die Liste der angezeigten Werte ändern willst, mußt Du nur userControl1.StringList = new List<string>() {"four", "five", "six"} ändern.
Marcel
- Als Antwort markiert Reticent77 Dienstag, 29. Juni 2010 13:04
-
Hallo,
wie schon angedeutet : Über das ITypeDescriptorContext Argument.
Voraussetzung ist dabei das der TypeConverter in einer Umgebung eingesetzt wird,
die diesen Kontext setzt. Das ist im PropertyGrid zur Entwurfszeit der Fall und
auch zur Laufzeit der Fall, wenn Du Dein Steuerelement SelectedObject zuweist.Einen UITypeEditor wie ihn Marcel zeigt braucht man gemeinhin nicht.
Denn das PropertyGrid ist schon "schlau" genug eine ComboBox darzustellen,
wenn StandardValues bereitgestellt werden.
Der wäre erst erforderlich wenn Du mehr an der Optik drehen willst.Das Beispiel von gestern (was aus einem älteren Projekt stammte) für ein UserControl umgedeutet:
using System; using System.ComponentModel; using System.Windows.Forms; namespace ElmarBoye.Samples.Forms { public partial class FeldNamenUserControl : UserControl { private string _feldName; public event EventHandler FeldNameChanged; public FeldNamenUserControl() { InitializeComponent(); } /// <summary>Liste der Feldnamen für den TypeConverter</summary> internal string[] FeldNamen { get { // Ein wenig variiert um die Herkunft zu verdeutlichen if (this.DesignMode) return new[] { "5. Wert", "6. Wert", "7. Wert" }; else return new[] { "4. Wert", "5. Wert", "6. Wert" }; } } [TypeConverter(typeof(FeldNameConverter))] public string FeldName { get { return this._feldName; } set { if (this._feldName != value) { this._feldName = value; OnFeldNameChanged(EventArgs.Empty); } } } protected virtual void OnFeldNameChanged(EventArgs e) { // Zur Verwendung bringen... this.label1.Text = this.FeldName; var handler = this.FeldNameChanged; if (handler != null) handler(this, e); } } public class FeldNameConverter : System.ComponentModel.StringConverter { private StandardValuesCollection _standardValues = null; public FeldNameConverter() { } /// <summary>Auflistung mit Standardwerten zurückliefern</summary> public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { if (context != null) { FeldNamenUserControl control = context.Instance as FeldNamenUserControl; if (control != null) return new StandardValuesCollection(control.FeldNamen); } // Irgendeine andere Vorgabe (ggf. auch leer) // ...hier wie gestern... TypeConverter.StandardValuesCollection values = this._standardValues; if (this._standardValues == null) { this._standardValues = new StandardValuesCollection(new[] { "1. Wert", "2. Wert", "3. Wert" }); } return this._standardValues; } /// <summary>GetStandardValues wird unterstützt.</summary> public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } } }
Ich habe die Rückgaben etwas variiert, damit man besser den Context erkennt.
Willst Du das Konzept universeller einsetzen, solltest Du eine Schnittstelle verwenden,
denn derzeit ist durch context.Instance as FeldNamenUserControl; eine direkte Bindung
an das Steuerelement gegeben.Gruß Elmar
P. S.: Bitte keine Fullquotes -
am besten auf Zitate ganz verzichten, das Forum ist eh schon träge genug.- Als Antwort markiert Reticent77 Dienstag, 29. Juni 2010 13:00
-
Hallo,
Hier ein funktionsfähiges Projekt [VS 2010] diesbezüglich, das eine dynamische DropDown-Liste für eine string-Eigenschaft: "FeldName" ermöglicht: [Download]. Implementation im Prinzip gemäß: [Optimale Nutzung des PropertyGrid-Steuerelements in .NET Framework] aber mit ein paar (wichtigen) Zusätzen, die nicht im Artikel stehen.
ciao Frank- Als Antwort markiert Reticent77 Dienstag, 29. Juni 2010 13:44
Alle Antworten
-
Hallo,
ich habe einen UserControl geschrieben. Er hat eine "Feldname"-Eigenschaft als string . Diese soll im View Design unter PropertyGrid aus einer ComboBox-Liste ausgewählt werden. Die Items für diese ComboBox will ich dynamisch auffüllen.
Wie kann man in einem usercontrol die Eigenschaften als items einer combobox anzeigen lassen?
wenn es nicht vollständig dynamisch sein muss, kann man die Eigenschaft, statt String mit einem eigenen Enum deklarieren. Im PropertyGrid werden dann automatisch die möglichen Werte dieses Enum in einer ComboBox vorgeschlagen.
public enum CustomEnum { Entry1, Entry2 } public class Foo { public Foo() { } public CustomEnum CustomProperty { get; set; } }
Möchtest Du dagegen völlig dynamische Werte anzeigen, wird es aufwändiger. Schau Dir dazu mal folgenden Artikel in der MSDN an:
Getting the Most Out of the .NET Framework PropertyGrid Control
http://msdn.microsoft.com/en-us/library/aa302326.aspx
=> Adding Domain List and Simple Drop-down Property Support
Thorsten Dörfler
Microsoft MVP Visual Basic
vb-faq.de- Als Antwort vorgeschlagen Frank Dzaebel Dienstag, 29. Juni 2010 13:21
-
Hallo,
erstelle dafür einen TypeConverter , dort überschreibst Du GetStandardValues
und GetStandardValuesSupported und ggf. GetStandardValuesExclusive.
Am einfachsten wäre eine Ableitung von System.ComponentModel.StringConverter.Ein einfaches Beispiel:
public class StringPropertyConverter : System.ComponentModel.StringConverter { private StandardValuesCollection _standardValues = null; public StringPropertyConverter() { } /// <summary>Auflistung mit Standardwerten zurückliefern</summary> public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { TypeConverter.StandardValuesCollection values = this._standardValues; if (this._standardValues == null) { this._standardValues = new StandardValuesCollection(new[] { "1. Wert", "2. Wert", "3. Wert" }); } return this._standardValues; } /// <summary>GetStandardValues wird unterstützt.</summary> public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } }
Und bei der Eigenschaft gibst Du dann den TypeConverter an:
string _stringProperty; [TypeConverter(typeof(StringPropertyConverter))] public string StringProperty { get { return this._stringProperty; } set { if (this._stringProperty != value) { this._stringProperty = value; RaisePropertyChanged("StringProperty"); } } }
Soll die Liste dynamisch ausfallen, mußt Du den ITypeDescriptorContext auswerten.
Mehr siehe: Gewusst wie: Implementieren eines TypkonvertersGruß Elmar
-
danke schön. Den Artikel schaue ich mir mal an.
Eigentlich will ich die möglichen Werte als eine Liste übergeben, weil sie nicht vorhersehbar sind. D.h. es muss schon dynamisch sein.
Genauer gesagt, ich will nur, dass die Werte so wie z.B. bei der Farbauswahl angezeigt werden.
-
Hallo,
Du könntest einen UITypeEditor verwenden. Etwa so:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; using System.Drawing.Design; using System.Windows.Forms.Design; namespace UiTypeEditTest { public partial class UserControl1 : UserControl { private List<string> _list = new List<string>() { "one", "two", "three" }; [Browsable(false)] public List<string> StringList { get { return _list; } set { _list = value; } } public UserControl1() { InitializeComponent(); } [Editor(typeof(MyDropDownEditor), typeof(UITypeEditor))] public string MyProperty { get; set; } } class MyDropDownEditor : UITypeEditor { private string _value; private IWindowsFormsEditorService _edsv = null; private ListBox _list = new ListBox(); public MyDropDownEditor() { _list.SelectedValueChanged += _list_SelectedValueChanged; } void _list_SelectedValueChanged(object sender, EventArgs e) { _value = _list.SelectedItem.ToString(); _edsv.CloseDropDown(); } public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; } public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { _list.Items.Clear(); _list.Items.AddRange((context.Instance as UserControl1).StringList.ToArray()); _value = (string)value; _edsv = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); _edsv.DropDownControl(_list); return _value; } } }
Wenn Du die Liste der angezeigten Werte ändern willst, mußt Du nur userControl1.StringList = new List<string>() {"four", "five", "six"} ändern.
Marcel
- Als Antwort markiert Reticent77 Dienstag, 29. Juni 2010 13:04
-
Hallo,
Hier ein funktionsfähiges Projekt [VS 2010] diesbezüglich, das eine dynamische DropDown-Liste für eine string-Eigenschaft: "FeldName" ermöglicht: [Download]. Implementation im Prinzip gemäß: [Optimale Nutzung des PropertyGrid-Steuerelements in .NET Framework] aber mit ein paar (wichtigen) Zusätzen, die nicht im Artikel stehen.
ciao Frank- Als Antwort markiert Reticent77 Dienstag, 29. Juni 2010 13:44
-
Hallo,
Du könntest einen UITypeEditor verwenden. Etwa so:
Wenn Du die Liste der angezeigten Werte ändern willst, mußt Du nur userControl1.StringList = new List<string>() {"four", "five", "six"} ändern.
Marcel
_list.Items.AddRange((context.Instance as UserControl1).StringList.ToArray());
Ich verstehe nicht wieso...
-
Hallo,
erstelle dafür einen TypeConverter , dort überschreibst Du GetStandardValues
und GetStandardValuesSupported und ggf. GetStandardValuesExclusive.
Am einfachsten wäre eine Ableitung von System.ComponentModel.StringConverter.Ein einfaches Beispiel:
Soll die Liste dynamisch ausfallen, mußt Du den ITypeDescriptorContext auswerten.
Mehr siehe: Gewusst wie: Implementieren eines TypkonvertersGruß Elmar
- Bearbeitet Reticent77 Dienstag, 29. Juni 2010 13:01
-
Dann wird StringList irgendwo im Code zwischenzeitlich erneut auf null gesetzt. Mach mal bitte folgendes: Entferne alle Haltepunkte. Erstelle eine neue Form mit einem PropertyGrid, ziehe das UserControl auf den Form Designer und im Load-Handler setze dann propertyGrid1.SelectedObject = t1. Starte die Anwendung.
-
ähm... macht es einen Unterschied, wenn ich einfach eine eigene Klasse von Control ableite und nicht einen UserControl benutze?
Ich arbeite mit Form Designer. Wenn ich mein Control auf meine Form platziere, erscheinen alle eigenschaften im PropertyGrid. Nur wenn ich die Liste von MyProperty ausfallen lassen möchte, bekomme ich den Fehler. Wenn ich natürlich im Konstruktor von MyDropDownEditor die _list mit Werten fülle, dann funktioniert es. Es klappt nur nicht zur Laufzeit die Listenelemente zu verändern... :(
-
Es klappt nur nicht zur Laufzeit die Listenelemente zu verändern... :(
Das glaube ich einfach nicht. Aber machen wir's einfach: Lade mal bitte Dein Testcode auf SkyDrive hoch (oder anderswo) und poste einen Link hier. Ich schau es mir an und melde mich bei Dir. Es wird sicherlich eine Lösung geben.Marcel
-
Einige Überprüfungen sind nicht ganz fehl am Platz:
if (context != null) { if (context.Instance != null) { UserControl1 uc1 = context.Instance as UserControl1; if (uc1 != null) _listBox.Items.AddRange(uc1.StringList.ToArray()); else throw (new NullReferenceException("uc1 war null in UserControl1.EditValue()")); } else { throw (new NullReferenceException("context.Instance war null in UserControl1.EditValue()")); } } else { throw (new NullReferenceException("ITypeDescriptorContext war null in UserControl1.EditValue()")); }
Überprüfe auch den Typ von context.Instance (falls es nicht null ist).
Marcel
-
Hallo,
wie schon angedeutet : Über das ITypeDescriptorContext Argument.
Voraussetzung ist dabei das der TypeConverter in einer Umgebung eingesetzt wird,
die diesen Kontext setzt. Das ist im PropertyGrid zur Entwurfszeit der Fall und
auch zur Laufzeit der Fall, wenn Du Dein Steuerelement SelectedObject zuweist.Einen UITypeEditor wie ihn Marcel zeigt braucht man gemeinhin nicht.
Denn das PropertyGrid ist schon "schlau" genug eine ComboBox darzustellen,
wenn StandardValues bereitgestellt werden.
Der wäre erst erforderlich wenn Du mehr an der Optik drehen willst.Das Beispiel von gestern (was aus einem älteren Projekt stammte) für ein UserControl umgedeutet:
using System; using System.ComponentModel; using System.Windows.Forms; namespace ElmarBoye.Samples.Forms { public partial class FeldNamenUserControl : UserControl { private string _feldName; public event EventHandler FeldNameChanged; public FeldNamenUserControl() { InitializeComponent(); } /// <summary>Liste der Feldnamen für den TypeConverter</summary> internal string[] FeldNamen { get { // Ein wenig variiert um die Herkunft zu verdeutlichen if (this.DesignMode) return new[] { "5. Wert", "6. Wert", "7. Wert" }; else return new[] { "4. Wert", "5. Wert", "6. Wert" }; } } [TypeConverter(typeof(FeldNameConverter))] public string FeldName { get { return this._feldName; } set { if (this._feldName != value) { this._feldName = value; OnFeldNameChanged(EventArgs.Empty); } } } protected virtual void OnFeldNameChanged(EventArgs e) { // Zur Verwendung bringen... this.label1.Text = this.FeldName; var handler = this.FeldNameChanged; if (handler != null) handler(this, e); } } public class FeldNameConverter : System.ComponentModel.StringConverter { private StandardValuesCollection _standardValues = null; public FeldNameConverter() { } /// <summary>Auflistung mit Standardwerten zurückliefern</summary> public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { if (context != null) { FeldNamenUserControl control = context.Instance as FeldNamenUserControl; if (control != null) return new StandardValuesCollection(control.FeldNamen); } // Irgendeine andere Vorgabe (ggf. auch leer) // ...hier wie gestern... TypeConverter.StandardValuesCollection values = this._standardValues; if (this._standardValues == null) { this._standardValues = new StandardValuesCollection(new[] { "1. Wert", "2. Wert", "3. Wert" }); } return this._standardValues; } /// <summary>GetStandardValues wird unterstützt.</summary> public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } } }
Ich habe die Rückgaben etwas variiert, damit man besser den Context erkennt.
Willst Du das Konzept universeller einsetzen, solltest Du eine Schnittstelle verwenden,
denn derzeit ist durch context.Instance as FeldNamenUserControl; eine direkte Bindung
an das Steuerelement gegeben.Gruß Elmar
P. S.: Bitte keine Fullquotes -
am besten auf Zitate ganz verzichten, das Forum ist eh schon träge genug.- Als Antwort markiert Reticent77 Dienstag, 29. Juni 2010 13:00
-
-
Hallo Marcel,
ich hole meine Brötchen zu Fuß solange es noch geht -
ein wenig Bewegung schadet bekanntlich nicht...
In einen UITypeEditor steige ich erst ein, wenn es bunt hergehen soll,
und nicht nur eine popelige Käfer-ListBox werden soll ;-))Gruß Elmar
-
also, alle eure Vorschläge sind richtig (wie gedacht) und funktionieren solange man einen UserControl im Designer-Modus auf die Form bringt.
Ich benutze den Form Designer (mithilfe von DesignSurfaceExt-Klasse) um meine Forms zur Laufzeig erstellen zu können.
Ein Control wird immer mit einem <IDesignerHost> .CreateControl( Type ComponentClass) erstellt. Und genau das ist der Hacken!
Jetzt muss ich schauen wie man eure Vorschläge in dem Fall umsetzen kann...
-
Hallo,
wenn Du damit den CodeProject Artikel meinst:
Have a Great DesignTime Experience with a Powerful DesignSurface (Extended) Class
so sollte das funktionieren - solange Du nicht irgendwo dran gedreht hast.Ich habe das FeldNamenUserControl von vorhin mal in die Demo unter CreateDesignSurface integriert
var uc = (FeldNamenUserControl)surface.CreateControl(typeof(FeldNamenUserControl), new Size( 100, 40 ), new Point( 50, 50 ));
Dort wird das Steuerelement gefunden und als im DesignMode behandelt.Gruß Elmar
-
wenn Du damit den CodeProject Artikel meinst:
Have a Great DesignTime Experience with a Powerful DesignSurface (Extended) Class
so sollte das funktionieren - solange Du nicht irgendwo dran gedreht hast.
absolut richtig! Genau das meine ich.
Ich habe niergendwas gedreht. und genau so erstelle ich mein Control. Das Steuerelement wird ja auch im DesindMode behandelt. Angezeigt werden aber nur folgende Werte
this._standardValues = new StandardValuesCollection(new[] { "1. Wert", "2. Wert", "3. Wert" }
und die "5.Wert" usw. sind niergends zu finden.
Welche Werte siehst du bei dir im DesignMode?
-
Und mit meinem Code funktioniert es auch:
// In: DemoConsoleForDesignSurfaceExt // private void CreateDesignSurface ( int n ) case 1: { rootComponent = (Form)surface.CreateRootComponent(typeof(Form), new Size(400, 400)); rootComponent.BackColor = Color.Gray; rootComponent.Text = "Root Component hosted by the DesignSurface N.1"; //- step.3 //- create some Controls at DesignTime UserControl1 u1 = (UserControl1)surface.CreateControl(typeof(UserControl1), new Size(200, 20), new Point(10, 200)); u1.BackColor = Color.Orange; }
Getestet habe ich sowohl unter VS 2010 / .NET 4.0 als auch unter VS 2008 / .NET 3.5.
Gruß
MarcelThe DesignSurface (Extended) Class is Back, Together with a DesignSurfaceManager (Extended) Class and a Form Designer Demo!
By Paolo Foti | 24 Feb 2010:
http://www.codeproject.com/KB/miscctrl/DesignSurfaceManager_Ext.aspx -
tatsächlich! Wenn ich das Projekt so wie es ist benutze und den UserControl einfüge, dann funktioniert es so wie ihr sagt!!
Nur in meinem eigenen Projekt funktioniert nicht richtig :(
oh man, oh man, oh man.... Was habe ich nur falsch gemacht?
Ich schau mal lieber genauer hin und geh jede Zeile am besten noch mal durch...
-
Vielleicht noch eine Hilfe zur den gezeigten Codes hier.
Gern wird DesignMode benutzt. DesignMode ist aber u.a. in Konstruktoren und verschachtelten UserControls oft nicht wirksam - es gibt aber Alternativen. Aus der C# Gruppe kann ich da über die Jahre ein Lied von [singen]. In meinem [Download] von gestern ist übrigens noch eine [DesignerSerializationVisibility.Hidden] enthalten.
ciao Frank -
habe den Problemverursacher gefunden. Es liegt nicht an dem DesignerSurfaceExt, sondern an FilteredPropertyGrid ( hier gefunden ). Ich benutze es um nur bestimmte Eigenschaften anzuzeigen... Na so was.
Jetzt bin ich an dem dran... Anscheinen funktioniert es irgend wie nicht ganz richtig.
-
der FilteredPropertyGrid benutzt einen wrapper um die Eigenschaften zu filtern. Der wrapper übernimmt die im UserControl angelegten Sachen nicht richtig.
Jetzt überlege ich mir wieder zum standard PropertyGrid zu wechseln. Nur wie kann ich dort nur bestimmte Properties anzeigen lassen. Bis jetzt weiss ich nur wie man die BrowsableAttribute benutzt.
propertyGrid.BrowsableAttributes = new AttributeCollection(new Attribute[] { new CategoryAttribute("Layout") });
aber wenn ich z.B. 2 Attribute anzeigen möchtepropertyGrid.BrowsableAttributes = new AttributeCollection(new Attribute[] { new CategoryAttribute("Layout"), new CategoryAttribute("Darstellung") });
dann bleibt der PropertyGrid leer.Außerdem wie kann ich hier bestimmte einzellne Eigenschaften ausblenden?
Was ist jetzt besser? Wieder zum standard-PropertyGrid zu wechseln oder den Filtered zu fixen?
-
DANKE DANKE DANKE an alle die mitgeholfen haben! Ihr seid die Besten!
ich mache zu PropertyGrid lieber eine neue Frage auf...
- Als Antwort markiert Reticent77 Dienstag, 29. Juni 2010 13:18
- Tag als Antwort aufgehoben Thorsten DörflerEditor Dienstag, 29. Juni 2010 13:47
-
-
Hallo Thorsten,
wenn es nicht vollständig dynamisch sein muss, kann man die Eigenschaft, statt String mit einem eigenen Enum deklarieren. Im PropertyGrid werden dann automatisch die möglichen Werte dieses Enum in einer ComboBox vorgeschlagen.
public enum CustomEnum { Entry1, Entry2 } public CustomEnum CustomProperty { get; set; }
bei einem ähnliches Problem hat mir deine Antwort sehr geholfen!
Gruß Klaus