我想拿一个对象,让我们说这个对象:
public class BenchmarkList
{
public string ListName { get; set; }
public IList<Benchmark> Benchmarks { get; set; }
}
并让该对象显示其ListName作为PropertiesGrid的“name”部分(“Benchmark”将是好的),并且对于PropertyGrid的“value”部分,要有一个IList的下拉列表&lt; &GT;基准:
这是基准对象
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<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);
}
}
}
以下是实现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属性显示为可显示内容的文本...
由于
答案 0 :(得分:32)
通常,属性网格中的下拉列表用于从给定列表设置属性的值。这意味着你应该更好地拥有像IBenchmark类型的“Benchmark”这样的属性以及其他地方可能的IBenchmark列表。我冒昧地改变你的分析类这样:
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);
}
}
}
您现在需要的不是ICustomTypeDescriptor,而是TypeConverter
和UITypeEditor
。您需要使用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();
}
}