Ответ 1
В общем случае выпадающий список в сетке свойств используется для задания значения свойства из заданного списка. Здесь это означает, что вам лучше иметь свойство типа "Benchmark" типа IBenchmark и возможный список 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
a 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();
}
}