我有多個屬性的類。有時可以在propertygrid中編輯屬性(A)。但有時財產A可能不會被編輯。這取決於另一個屬性的值。C#如何根據propertygrid中的另一個屬性的值公開屬性?
我該怎麼做?
編輯: 對不起,我忘了提及我想要在設計時。
我有多個屬性的類。有時可以在propertygrid中編輯屬性(A)。但有時財產A可能不會被編輯。這取決於另一個屬性的值。C#如何根據propertygrid中的另一個屬性的值公開屬性?
我該怎麼做?
編輯: 對不起,我忘了提及我想要在設計時。
運行時屬性模型是一個高級主題。對於PropertyGrid,最簡單的路線是編寫一個TypeConverter,繼承自ExpandableObjectConverter。覆蓋GetProperties,並將有問題的屬性交換爲自定義屬性。
從頭開始編寫一個PropertyDescriptor是件雜事;但在這種情況下,你只需要將所有方法鏈接到原始(反射)描述符。只需重寫IsReadOnly即可返回所需的布爾值。
絕不平凡,但可以實現。
using System;
using System.ComponentModel;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.Run(new Form { Text = "read only",
Controls = {
new PropertyGrid { Dock = DockStyle.Fill, SelectedObject = new Foo { IsBarEditable = false }}
}
});
Application.Run(new Form { Text = "read write",
Controls = {
new PropertyGrid { Dock = DockStyle.Fill, SelectedObject = new Foo { IsBarEditable = true }}
}
});
}
}
[TypeConverter(typeof(Foo.FooConverter))]
class Foo
{
[Browsable(false)]
public bool IsBarEditable { get; set; }
public string Bar { get; set; }
private class FooConverter : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
var props = base.GetProperties(context, value, attributes);
if (!((Foo)value).IsBarEditable)
{ // swap it
PropertyDescriptor[] arr = new PropertyDescriptor[props.Count];
props.CopyTo(arr, 0);
for (int i = 0; i < arr.Length; i++)
{
if (arr[i].Name == "Bar") arr[i] = new ReadOnlyPropertyDescriptor(arr[i]);
}
props = new PropertyDescriptorCollection(arr);
}
return props;
}
}
}
class ReadOnlyPropertyDescriptor : ChainedPropertyDescriptor
{
public ReadOnlyPropertyDescriptor(PropertyDescriptor tail) : base(tail) { }
public override bool IsReadOnly
{
get
{
return true;
}
}
public override void SetValue(object component, object value)
{
throw new InvalidOperationException();
}
}
abstract class ChainedPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor tail;
protected PropertyDescriptor Tail { get {return tail; } }
public ChainedPropertyDescriptor(PropertyDescriptor tail) : base(tail)
{
if (tail == null) throw new ArgumentNullException("tail");
this.tail = tail;
}
public override void AddValueChanged(object component, System.EventHandler handler)
{
tail.AddValueChanged(component, handler);
}
public override AttributeCollection Attributes
{
get
{
return tail.Attributes;
}
}
public override bool CanResetValue(object component)
{
return tail.CanResetValue(component);
}
public override string Category
{
get
{
return tail.Category;
}
}
public override Type ComponentType
{
get { return tail.ComponentType; }
}
public override TypeConverter Converter
{
get
{
return tail.Converter;
}
}
public override string Description
{
get
{
return tail.Description;
}
}
public override bool DesignTimeOnly
{
get
{
return tail.DesignTimeOnly;
}
}
public override string DisplayName
{
get
{
return tail.DisplayName;
}
}
public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter)
{
return tail.GetChildProperties(instance, filter);
}
public override object GetEditor(Type editorBaseType)
{
return tail.GetEditor(editorBaseType);
}
public override object GetValue(object component)
{
return tail.GetValue(component);
}
public override bool IsBrowsable
{
get
{
return tail.IsBrowsable;
}
}
public override bool IsLocalizable
{
get
{
return tail.IsLocalizable;
}
}
public override bool IsReadOnly
{
get { return tail.IsReadOnly; }
}
public override string Name
{
get
{
return tail.Name;
}
}
public override Type PropertyType
{
get { return tail.PropertyType; }
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
tail.RemoveValueChanged(component, handler);
}
public override void ResetValue(object component)
{
tail.ResetValue(component);
}
public override void SetValue(object component, object value)
{
tail.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return tail.ShouldSerializeValue(component);
}
public override bool SupportsChangeEvents
{
get
{
return tail.SupportsChangeEvents;
}
}
}
這個答案假設你在談論WinForms。如果你想改變一個物業的基於另一個物業的只讀狀態,你需要有你的對象implement ICustomTypeDescriptor。這不是一件簡單的事情,但它會爲您在類中顯示的類提供很大的靈活性。
不完全準確;在這裏TypeConverter會比較容易,你也可以使用TypeDescriptionProvider;所以3個選項 – 2011-01-14 16:18:05
我已經通過this stack solution提供以往類似的解決方案。它使用自定義屬性,並有條件地忽略在設計時和運行時間進行更改的嘗試,但我確定可以在SETter中通過應用您自己的「標準」來更改它。
這是否也計算設計時間? – Martijn 2011-01-14 16:25:27