Как использовать PropertyGrid, чтобы разрешить редактирование свойств без установщика? - PullRequest
1 голос
/ 04 марта 2020

По умолчанию PropertyGrid разрешает редактировать свойства только с помощью установщика publi c. Я хотел бы разрешить редактирование свойств без установщика.

Например:

class A {

   public int X {get;set}

   public int Y {get;}
}

В примере выше, только X будет редактироваться. Y будет отображаться, но будет серым. Как я могу сделать Y редактируемым?

Примечание: создание приватного поля поддержки будет в порядке. Например:

class A {

   public int X {get;set}

   private int y;
   public int Y {get => y; }
}

Ответы [ 2 ]

2 голосов
/ 04 марта 2020

Вы можете создать класс-оболочку / прокси на основе интерфейса ICustomTypeDescriptor , который позволяет настраивать свойства во время выполнения.

Вот как вы можете его использовать:

var a = new A();

// build a proxy
var proxy = new Proxy(a);

// tweak any properties
proxy.Properties["Y"].IsReadOnly = false;
// you can also tweak attributes
proxy.Properties["Y"].Attributes.Add(new CategoryAttribute("R/O -> R/W"));
proxy.Properties["Y"].Attributes.Add(new DescriptionAttribute("This works"));

// handle property change
propertyGrid1.PropertyValueChanged += (s, e) =>
{
    if (e.ChangedItem.PropertyDescriptor.Name == "Y")
    {
        a.Y = (int)e.ChangedItem.Value;
    }
};

// select the proxy instead of the original instance
propertyGrid1.SelectedObject = proxy;

А вот и результат

enter image description here

...
class A
{
    public int X { get; set; }
    public int Y { get; internal set; }
}

...


public class Proxy : ICustomTypeDescriptor
{
    public Proxy(object instance)
    {
        if (instance == null)
            throw new ArgumentNullException(nameof(instance));

        Instance = instance;
        Properties = TypeDescriptor.GetProperties(instance).OfType<PropertyDescriptor>().Select(d => new ProxyProperty(instance, d)).ToDictionary(p => p.Name);
    }

    public object Instance { get; }
    public IDictionary<string, ProxyProperty> Properties { get; }

    public AttributeCollection GetAttributes() => TypeDescriptor.GetAttributes(Instance);
    public string GetClassName() => TypeDescriptor.GetClassName(Instance);
    public string GetComponentName() => TypeDescriptor.GetComponentName(Instance);
    public TypeConverter GetConverter() => TypeDescriptor.GetConverter(Instance);
    public EventDescriptor GetDefaultEvent() => TypeDescriptor.GetDefaultEvent(Instance);
    public object GetEditor(Type editorBaseType) => TypeDescriptor.GetEditor(Instance, editorBaseType);
    public EventDescriptorCollection GetEvents() => TypeDescriptor.GetEvents(Instance);
    public EventDescriptorCollection GetEvents(Attribute[] attributes) => TypeDescriptor.GetEvents(Instance, attributes);
    public PropertyDescriptor GetDefaultProperty() => TypeDescriptor.GetDefaultProperty(Instance);
    public PropertyDescriptorCollection GetProperties() => new PropertyDescriptorCollection(Properties.Values.Select(p => new Desc(this, p)).ToArray());
    public PropertyDescriptorCollection GetProperties(Attribute[] attributes) => GetProperties();
    public object GetPropertyOwner(PropertyDescriptor pd) => Instance;

    private class Desc : PropertyDescriptor
    {
        public Desc(Proxy proxy, ProxyProperty property)
            : base(property.Name, property.Attributes.ToArray())
        {
            Proxy = proxy;
            Property = property;
        }

        public Proxy Proxy { get; }
        public ProxyProperty Property { get; }

        public override Type ComponentType => Proxy.GetType();
        public override Type PropertyType => Property.PropertyType ?? typeof(object);
        public override bool IsReadOnly => Property.IsReadOnly;
        public override bool CanResetValue(object component) => Property.HasDefaultValue;
        public override object GetValue(object component) => Property.Value;
        public override void ResetValue(object component) { if (Property.HasDefaultValue) Property.Value = Property.DefaultValue; }
        public override void SetValue(object component, object value) => Property.Value = value;
        public override bool ShouldSerializeValue(object component) => Property.ShouldSerializeValue;
    }
}

public class ProxyProperty
{
    public ProxyProperty(string name, object value)
    {
        if (name == null)
            throw new ArgumentNullException(nameof(value));

        Name = name;
        Value = value;
        Attributes = new List<Attribute>();
    }

    public ProxyProperty(object instance, PropertyDescriptor descriptor)
    {
        if (descriptor == null)
            throw new ArgumentNullException(nameof(descriptor));

        Name = descriptor.Name;
        Value = descriptor.GetValue(instance);
        var def = descriptor.Attributes.OfType<DefaultValueAttribute>().FirstOrDefault();
        if (def != null)
        {
            HasDefaultValue = true;
            DefaultValue = def.Value;
        }

        IsReadOnly = (descriptor.Attributes.OfType<ReadOnlyAttribute>().FirstOrDefault()?.IsReadOnly).GetValueOrDefault();
        ShouldSerializeValue = descriptor.ShouldSerializeValue(instance);
        Attributes = descriptor.Attributes.Cast<Attribute>().ToList();
        PropertyType = descriptor.PropertyType;
    }

    public string Name { get; }
    public object Value { get; set; }
    public object DefaultValue { get; set; }
    public bool HasDefaultValue { get; set; }
    public bool IsReadOnly { get; set; }
    public bool ShouldSerializeValue { get; set; }
    public Type PropertyType { get; set; }
    public IList<Attribute> Attributes { get; }
}
1 голос
/ 04 марта 2020

Там есть способ сделать это, но это абсурдно сложный;

  • вы создаете пользовательский TypeDescriptionProvider и связываете его с введите или введите ICustomTypeDescriptor в типе, и
  • создайте пользовательский PropertyDescriptor, который знает, как редактировать поле в GetValue и SetValue
  • , которые вы создаете custom TypeDescriptor, который повторяет стандартные дескрипторы свойств для большинства вещей, и ваш новый дескриптор свойств в этом случае

На полном серьезе; пожалуйста, не делай этого! Просто сделайте собственность доступной в первую очередь. Если это не настраиваемое свойство, вы не должны пытаться установить его .


, основываясь на комментариях, это звучит как то, что вы на самом деле нужна «неизменность эскимо»; рассмотрим:

class Foo {
    private bool _frozen;
    public void Freeze() => _frozen = true;

    protected void ThrowIfFrozen() {
        if (_frozen) throw new InvalidOperationException(
           "The object cannot be changed once Freeze has been called");
    }

    private int _x, _y;
    public int X {
        get => _x;
        set {
            if (value != _x) {
                ThrowIfFrozen();
                _x = value;
            }
        }
    }
    public int Y {
        get => _y;
        set {
            if (value != _y) {
                ThrowIfFrozen();
                _y = value;
            }
        }
    }
}
...