display list of custom objects as a drop-down in the PropertiesGrid

后端 未结 1 938
陌清茗
陌清茗 2020-12-06 01:20

I want to take an object, let\'s say this object:

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


        
相关标签:
1条回答
  • 2020-12-06 01:54

    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();
        }
    }
    
    0 讨论(0)
提交回复
热议问题