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

时间:2011-03-02 17:20:29

标签: c# winforms propertygrid

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

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属性,以便用户可以看到。这是一个直观的例子:

enter image description here

因此,基本上,我试图将一组Benchmark对象放入一个下拉列表中,这些对象应该将其Name属性显示为下拉列表中的值。

我已经阅读了有关使用PropertiesGrid的其他文章,包括THISTHIS,但它们比我正在尝试的更复杂。

我经常处理服务器端的东西,并且不通过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编辑器,然后可以查看每个对象及其各自的属性:

enter image description here

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

由于

1 个答案:

答案 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,而是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();
    }
}