将自定义对象列表显示为PropertiesGrid中的下拉列表

我想拿一个对象,让我们说这个对象:

public class BenchmarkList { public string ListName { get; set; } public IList Benchmarks { get; set; } } 

并让该对象将其ListName显示为PropertiesGrid的“name”部分(“Benchmark”将是好的),并且对于PropertyGrid的“value”部分,要有一个IList of Benchmarks的下拉列表:

这是Benchmark对象

 public class Benchmark { public int ID {get; set;} public string Name { get; set; } public Type Type { get; set; } } 

我希望下拉列表显示基准的Name属性,以便用户可以看到。 这是一个视觉示例:

在此处输入图像描述

所以,基本上,我正在尝试将Benchmark对象的集合放入下拉列表中,并且这些对象应该将其Name属性显示为下拉列表中的值。

我已经阅读了有关使用PropertiesGrid的其他文章,包括THIS和THIS ,但它们比我想做的更复杂。

我经常处理服务器端的东西,不通过WebForms或WinForms处理UI,所以这个PropertiesGrid真的带我去旅行…

我知道我的解决方案在于实现“ICustomTypeDescriptor”,这将允许我告诉PropertiesGrid它应该显示哪些值,而不管我想要绑定到下拉列表中的对象的属性,但我是只是不确定如何或在何处实施它。

任何指针/帮助将非常感激。

谢谢,迈克

更新:

好的,所以我正在改变一些细节。 我之前已经过度考虑了我认为应该参与的对象,所以这是我的新方法。

我有一个名为Analytic的对象。 这是应该绑定到PropertiesGrid的对象。 现在,如果我公开一个枚举类型的属性,PropertiesGrid将为我处理下拉列表,这是非常好的。 如果我公开一个属于自定义类型集合的属性,那么PropertiesGrid就不那么好……

以下是Analytic的代码,我想要绑定到PropertiesGrid的对象:

 public class Analytic { public enum Period { Daily, Monthly, Quarterly, Yearly }; public Analytic() { this.Benchmark = new List(); } public List Benchmark { get; set; } public Period Periods { get; set; } public void AddBenchmark(IBenchmark benchmark) { if (!this.Benchmark.Contains(benchmark)) { this.Benchmark.Add(benchmark); } } } 

以下是实现IBenchmark接口的两个对象的简短示例:

 public class Vehicle : IBenchmark { public Vehicle() { this.ID = "00000000-0000-0000-0000-000000000000"; this.Type = this.GetType(); this.Name = "Vehicle Name"; } public string ID {get;set;} public Type Type {get;set;} public string Name {get;set;} } public class PrimaryBenchmark : IBenchmark { public PrimaryBenchmark() { this.ID = "PrimaryBenchmark"; this.Type = this.GetType(); this.Name = "Primary Benchmark"; } public string ID {get;set;} public Type Type {get;set;} public string Name {get;set;} } 

这两个对象将添加到WinForms代码中的Analytic对象的Benchmark List集合中:

 private void Form1_Load(object sender, EventArgs e) { Analytic analytic = new Analytic(); analytic.AddBenchmark(new PrimaryBenchmark()); analytic.AddBenchmark(new Vehicle()); propertyGrid1.SelectedObject = analytic; } 

这是PropertiesGrid中输出的屏幕抓取。 请注意,作为枚举公开的属性获得了一个没有工作的好的下拉列表,但是作为List on公开的属性获得了值(Collection)。 当您单击(Collection)时,您将获得Collection编辑器,然后可以查看每个对象及其各自的属性:

在此处输入图像描述

这不是我想要的。 就像我在这篇文章中的第一个屏幕抓取一样,我正在尝试将List的属性Benchmark集合呈现为一个下拉列表,该列表将对象的name属性显示为可以显示的内容的文本…

谢谢

通常,属性网格中的下拉列表用于从给定列表设置属性的值。 这意味着你应该更好地拥有像IBenchmark类型的“Benchmark”这样的属性以及其他地方可能的IBenchmark列表。 我冒昧地改变你的分析类这样:

 public class Analytic { public enum Period { Daily, Monthly, Quarterly, Yearly }; public Analytic() { this.Benchmarks = new List(); } // define a custom UI type editor so we can display our list of benchmark [Editor(typeof(BenchmarkTypeEditor), typeof(UITypeEditor))] public IBenchmark Benchmark { get; set; } [Browsable(false)] // don't show in the property grid public List Benchmarks { get; private set; } public Period Periods { get; set; } public void AddBenchmark(IBenchmark benchmark) { if (!this.Benchmarks.Contains(benchmark)) { this.Benchmarks.Add(benchmark); } } } 

你现在需要的不是ICustomTypeDescriptor,而是TypeConverterUITypeEditor 。 您需要使用UITypeEditor(如上所述)和带有TypeConverter的IBenchmark接口来装饰Benchmark属性,如下所示:

 // use a custom type converter. // it can be set on an interface so we don't have to redefine it for all deriving classes [TypeConverter(typeof(BenchmarkTypeConverter))] public interface IBenchmark { string ID { get; set; } Type Type { get; set; } string Name { get; set; } } 

以下是TypeConverter实现的示例:

 // this defines a custom type converter to convert from an IBenchmark to a string // used by the property grid to display item when non edited public class BenchmarkTypeConverter : TypeConverter { public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { // we only know how to convert from to a string return typeof(string) == destinationType; } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (typeof(string) == destinationType) { // just use the benchmark name IBenchmark benchmark = value as IBenchmark; if (benchmark != null) return benchmark.Name; } return "(none)"; } } 

这是一个示例UITypeEditor实现:

 // this defines a custom UI type editor to display a list of possible benchmarks // used by the property grid to display item in edit mode public class BenchmarkTypeEditor : UITypeEditor { private IWindowsFormsEditorService _editorService; public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { // drop down mode (we'll host a listbox in the drop down) return UITypeEditorEditStyle.DropDown; } public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { _editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); // use a list box ListBox lb = new ListBox(); lb.SelectionMode = SelectionMode.One; lb.SelectedValueChanged += OnListBoxSelectedValueChanged; // use the IBenchmark.Name property for list box display lb.DisplayMember = "Name"; // get the analytic object from context // this is how we get the list of possible benchmarks Analytic analytic = (Analytic)context.Instance; foreach (IBenchmark benchmark in analytic.Benchmarks) { // we store benchmarks objects directly in the listbox int index = lb.Items.Add(benchmark); if (benchmark.Equals(value)) { lb.SelectedIndex = index; } } // show this model stuff _editorService.DropDownControl(lb); if (lb.SelectedItem == null) // no selection, return the passed-in value as is return value; return lb.SelectedItem; } private void OnListBoxSelectedValueChanged(object sender, EventArgs e) { // close the drop down as soon as something is clicked _editorService.CloseDropDown(); } }