C#如何根据propertygrid中另一个属性的值公开属性?

时间:2011-01-14 14:59:17

标签: c# winforms propertygrid

我有多个属性的类。有时可以在propertygrid中编辑属性(A)。但有时属性A可能无法编辑。这取决于另一个属性的值。

我该怎么做?

编辑: 对不起,我忘了在设计时提到我想要这个。

3 个答案:

答案 0 :(得分:2)

运行时属性模型是一个高级主题。对于PropertyGrid,最简单的方法是编写一个TypeConverter,继承自ExpandableObjectConverter。覆盖GetProperties,并将有问题的属性交换为自定义属性。

从头开始编写PropertyDescriptor是件苦差事;但在这种情况下,主要只需要将所有方法链接(“装饰器”)到原始(反射)描述符。并且只需覆盖IsReadOnly即可返回所需的bool。

绝不是微不足道的,但可以实现。


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;
        }
    }
}

答案 1 :(得分:1)

这个答案假设你在谈论WinForms。如果您想根据另一个属性更改一个属性的只读状态,则需要拥有对象implement ICustomTypeDescriptor。这不是一件简单的事情,但它会为您提供有关如何在属性网格中显示类的灵活性。

答案 2 :(得分:0)

我过去通过this stack solution提供了类似的解决方案。它使用自定义属性,有条件地忽略了在设计时与运行时更改的尝试,但我确信可以通过应用您自己的“条件”来更改SETter中的更改...