在PropertyGrid中动态设置属性的只读属性

8

我有一个PropertyGrid,用于显示辅助类中的属性。我将辅助类分配给PropertyGrid,如下所示:

myPropertyGrid.SelectedObject = mySettingsHelper;

在助手类中,我在设计时像这样分配ReadOnlyAttribute
[DisplayName("DisplayExA"),
Description("DescriptionExA"),
ReadOnlyAttribute(true)]
public string PropertyA { get; set; }

[DisplayName("DisplayExB"),
Description("DescriptionExB"),
ReadOnlyAttribute(false)]
public string PropertyB { get; set; }

[DisplayName("DisplayExC"),
Description("DescriptionExC"),
ReadOnlyAttribute(true)]
public string PropertyC { get; set; }

但现在我需要能够在运行时动态地更改各个属性的此属性。根据某些条件,其中一些属性可能需要是只读或非只读的。我应该如何在运行时动态地进行更改?

编辑:

我尝试了以下代码,但这会为对象的每个实例设置ReadOnly属性!我想要对每个对象执行此操作。有时,一个对象可能将PropertyA设置为只读,而另一个对象可能将PropertyA设置为非只读。

public static class PropertyReadOnlyHelper
{
    public static  void SetReadOnly(object container, string name, bool value)
    {
        try
        {
            PropertyDescriptor descriptor = TypeDescriptor.GetProperties(container.GetType())[name];
            ReadOnlyAttribute attribute = (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];
            FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
                                                System.Reflection.BindingFlags.NonPublic |
                                                System.Reflection.BindingFlags.Instance);
            fieldToChange.SetValue(attribute, value);
        }
        catch { }
    }
}

你的应用程序中使用了多少个 PropertyGrid?我认为如果一次只使用一个 PropertyGrid,你的目的就可以达到。我们仍然需要更改类型的 Attribute,但在选择对象之前,我们将根据需要切换 ReadOnly,这应该就能解决问题了。 - King King
7个回答

2
我能够使用这篇CodeProject文章中介绍的库,实现我所需的准确操作(只读属性的对象级别赋值)。使用该库后,我仍然可以使用.NET PropertyGrid,并使用自定义属性来处理动态设置。

1
CodeProject文章的链接已失效。 - Crackerjack

0

请查看此页面:
https://www.codeproject.com/Articles/152945/Enabling-disabling-properties-at-runtime-in-the-Pr

引用自上述帖子:

重要的是要静态定义类的每个属性的ReadOnly属性为您想要的任何值。如果不这样做,以这种方式在运行时更改属性将错误地修改类的每个属性的属性。

使用反射来在运行时修改目标属性的“ReadOnly”属性以实现您的目标。设置一个属性应用于所有属性的问题是因为您需要在同一PropertyGrid对象中显式设置具有ReadOnly属性的所有属性,以避免该问题。

[RefreshProperties(System.ComponentModel.RefreshProperties.All)]
[ReadOnly(false)]
public string Country
{
  get { return mCountry; }
  set
      {
      mCountry = value;
      PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())["State"];
      ReadOnlyAttribute attribute = (ReadOnlyAttribute)
                                    descriptor.Attributes[typeof(ReadOnlyAttribute)];
      FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly", 
                                       System.Reflection.BindingFlags.NonPublic | 
                                       System.Reflection.BindingFlags.Instance);
      fieldToChange.SetValue(attribute, mCountry != "U.S.");
      }
}

[ReadOnly(true)]
public string State
{
   get { return mState; }
   set { mState = value; }
}

0
非常感谢。根据答案,我编写了以下代码,它可以正常工作:
private void SetReadonly ( object o, bool value )
{
  foreach ( PropertyInfo property in o.GetType().GetProperties() )
    if ( property.GetCustomAttribute<ReadOnlyAttribute>() != null )
    {
      Attribute readOnly = TypeDescriptor.GetProperties( o.GetType() )[property.Name].Attributes[typeof( ReadOnlyAttribute )];
      readOnly.GetType().GetField( nameof( ReadOnlyAttribute.IsReadOnly ), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase ).SetValue( readOnly, value );
    }
}

对我来说,在ASP.NET Core下,<IsReadOnly>k__BackingField是名称,而不是nameof(ReadOnlyAttribute.IsReadOnly) - Tyler Montney

0

使用反射获取ReadOnlyAttribute类的实例引用,然后切换该实例上的IsReadOnly属性。最后,如果需要重新选择PropertyGrid中的项目,则通过将其SelectedObjects设置为null,然后重置它来完成。您也可以尝试使用PropertyGrid RefreshTabs方法来完成此操作,但我不确定。

编辑:

不幸的是,IsReadOnly属性本身是只读的...在这种情况下,我们必须使用反射来更改IsReadOnly属性的后备字段的值。


0

添加只读

TextBoxID.Attributes.Add("readonly","true");

移除只读属性

TextBoxID.Attributes.Remove("readonly");

0
Please try the code below.


  
[CategoryAttribute("2. LINE"), DisplayNameAttribute("Spline Line Tension"),
 DescriptionAttribute("Chart's Spline Line Tension "), ReadOnlyAttribute(false)]
public float _PG_SplineTension
{
    get
    {
        bool lbReadyOnly = true;
        SetPropertyReadOnly("_PG_SplineTension", lbReadyOnly);
        return this.cfSplineTension;
   }
    set { this.cfSplineTension = value; }
}



private void SetPropertyReadOnly(string lsProperty, bool lbIsReadOnly)
{
    PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())[lsProperty];
    ReadOnlyAttribute attribute = (ReadOnlyAttribute)

    descriptor.Attributes[typeof(ReadOnlyAttribute)];
    FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
        System.Reflection.BindingFlags.NonPublic |
        System.Reflection.BindingFlags.Instance);
    fieldToChange.SetValue(attribute, lbIsReadOnly);
}

0
动态设置属性网格中属性的browsablereadonly属性通常需要同时进行,而且它们是相似的工作。
经过一些调整,Reza Aghaei的出色答案关于“在运行时隐藏属性网格中的某些属性”也适用于操作readonly属性。
public class CustomObjectWrapper : CustomTypeDescriptor
{
    public object WrappedObject { get; private set; }
    public List<string> BrowsableProperties { get; private set; }
    public List<string> ReadonlyProperties { get; private set; }

    public CustomObjectWrapper(object o)
        : base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o))
    {
        WrappedObject = o;
        BrowsableProperties = new List<string>() { "Text", "BackColor" };
        ReadonlyProperties = new List<string>() { "Font" };
    }
    public override PropertyDescriptorCollection GetProperties()
    {
        return this.GetProperties(new Attribute[] { });
    }
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        List<PropertyDescriptor> result = new List<PropertyDescriptor>();

        IEnumerable<PropertyDescriptor> properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
            .Where(p => BrowsableProperties.Contains(p.Name));//unbrowsable filtering

        foreach (var p in properties)
        {
            PropertyDescriptor resultPropertyDescriptor = null;

            //handle being readonly 
            if (ReadonlyProperties.Contains(p.Name))
            {
                List<Attribute> atts = p.Attributes.Cast<Attribute>().ToList();
                atts.RemoveAll(a => a.GetType().Equals(typeof(ReadOnlyAttribute)));//remove any readonly attribute
                atts.Add(new ReadOnlyAttribute(true));//add "readonly=true" attribute

                resultPropertyDescriptor = TypeDescriptor.CreateProperty(WrappedObject.GetType(), p, atts.ToArray());
            }
            else
            {
                resultPropertyDescriptor = TypeDescriptor.CreateProperty(WrappedObject.GetType(), p, p.Attributes.Cast<Attribute>().ToArray());
            }

            if (resultPropertyDescriptor != null)
                result.Add(resultPropertyDescriptor);

        }

        return new PropertyDescriptorCollection(result.ToArray());
    }
}

以及使用方法:

propertyGrid1.SelectedObject = new CustomObjectWrapper(myobject);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接