2010-01-12 8 views
5

J'essaie d'utiliser System.Windows.Forms.PropertyGrid.Pourquoi l'attribut Browsable rend la propriété non collable?

Pour qu'une propriété ne soit pas visible dans cette grille, il faut utiliser BrowsableAttribute défini sur false. Mais l'ajout de cet attribut rend la propriété non collable.

Exemple: Créer un nouveau projet Windows Forms, et déposer une TextBox et PropertyGrid sur Form1. En utilisant le code ci-dessous, la largeur de la TextBox ne soit pas lié à 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; 
    } 
} 

Le code de la classe de données est:

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 
} 

Si vous changez l'attribut Browsable true sur chaque propriété dans Data cela fonctionne. Maintenant, il semble que BindingSource recherche une source de données par l'attribut Browsable.

Répondre

6

réponse Mise à jour basée sur exemple de mise à jour:

Je l'ai fait quelques recherches dans le réflecteur et découvert que le « problème » est en fait dans ListBindingHelper, qui est utilisé par CurrencyManager, qui est à son tour utilisé par le BindingSource (tous sont dans l'espace de noms System.Windows.Forms).

Pour découvrir les propriétés pouvant être liées, CurrencyManager appelle ListBindingSource.GetListItemProperties. Sous le capot, cela appelle GetListItemPropertiesByInstance (lorsque vous passez dans un seul objet). Cette méthode a la ligne de code suivante à la fin:

return TypeDescriptor.GetProperties(target, BrowsableAttributeList); 

Et la mise en œuvre de BrowsableAttributeList est:

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

Vous pouvez voir qu'il est, en fait, les propriétés du filtrage par BrowsableAttribute(true). Il devrait probablement utiliser BindableAttribute à la place, mais je suppose que les concepteurs se sont rendu compte que tout le monde dépendait déjà de BrowsableAttribute et a décidé d'utiliser celui-là à la place. Vous ne pourrez malheureusement pas contourner ce problème si vous utilisez BrowsableAttribute. Vos seules options sont soit de faire ce que Marc suggère et d'utiliser un TypeConverter personnalisé, soit de filtrer la grille de propriétés elle-même en utilisant l'une des solutions de cette question: Programatically Hide Field in PropertyGrid.

+0

Oui, vous avez raison. Cela semble fonctionner. J'ai ce problème dans un grand projet. Je vais essayer d'écrire un meilleur exemple bientôt. – bodziec

3

BrowsableAttribute est utilisé par un grand nombre de composants comme un mécanisme pour éviter d'être inclus. Peut-être la meilleure option est de ne pas ajouter [Browsable(false)].

Il y a plusieurs autres façons de filtrer PropertyGrid, y compris (en complexité croissante) TypeConverter, ICustomTypeDescriptor, TypeDescriptionProvider - mais le plus simple est probablement de dire PropertyGrid les attributs qui décrivent des choses que vous faites besoin (.BrowsableAttributes), et marquer la autres propriétés. Notez qu'une autre option consiste à créer un onglet personnalisé - mais cela est hit'n'miss dans mon expérience.

Voici un exemple utilisant TypeConverter, qui est utilisé par PropertyGrid, mais pas par la plupart des autres liaisons; il fonctionne en ayant un type convertisseur de mesure qui exclut une propriété spécifique par son nom, mais vous pouvez aussi utiliser quelque chose comme Attribute.IsDefined pour faire le masquage:

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; } 
}