2011-03-02 52 views
14

我想借此对象的下拉自定义对象的名单,让我们说这个对象:显示作为PropertiesGrid

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

,并有对象显示其LISTNAME为一体的“名称”部分PropertiesGrid(“基准”将是一件好事),并为PropertyGrid中的“价值”的部分,有基准的IList的<>的下拉列表:

这里是基准对象

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

我希望下拉列表显示用户可以看到的基准的名称属性。这里是一个可视化例子:

enter image description here

所以,从本质上讲,我试图让基准对象的集合到一个下拉列表,以及这些对象应该表现出他们的名称属性作为价值落下。

我读过使用PropertiesGrid的其他文章,包括THISTHIS,但它们比我想要做的更复杂。

我通常在服务器端的东西的工作,并且不通过的WebForms或WinForms的UI处理,所以这PropertiesGrid真的带我兜风...

我知道我的解决办法在于执行“ICustomTypeDescriptor”,它允许我告诉PropertiesGrid它应该显示什么值,而不管我想要绑定到下拉列表中的对象的属性如何,但我只是不确定如何或在哪里实现它。

任何指针/帮助将不胜感激。

谢谢, 迈克

UPDATE:

好了,所以我改变了周围的小细节。我以前就想过要涉足这些物体,所以这里是我的新方法。

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

下面是解析的代码,我想绑定到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代码:

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)值。当你点击(集合),你得到的集编辑器,然后可以看到每个对象,以及它们各自的特性:

enter image description here

这不是我要找的。就像在我的第一个屏幕抓住这篇文章,我试图呈现列表属性Benchmark集合作为下拉列表,显示对象的名称属性作为可以显示的文本...

谢谢

回答

30

通常,属性网格中的下拉列表用于设置给定列表中属性的值。这意味着您应该更好地拥有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的(如上),并用类型转换器这样的IBenchmark接口来装点基准属性:

// 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; } 
} 

下面是一个样本类型转换器的实现:

// 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(); 
    } 
} 
+0

Simon,一切都很好!我了解BenchmarkTypeConverter类,但BenchmarkTypeEdit类仍然是我的一个谜。它看起来像是每当你点击下拉菜单时,整个列表必须重新构建,然后选择新的值。我认为这就是PropertyGrid必须在“幕后”工作的原因,这就是为什么做一些非常简单的事情可以花费大量代码。无论哪种方式,非常感谢您伸出援助之手!我之前还很远。一些好的业力来到你的路上!迈克 – 2011-03-03 20:11:45

+0

一些其他很好的资源来解释西蒙在这里做了什么。西蒙,再次感谢! http://msdn.microsoft.com/en-us/library/ms171839.aspx http://msdn.microsoft.com/en-us/library/ms171840.aspx – 2011-03-10 17:28:07

+0

西蒙你已经很好地解释了这个概念并感谢你一个非常好的和简单的工作示例:)干杯! – 2016-09-09 11:01:03