Why Browsable attribute makes property not bindable?

荒凉一梦 提交于 2019-11-30 14:43:44
Aaronaught

Updated answer based on update example:

I've done some digging in Reflector and discovered that the "problem" is actually in ListBindingHelper, which is used by CurrencyManager, which is in turn used by the BindingSource (these are all in the System.Windows.Forms namespace).

To discover the bindable properties, the CurrencyManager invokes ListBindingSource.GetListItemProperties. Under the hood, this calls out to GetListItemPropertiesByInstance (when you pass in a single object). That method has the following line of code at the end:

return TypeDescriptor.GetProperties(target, BrowsableAttributeList);

And the implementation of BrowsableAttributeList is:

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

You can see that it is, in fact, filtering properties by BrowsableAttribute(true). It should probably be using BindableAttribute instead, but my guess is that the designers realized that everybody was already depending on BrowsableAttribute and decided to use that one instead.

So unfortunately you won't be able to get around this issue if you use BrowsableAttribute. Your only options are to either do what Marc suggests and use a custom TypeConverter, or filter the property grid itself using one of the solutions in this question: Programatically Hide Field in PropertyGrid.

BrowsableAttribute is used by a lot of the component-model way as a mechanism to avoid it being included. Perhaps the best option is to not add [Browsable(false)].

There are several other ways of filtering PropertyGrid, including (in increasing complexity) TypeConverter, ICustomTypeDescriptor, TypeDescriptionProvider - but the simplest is probably to tell PropertyGrid the attributes that describe things you do want (.BrowsableAttributes), and mark the other properties.

Note that another option is to create a custom tab - but that is hit'n'miss in my experience.

Here's an example using TypeConverter, which is used by PropertyGrid, but not by most other bindings; it works by having a custom type-converter which excludes a specific property by name, but you could also use something like Attribute.IsDefined to do the masking:

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; }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!