为什么Browsable属性会使属性不可绑定?

5
我正在尝试使用 System.Windows.Forms.PropertyGrid。 为了在此网格中使属性不可见,应使用设置为 falseBrowsableAttribute。 但是添加此属性会使属性无法绑定。 示例:创建一个新的Windows Forms项目,在Form1上放置一个TextBoxPropertyGrid。使用下面的代码,TextBox的宽度不会绑定到 Data.Width:
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        Data data = new Data();
        data.Text = "qwe";
        data.Width = 500;

        BindingSource bindingSource = new BindingSource();
        bindingSource.Add(data);

        textBox1.DataBindings.Add("Text", bindingSource, "Text", true,
            DataSourceUpdateMode.OnPropertyChanged);
        textBox1.DataBindings.Add("Width", bindingSource, "Width", true,
            DataSourceUpdateMode.OnPropertyChanged);

        propertyGrid1.SelectedObject = data;
    }
}

数据类的代码如下:
public class Data : IBindableComponent
{
    public event EventHandler TextChanged;
    private string _Text;
    [Browsable(true)]
    public string Text
    {
        get
        {
            return _Text;
        }
        set
        {
            _Text = value;
            if (TextChanged != null)
                TextChanged(this, EventArgs.Empty);
        }
    }

    public event EventHandler WidthChanged;
    private int _Width;
    [Browsable(false)]
    public int Width
    {
        get
        {
            return _Width;
        }
        set
        {
            _Width = value;
            if (WidthChanged != null)
                WidthChanged(this, EventArgs.Empty);
        }
    }

    #region IBindableComponent Members

    private BindingContext _BindingContext;
    public BindingContext BindingContext
    {
        get
        {
            if (_BindingContext == null)
                _BindingContext = new BindingContext();

            return _BindingContext;
        }
        set
        {
            _BindingContext = value;
        }
    }

    private ControlBindingsCollection _DataBindings;
    public ControlBindingsCollection DataBindings
    {
        get 
        {
            if (_DataBindings == null)
                _DataBindings = new ControlBindingsCollection(this);

            return _DataBindings;    
        }
    }

    #endregion

    #region IComponent Members

    public event EventHandler Disposed;

    public System.ComponentModel.ISite Site
    {
        get
        {
            return null;
        }
        set
        {

        }
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        throw new NotImplementedException();
    }

    #endregion
}

如果您将Data中的每个属性的Browsable属性切换为true,则可以正常工作。现在,BindingSource似乎通过Browsable属性搜索数据源。
2个回答

6
根据更新的示例更新答案:

我在 Reflector 中进行了一些调查,发现 "问题" 实际上出现在 ListBindingHelper 中,它被 CurrencyManager 使用,而 BindingSource 又使用了 CurrencyManager(这些都在 System.Windows.Forms 命名空间中)。

为了发现可绑定属性,CurrencyManager 调用 ListBindingSource.GetListItemProperties。在幕后,这会调用 GetListItemPropertiesByInstance(当您传入单个对象时)。该方法在结尾处有以下代码行:

return TypeDescriptor.GetProperties(target, BrowsableAttributeList);

BrowsableAttributeList的实现如下:

private static Attribute[] BrowsableAttributeList
{
    get
    {
        if (browsableAttribute == null)
        {
            browsableAttribute = new Attribute[] { new BrowsableAttribute(true) };
        }
        return browsableAttribute;
    }
}

您可以看到,实际上是通过 BrowsableAttribute(true) 进行属性过滤。但它应该使用 BindableAttribute,但我猜设计师们意识到每个人都已经依赖于 BrowsableAttribute,因此决定改用它。
因此,如果您使用 BrowsableAttribute,则不幸的是无法避免此问题。您的选择只有执行 Marc 建议的使用自定义 TypeConverter,或使用本问题中的解决方案之一来过滤属性网格:Programatically Hide Field in PropertyGrid

是的,你说得对。它似乎有效。我在一个大项目中遇到了这个问题。我会尽快尝试写一个更好的例子。 - bodziec

3

BrowsableAttribute被许多组件模型用作一种机制来避免其被包含。也许最好的选择是不添加[Browsable(false)]

还有其他几种过滤PropertyGrid的方法,包括(按复杂性递增)TypeConverterICustomTypeDescriptorTypeDescriptionProvider,但最简单的可能是告诉PropertyGrid描述你想要的属性的属性,然后标记其他属性。

请注意,另一种选择是创建自定义选项卡,但根据我的经验,这是靠运气的。

以下是使用TypeConverter的示例,它被PropertyGrid使用,但不被大多数其他绑定使用;它通过具有排除特定名称属性的自定义类型转换器来工作,但您也可以使用类似于Attribute.IsDefined的东西来进行屏蔽:

using System.Windows.Forms;
using System;
using System.Linq;
using System.ComponentModel;
static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Data data = new Data { Name = "the name", Value = "the value" };
        using (Form form = new Form
        {
            Controls =
            {
                new PropertyGrid {
                    Dock = DockStyle.Fill,
                    SelectedObject = data
                },
                new TextBox {
                    Dock = DockStyle.Bottom,
                    DataBindings = { {"Text", data, "Value"}, }
                },
                new TextBox {
                    Dock = DockStyle.Bottom,
                    DataBindings = { {"Text", data, "Name"}, }
                }
            }
        })
        {
            Application.Run(form);
        }        
    }
}
[TypeConverter(typeof(DataConverter))]
class Data
{
    class DataConverter : ExpandableObjectConverter
    {
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            var props = base.GetProperties(context, value, attributes);
            return new PropertyDescriptorCollection(
                (from PropertyDescriptor prop in props
                 where prop.Name != "Value"
                 select prop).ToArray(), true);
        }
    }
    public string Value { get; set; }
    public string Name { get; set; }
}

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