Я пытаюсь использовать System.Windows.Forms.PropertyGrid
.
Для создания свойства не видимым в этой сетке, нужно использовать BrowsableAttribute
набор к false
. Но добавление этого атрибута делает свойство не связываемым.
Пример: Создайте новый проект Windows Forms и отбросьте a TextBox
и PropertyGrid
на Form1
. С помощью кода ниже, ширина 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
}
При переключении Доступного для просмотра атрибута на истинный на каждом свойстве в Данных, это работает. Теперь это походит на источник данных поисков BindingSource Доступным для просмотра Атрибутом.
BrowsableAttribute
используется многими компонентами как механизм, чтобы избежать его включения. Возможно, лучший вариант - не добавлять [Browsable(false)]
.
Существует несколько других способов фильтрации PropertyGrid
, в том числе (все сложнее) TypeConverter
, ICustomTypeDescriptor
, TypeDescriptionProvider
- но самое простое, наверное, сказать PropertyGrid
атрибуты, которые описывают вещи, которые вы хотите (). BrowsableAttributes
), и пометить другие свойства.
Обратите внимание, что другой вариант - это создать пользовательскую закладку - но это, по моему опыту, не так уж и плохо.
Вот пример использования 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; }
}
Обновленный ответ на основе примера обновления:
Я покопался в Reflector и обнаружил, что «проблема» на самом деле в ListBindingHelper
, который используется CurrencyManager
, который, в свою очередь, используется BindingSource
(все они находятся в пространстве имен 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
. Единственный вариант - либо сделать то, что предлагает Марк, и использовать настраиваемый TypeConverter
, либо отфильтровать саму сетку свойств, используя одно из решений в этом вопросе: Программно скрыть поле в PropertyGrid .