2010-01-12 9 views
5

Sto provando a usare System.Windows.Forms.PropertyGrid.Perché l'attributo Sfogliabile rende la proprietà non associabile?

Per rendere una proprietà non visibile in questa griglia, è necessario utilizzare BrowsableAttribute impostato su false. Ma l'aggiunta di questo attributo rende la proprietà non associabile.

Esempio: Creare un nuovo progetto Windows Form, e rilasciare un TextBox e PropertyGrid su Form1. Usando il codice qui sotto, la larghezza del TextBox non viene tenuto a 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; 
    } 
} 

Il codice per la classe di dati è:

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 
} 

Se si cambia l'attributo sfogliabile su true su ogni proprietà in Data funziona. Ora sembra che BindingSource cerchi l'origine dati per l'attributo Browsable.

risposta

6

risposta Aggiornato sulla base degli esempio:

ho fatto qualche scavo in Reflector e ha scoperto che il "problema" è in realtà in ListBindingHelper, che viene utilizzato da CurrencyManager, che è a sua volta utilizzato dal BindingSource (sono tutti nello spazio dei nomi System.Windows.Forms).

Per scoprire le proprietà associabili, CurrencyManager invoca ListBindingSource.GetListItemProperties. Sotto il cofano, questo chiama a GetListItemPropertiesByInstance (quando si passa in un singolo oggetto). Tale metodo ha la seguente riga di codice al fine:

return TypeDescriptor.GetProperties(target, BrowsableAttributeList); 

E l'attuazione di BrowsableAttributeList è:

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

Si può vedere che è, in effetti, filtrando proprietà da BrowsableAttribute(true). Probabilmente dovrebbe usare BindableAttribute invece, ma la mia ipotesi è che i progettisti si siano resi conto che tutti dipendevano già da BrowsableAttribute e hanno deciso di usarlo.

Quindi, purtroppo, non sarà possibile risolvere questo problema se si utilizza BrowsableAttribute. Le tue uniche opzioni sono di fare quello che Marc suggerisce e usare un custom TypeConverter, o filtrare la griglia di proprietà stessa usando una delle soluzioni in questa domanda: Programatically Hide Field in PropertyGrid.

+0

Sì, hai ragione. Sembra funzionare. Ho questo problema in un grande progetto. Proverò a scrivere un esempio migliore al più presto. – bodziec

3

BrowsableAttribute viene utilizzato da molti del modello di componente come un meccanismo per evitare che venga incluso. Forse l'opzione migliore è non aggiungere [Browsable(false)].

Ci sono molti altri modi per filtrare PropertyGrid, tra cui (in crescente complessità) TypeConverter, ICustomTypeDescriptor, TypeDescriptionProvider - ma il più semplice è probabilmente quello di raccontare PropertyGrid gli attributi che descrivono cose che fanno Desidera (.BrowsableAttributes), e segnare il altre proprietà.

Si noti che un'altra opzione è quella di creare una scheda personalizzata, ma questa è hit'n'miss nella mia esperienza.

Ecco un esempio utilizzando TypeConverter, che viene utilizzato da PropertyGrid, ma non dalla maggior parte delle altre associazioni; funziona avendo un tipo di convertitore personalizzato che esclude una proprietà specifica per nome, ma si potrebbe anche usare qualcosa come Attribute.IsDefined per fare il mascheramento:

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