отображать список пользовательских объектов в виде раскрывающегося списка в PropertiesGrid - PullRequest
15 голосов
/ 02 марта 2011

Я хочу взять объект, скажем, этот объект:

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

и чтобы этот объект отображал свое ListName в качестве части «name» в PropertiesGrid («Benchmark» было бы хорошо), а для части «value» в PropertyGrid должен быть раскрывающийся список IList < > контрольных показателей:

вот эталонный объект

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

Я бы хотел, чтобы в раскрывающемся списке отображалось свойство Имя эталонного теста для того, что могут видеть пользователи. Вот наглядный пример:

enter image description here

Итак, по сути, я пытаюсь получить коллекцию объектов Benchmark в раскрывающемся списке, и эти объекты должны отображать свое свойство Name в качестве значения в раскрывающемся списке.

Я читал другие статьи об использовании PropertiesGrid, в том числе ЭТО и ЭТО , но они более сложны, чем то, что я пытаюсь сделать.

Я обычно работаю над серверными вещами и не имею дело с пользовательским интерфейсом через WebForms или WinForms, так что эта PropertiesGrid действительно возьмет меня с собой ...

Я знаю, что мое решение заключается в реализации «ICustomTypeDescriptor», который позволит мне сообщить PropertiesGrid, какие значения он должен отображать, независимо от свойств объекта, к которому я хочу привязаться, в раскрывающемся списке, но Я просто не уверен, как и где это реализовать.

Любые указатели / помощь будет принята с благодарностью.

Спасибо, Mike

UPDATE:

Хорошо, поэтому я немного изменяю детали. Раньше я шел за борт с объектами, которые, как я думал, должны быть задействованы, так что вот мой новый подход.

У меня есть объект с именем Analytic. Это объект, который должен быть привязан к PropertiesGrid. Теперь, если я предоставлю свойство, имеющее тип enum, 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;}
}

Эти два объекта будут добавлены в коллекцию списка тестовых объектов объекта Analytic в коде 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. Обратите внимание, что свойство, представленное в виде enum, получает хороший выпадающий список без работы, но свойство, представленное как из List on, получает значение (Collection). Когда вы нажимаете (Коллекция), вы получаете редактор Коллекции, а затем можете видеть каждый объект и соответствующие ему свойства:

enter image description here

Это не то, что я ищу. Как и в моем первом снимке экрана в этом посте, я пытаюсь отобразить коллекцию свойств Benchmark List как раскрывающийся список, в котором свойство имени объекта отображается как текст того, что может быть отображено ...

Спасибо

1 Ответ

32 голосов
/ 03 марта 2011

Как правило, раскрывающийся список в сетке свойств используется для установки значения свойства из данного списка. Здесь это означает, что вам лучше иметь свойство типа «Benchmark» типа IBenchmark и возможный список IBenchmark где-нибудь еще. Я позволил себе сменить свой класс Analytic следующим образом:

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. Вам необходимо украсить свойство Benchmark с помощью UITypeEditor (как указано выше) и интерфейс IBenchmark с TypeConverter следующим образом:

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