定制对象的显示列表作为PropertiesGrid下拉 [英] display list of custom objects as a drop-down in the PropertiesGrid
问题描述
我要带一个对象,让我们说这个对象:
公共类BenchmarkList
{
酒店的公共字符串LISTNAME {搞定;组; }
公众的IList<&基准GT;基准{搞定;组; }
}
和有对象显示其LISTNAME为一体的名称部分PropertiesGrid(基准将是一件好事),并为PropertyGrid的的价值的一部分,有IList中℃的下拉列表中,基准的>:
这里是基准对象
公共类基准
{
公众诠释ID {搞定;设置;}
公共字符串名称{;组; }
公共类型类型{搞定;组; }
}
我想下拉显示基准的名称属性什么用户可以看到。这里是一个可视化的例子:
所以,从本质上讲,我试图让基准对象的集合到一个下拉列表,以及这些对象应该显示自己的Name属性作为价值下拉。
我读过关于使用PropertiesGrid,其中的THIS 和本,但它们比更复杂我想要做的。
我通常在服务器端的东西的工作,而不要通过的WebForms或WinForms的用户界面与处理,所以这PropertiesGrid真的走我要骑...
我知道我的解决办法在于落实ICustomTypeDescriptor,这将让我告诉PropertiesGrid什么值应该不分显示该对象的属性,而我要绑定到下拉列表,但我只是不知道如何或在哪里实现它。
任何指针/ 。帮助将非常感激。
谢谢,
迈克
更新:
好了,所以我改变了周围的一点点细节。我之前去过分与我想应该参与的对象,所以这里是我的新方法。
我有一个名为分析的对象。这是应绑定到PropertiesGrid的对象。现在,如果我公开的属性是枚举类型的,PropertiesGrid会照顾下拉列表中对我来说,这是非常好的吧。如果我公开一个属性,它是一个自定义类型的集合,PropertiesGrid也不是那么好...
下面是解析的代码,我想绑定的对象到PropertiesGrid:
公共类分析
{
公共枚举期{每日,每月,每季度,每年};
公共分析()
{
this.Benchmark =新的List< IBenchmark>();
}
公开名单< IBenchmark>基准{搞定;组; }
公共期内的各个期间{搞定;组; }
公共无效AddBenchmark(IBenchmark基准)
{
如果(!this.Benchmark.Contains(基准))
{
this.Benchmark.Add(基准) ;
}
}
}
下面是一个简短的例子实现了IBenchmark接口的两个对象:
公共类车辆:IBenchmark
{
公共车辆()
{
this.ID =00000000-0000-0000-0000-000000000000;
this.Type = this.GetType();
this.Name =车辆名称;
}
公共字符串ID {获取;设置;}
公共类型类型{获取;集;}
公共字符串名称{;设置;}
}
公共类PrimaryBenchmark:IBenchmark
{
公共PrimaryBenchmark()
{
this.ID =PrimaryBenchmark;
this.Type = this.GetType();
this.Name =主要基准;
}
公共字符串ID {获取;设置;}
公共类型类型{获取;集;}
公共字符串名称{;设置;}
}
这两个对象将被加入到的WinForms代码中的分析对象的基准List集合:
私人无效Form1_Load的(对象发件人,EventArgs五)
{
解析解析=新分析() ;
analytic.AddBenchmark(新PrimaryBenchmark());
analytic.AddBenchmark(新车());
propertyGrid1.SelectedObject =分析;
}
下面是在PropertiesGrid输出的屏幕抓取。需要注意的是财产公开为枚举得到一个不错的下拉列表中没有工作,但是属性公开为列表上获得的(集合)的值。当你点击(集合),你得到的集编辑器,然后可以看到每个对象,它们各自的特性:
这是不是我要找的。就像在这篇文章我第一次屏幕抓取,我试图渲染列表的财产基准集合作为显示对象的名称属性作为可显示的内容文本下拉列表...
感谢
解决方案
在一般情况下,在属性网格下拉列表用于
公共类分析
{
公枚举期间{每日,每月,每季度,每年};
公共分析()
{
this.Benchmarks =新的List< IBenchmark>();
}
//定义自定义UI类型编辑器,所以我们可以展示我们的基准
$ B $名单b酒店的公共IBenchmark基准{搞定;组; }
[可浏览(假)] //不要在属性网格
公开名单<显示,IBenchmark>基准{搞定;私人集; }
公共期内的各个期间{搞定;组; }
公共无效AddBenchmark(IBenchmark基准)
{
如果(!this.Benchmarks.Contains(基准))
{
this.Benchmarks.Add(基准) ;
}
}
}
您现在需要的是不一个ICustomTypeDescriptor,而是一个的TypeConverter
的一个 UITypeEditor的
。您需要用UITypeEditor的(如上),并与像这样的TypeConverter的IBenchmark接口来装点基准属性:
//使用自定义类型转换器。
//它可以在一个界面上设置,所以我们不必重新定义它为所有派生类
[的TypeConverter(typeof运算(BenchmarkTypeConverter))]
公共接口IBenchmark
{
字符串ID {搞定;组; } $ B $型B型{搞定;组; }
字符串名称{;组; }
}
下面是一个简单的类型转换器实现:
//这个定义自定义类型转换器从IBenchmark转换为字符串
//使用属性网格中显示项目时不编辑
酒店的公共类BenchmarkTypeConverter:TypeConverter的
{
公众覆盖布尔CanConvertTo(ITypeDescriptorContext背景下,类型destinationType)
{
//我们只知道如何从一个字符串转换
返回typeof运算(字符串)== destinationType;
}
公众覆盖对象的ConvertTo(ITypeDescriptorContext方面,CultureInfo的文化,对象的值,类型destinationType)
{
如果(typeof运算(字符串)== destinationType)
{
//只需使用基准名称
IBenchmark基准=值IBenchmark;
如果(标杆!= NULL)
返回benchmark.Name;
}
回归(无);
}
}
这是一个示例UITypeEditor的实现:
//这个定义自定义UI类型编辑器来显示可能的基准
//使用属性网格显示列表在编辑模式下
公共类BenchmarkTypeEditor项目:UITypeEditor的
{
私人IWindowsFormsEditorService _editorService;
公众覆盖UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext上下文)
{
//下拉模式(我们将举办在下拉列表框)
返回UITypeEditorEditStyle.DropDown ;
}
公众覆盖对象的EditValue(ITypeDescriptorContext背景下,IServiceProvider的供应商,对象的值)
{
_editorService =(IWindowsFormsEditorService)provider.GetService(typeof运算(IWindowsFormsEditorService)) ;
//使用一个列表框
列表框磅=新的ListBox();
lb.SelectionMode = SelectionMode.One;
lb.SelectedValueChanged + = OnListBoxSelectedValueChanged;
//使用列表框中显示
lb.DisplayMember =名IBenchmark.Name财产;
//从上下文中
的分析对象//这是我们如何得到可能的基准
解析解析=(分析)context.Instance列表;
的foreach(IBenchmark标杆analytic.Benchmarks)
{
//我们存储基准测试对象直接在列表框中
INT指数= lb.Items.Add(基准);
如果(benchmark.Equals(值))
{
lb.SelectedIndex =指数;
}
}
//显示这个模型的东西
_editorService.DropDownControl(磅);
如果(lb.SelectedItem == NULL)//没有选择,则返回传入的值是
返回值;
返回lb.SelectedItem;
}
私人无效OnListBoxSelectedValueChanged(对象发件人,EventArgs五)
{
//只要东西被点击
_editorService关闭下拉。 CloseDropDown();
}
}
I want to take an object, let's say this object:
public class BenchmarkList
{
public string ListName { get; set; }
public IList<Benchmark> Benchmarks { get; set; }
}
and have that object display its ListName as the "name" part of the PropertiesGrid ("Benchmark" would be good), and for the "value" part of the PropertyGrid, to have a drop-down list of the IList<> of Benchmarks:
here is the Benchmark object
public class Benchmark
{
public int ID {get; set;}
public string Name { get; set; }
public Type Type { get; set; }
}
I would want the drop-down to show the Name property of the Benchmark for what the users can see. Here is a visual example:
So, essentially, I'm trying to get a collection of Benchmark objects into a drop-down list, and those objects should show their Name property as the value in the drop-down.
I've read other articles on using the PropertiesGrid, including THIS and THIS, but they are more complex than what I'm trying to do.
I usually work on server-side stuff, and don't deal with UI via WebForms or WinForms, so this PropertiesGrid is really taking me for a ride...
I do know my solution lies in implementing "ICustomTypeDescriptor", which will allow me to tell the PropertiesGrid what values it should be displaying regardless of the properties of the object to which I want to bind into the drop-down list, but I'm just not sure how or where to implement it.
Any pointers/help would be much appreciated.
Thanks, Mike
UPDATE:
Okay, so I'm changing the details around a little. I was going overboard before with the objects I thought should be involved, so here is my new approach.
I have an object called Analytic. This is the object that should be bound to the PropertiesGrid. Now, if I expose a property that is of an enum type, PropertiesGrid will take care of the drop-down list for me, which is very nice of it. If I expose a property that is a collection of a custom type, PropertiesGrid is not so nice...
Here is the code for Analytic, the object I want to bind to the PropertiesGrid:
public class Analytic
{
public enum Period { Daily, Monthly, Quarterly, Yearly };
public Analytic()
{
this.Benchmark = new List<IBenchmark>();
}
public List<IBenchmark> Benchmark { get; set; }
public Period Periods { get; set; }
public void AddBenchmark(IBenchmark benchmark)
{
if (!this.Benchmark.Contains(benchmark))
{
this.Benchmark.Add(benchmark);
}
}
}
Here is a short example of two objects that implement the IBenchmark interface:
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;}
}
These two objects will be added to the Analytic object's Benchmark List collection in the WinForms code:
private void Form1_Load(object sender, EventArgs e)
{
Analytic analytic = new Analytic();
analytic.AddBenchmark(new PrimaryBenchmark());
analytic.AddBenchmark(new Vehicle());
propertyGrid1.SelectedObject = analytic;
}
Here is a screen-grab of the output in the PropertiesGrid. Note that the property exposed as an enum gets a nice drop-down list with no work, but the property exposed as an of List on gets a value of (Collection). When you click on (Collection), you get the Collection editor and then can see each object, and their respective properties:
This is not what I'm looking for. Like in my first screen grab in this post, I'm trying to render the property Benchmark collection of List as a drop-down list that shows the object's name property as the text of what can be displayed...
Thanks
In general, a drop down list in a property grid is used for setting the value of a property, from a given list. Here that means you should better have a property like "Benchmark" of type IBenchmark and a possible list of IBenchmark somewhere else. I have taken the liberty of changing your Analytic class like this:
public class Analytic
{
public enum Period { Daily, Monthly, Quarterly, Yearly };
public Analytic()
{
this.Benchmarks = new List<IBenchmark>();
}
// 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<IBenchmark> Benchmarks { get; private set; }
public Period Periods { get; set; }
public void AddBenchmark(IBenchmark benchmark)
{
if (!this.Benchmarks.Contains(benchmark))
{
this.Benchmarks.Add(benchmark);
}
}
}
What you need now is not an ICustomTypeDescriptor, but instead a TypeConverter
an an UITypeEditor
. You need to decorate the Benchmark property with the UITypeEditor (as above) and the IBenchmark interface with the TypeConverter like this:
// 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; }
}
Here is a sample TypeConverter implementation:
// 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)";
}
}
And here is a sample UITypeEditor implementation:
// 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();
}
}
这篇关于定制对象的显示列表作为PropertiesGrid下拉的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!