【问题标题】:.Net how to set IsReadOnly for a property for a usercontrol.Net 如何为用户控件的属性设置 IsReadOnly
【发布时间】:2018-01-20 19:08:19
【问题描述】:

我在 .NET 中有一个带有 2 个新属性的用户控件

Prop1: Boolean
Prop2: String

当用户将 Prop1 设置为 false 时,我想在属性网格中设置 Prop2 READONLY

【问题讨论】:

  • 只定义 getter。
  • 当 prop1 为真时,我需要 getter 和 setter。
  • 你已经混淆了这一点,但你真正需要的是一个包含这两个成员的类为什么你不能基于Prop1推断关于Prop2是否有字符串长度?
  • 您可以使用自定义类型描述符将PropertyGrid 中的属性设为灰色。
  • @Reza Aghaei 好建议。 Sameh,看到这个Customizing the PropertyGrid Control - MSDN

标签: c# .net vb.net winforms user-controls


【解决方案1】:

如果您想根据某些条件在运行时将属性外观设为只读(灰色),您需要为您的类分配一个CustomTypeDescriptor,它为属性网格提供有关您的类的元数据。

PropertyGrid 控件使用对象的类型描述符来提取有关其要显示的属性的信息。类型描述符返回 PropertyDescriptor 对象列表作为属性列表。每个PropertyDescriptor 都包含一些方法和属性,用于返回显示名称、描述和其他有关属性的信息。 PropertyDescriptorIsReadOnly 属性负责通知PropertyGrid 该属性是否为只读。

示例

在以下示例中,我创建了一个包含两个属性的类。 EditableStringProperty。如果Editabletrue,那么StringProperty 是可编辑的,否则它将是只读的并且将在PropertyGrid 中显示为灰色。

MyPropertyDescriptor

它负责为属性提供元数据。在实现这个类时,对于大多数属性,我们将使用使用原始属性实现的普通实现,但对于IsReadOnly,我们将根据所有者对象的Editable属性的值来决定:

using System;
using System.ComponentModel;
using System.Linq;
public class MyPropertyDescriptor : PropertyDescriptor
{
    PropertyDescriptor p;
    SampleClass o;
    public MyPropertyDescriptor(PropertyDescriptor originalProperty, SampleClass owenr)
        : base(originalProperty) { p = originalProperty; o = owenr; }
    public override bool CanResetValue(object component)
    { return p.CanResetValue(component); }
    public override object GetValue(object component) { return p.GetValue(component); }
    public override void ResetValue(object component) { p.ResetValue(component); }
    public override void SetValue(object component, object value)
    { p.SetValue(component, value); }
    public override bool ShouldSerializeValue(object component)
    { return p.ShouldSerializeValue(component); }
    public override AttributeCollection Attributes { get { return p.Attributes; } }
    public override Type ComponentType { get { return p.ComponentType; } }
    public override bool IsReadOnly { get { return !o.Editable; } }
    public override Type PropertyType { get { return p.PropertyType; } }
}

MyTypeDescriptor

它负责提供对象的属性列表。对于StringProperty,我们将在运行时更改其行为,我们将返回MyPropertyDescriptor

using System;
using System.ComponentModel;
using System.Linq;
public class MyTypeDescriptor : CustomTypeDescriptor
{
    ICustomTypeDescriptor d;
    SampleClass o;
    public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor, SampleClass owner)
        : base(originalDescriptor) { d = originalDescriptor; o = owner; }
    public override PropertyDescriptorCollection GetProperties()
    { return this.GetProperties(new Attribute[] { }); }
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
            .Select(p => p.Name == "StringProperty" ? new MyPropertyDescriptor(p, o) : p)
            .ToArray();
        return new PropertyDescriptorCollection(properties);
    }
}

MyTypeDescriptionProvider

当有人(如属性网格)请求类型描述时,它负责为您的对象返回类型描述符:

using System;
using System.ComponentModel;
public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
    public MyTypeDescriptionProvider()
        : base(TypeDescriptor.GetProvider(typeof(object))) { }

    public override ICustomTypeDescriptor GetTypeDescriptor(Type type, object o)
    {
        ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(type, o);
        return new MyTypeDescriptor(baseDescriptor, (SampleClass)o);
    }
}

SampleClass

最后是类的实现:

using System;
using System.ComponentModel;
[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class SampleClass
{
    [RefreshProperties(RefreshProperties.All)]
    public bool Editable { get; set; }
    string sp;
    public string StringProperty
    {
        get { return sp; }
        set
        {
            if (Editable)
                sp = value;
        }
    }
}

结果

进一步阅读

您可以在以下帖子中了解其他一些解决方案:

【讨论】:

  • 解决方案 2 在设计时不起作用(在 VS 属性网格中)。解决方案 3 真的很危险(可以修改 ReadOnlyAttribute 的静态只读 YesNo 字段的内部状态) - 我强烈建议将其从帖子中删除(以及整个“扩展”SetReadOnlyAttribute 方法)
  • 感谢@IvanStoev 的反馈 是的,解决方案2 只是为PropertyGrid 控件编写的,不支持Windows 窗体设计器。事实上解决方案 2 就像在运行时处理控件的事件并做一些事情,它是 UI 代码。关于扩展方法,只有当有人将ReadOnlyAttribute.Yes/No/Default 字段作为Attributes 集合的一部分返回时,才会发生风险。我了解风险,我会尽力处理。
  • 我的第一次尝试是从属性描述符的 Attributes 集合中删除现有的 ReadOnly 属性并添加一个新属性。 (它没有任何风险)但令人惊讶的是,财产消失了。然后我决定更新值。如果出于某种原因我无法解决删除/添加 ReadOnly 属性的问题,我可能会更改 Post 而不是使用 PropertyDescriptor,而是使用 ReadOnly 属性装饰属性并使用反射更改它(这不会'没有任何风险。)
  • @IvanStoev 更新了SetReadOnlyAttribute 扩展方法以从AttributesArray 中删除现有的ReadOnly 属性并添加一个新的ReadOnly 属性。因此,静态值不再存在风险。感谢您分享关注!
【解决方案2】:

非常感谢@reza-aghaei, 根据您的详细回答和@Ivan cmets,以下 sn-p 解决了我的问题:

Dim captionFld = TypeDescriptor.GetProperties(Me)("Caption")
Dim roaCaption = captionFld.Attributes.OfType(Of ReadOnlyAttribute)().FirstOrDefault
roaCaption.GetType().GetField("isReadOnly", BindingFlags.NonPublic Or BindingFlags.Instance).SetValue(roaCaption, True)

或 C#

var captionFld = TypeDescriptor.GetProperties(this)["Caption"];
var roaCaption = captionFld.Attributes.OfType(Of, ReadOnlyAttribute)[].FirstOrDefault;
roaCaption.GetType().GetField("isReadOnly", (BindingFlags.NonPublic | BindingFlags.Instance)).SetValue(roaCaption, true);

您可以更改 .SetValue(roaCaption, true|false);

非常感谢。

【讨论】:

  • 注意:此代码应添加到 UserControl_Load 事件中。
【解决方案3】:

请确保您在名为“可编辑”的属性上具有以下属性

[RefreshProperties(System.ComponentModel.RefreshProperties.All)]

添加以下方法,并从“Editable”属性的setter中调用它,并将属性名称传递给设置为只读。

    public void EnableDisableProperty(string PropertyName,bool IsReadOnly)
    {
        PropertyDescriptor _propDescriptor = TypeDescriptor.GetProperties(this.GetType())[PropertyName];
        ReadOnlyAttribute _readOnlyAttribute = (ReadOnlyAttribute)
                                      _propDescriptor.Attributes[typeof(ReadOnlyAttribute)];

        FieldInfo _fieldToChange = _readOnlyAttribute.GetType().GetField("isReadOnly",
                                         System.Reflection.BindingFlags.NonPublic |
                                         System.Reflection.BindingFlags.Instance);
        _fieldToChange.SetValue(_readOnlyAttribute, IsReadOnly);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多