第一步:自定义控件的TypeDescription描述。
为了扩展.NET的类型描述系统(Type Descriptor System),在运行时动态地更改对象的属性,使得这些属性在PropertyGrid上下文中不会被显示。
1.CLTypeDescriptionProvider:这是一个TypeDescriptionProvider的实现。TypeDescriptionProvider是.NET的一部分,用于提供有关类型的信息。在这个实现中,它将返回一个自定义的ICustomTypeDescriptor实现,即CLTypeDescriptor。
2.CLTypeDescriptor:这是一个ICustomTypeDescriptor的实现。ICustomTypeDescriptor为对象提供了一种方式来提供有关其属性的自定义信息。在这个实现中,它将返回一个包含自定义PropertyDescriptor的PropertyDescriptorCollection。
3.CLPropertyDescriptor:这是一个PropertyDescriptor的实现。PropertyDescriptor描述了一个对象的属性,包括其名称、类型、默认值等。在这个实现中,它将返回一个修改过的AttributeCollection,该AttributeCollection包含一个新的BrowsableAttribute,其值为false。这意味着这个属性在PropertyGrid上下文中不会被显示。
这是代码:
public class CLTypeDescriptionProvider : TypeDescriptionProvider
{
public CLTypeDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(object))) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type type, object o)
{
ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(type, o);
return new CLTypeDescriptor(baseDescriptor);
}
}
public class CLTypeDescriptor : CustomTypeDescriptor
{
ICustomTypeDescriptor original;
public CLTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
: base(originalDescriptor) { original = originalDescriptor; }
public override PropertyDescriptorCollection GetProperties()
{ return this.GetProperties(new Attribute[] { }); }
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Select(p => new CLPropertyDescriptor(p))
.ToArray();
return new PropertyDescriptorCollection(properties, true);
}
}
public class CLPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor o;
public CLPropertyDescriptor(PropertyDescriptor originalProperty)
: base(originalProperty) { o = originalProperty; }
public override bool CanResetValue(object component)
{ return o.CanResetValue(component); }
public override object GetValue(object component) { return o.GetValue(component); }
public override void ResetValue(object component) { o.ResetValue(component); }
public override void SetValue(object component, object value)
{ o.SetValue(component, value); }
public override bool ShouldSerializeValue(object component)
{ return o.ShouldSerializeValue(component); }
public override AttributeCollection Attributes
{
get
{
var attributes = base.Attributes.Cast<Attribute>().ToList();
var category = attributes.OfType<CategoryAttribute>().FirstOrDefault();
//if (category != null && category.Category == "Extra") attributes.Add(new BrowsableAttribute(true));
attributes.Add(new BrowsableAttribute(false));
return new AttributeCollection(attributes.ToArray());
}
}
public override Type ComponentType { get { return o.ComponentType; } }
public override bool IsReadOnly { get { return o.IsReadOnly; } }
public override Type PropertyType { get { return o.PropertyType; } }
}
第二步:自定义控件注入类特性CLTypeDescriptionProvider,字段特性Category、Browsable、DisplayName、Editor。
1.CategoryAttribute:这个特性用于将属性分组到特定的类别中。在PropertyGrid控件中,类似的属性可以被组织在一起。例如,你可以使用`[Category("Appearance")]`来指示属性应该在"Appearance"类别下显示。
2.BrowsableAttribute:这个特性用于控制属性是否应PropertyGrid控件中显示。如果你设置`[Browsable(false)]`,该属性将不会在属性浏览器中显示。
3.DisplayNameAttribute:这个特性用于控制属性在PropertyGrid控件中的显示名称。默认情况下,属性的名称就是它在代码中的名称。但是,你可以使用`[DisplayName("My Property")]`来改变它在属性浏览器中的显示名称。
4.EditorAttribute:这个特性用于指定用于编辑属性的编辑器。这个编辑器可以是一个字符串编辑器,也可以是一个复杂的用户界面。例如,你可以使用`[Editor(typeof(MyCustomEditor), typeof(UITypeEditor))]`来指定一个自定义的编辑器。在这个例子中,`MyCustomEditor`需要继承自`UITypeEditor`类,它提供了创建和管理属性编辑器的方法。
这是代码:
[TypeDescriptionProvider(typeof(CLTypeDescriptionProvider))]
public class ClLabel : UserControl, IExecutable, ICommunicationBindable
{
[Browsable(true)]
[Category("自定义项目")]
[DisplayName("VM项目")]
[Editor(typeof(WpfApp2.OpenCollectionEditor), typeof(OpenCollectionView))]
public CustomType VMItem
{
get { return (CustomType)GetValue(VMItemProperty); }
set { SetValue(VMItemProperty, value); }
}
第三步:实现自定义编辑器,该类继承自PropertyEditorBase。OpenCollectionEditor是一个自定义编辑器,用于编辑属性。
`OpenCollectionEditor`类覆盖了`PropertyEditorBase`的两个方法:`CreateElement`和`GetDependencyProperty`。
1.CreateElement方法接受一个`PropertyItem`参数,该参数表示要编辑的属性。在此方法中,你首先获取属性的值(假设它是一个`ClLabel`类型的对象),然后创建一个`OpenCollectionView`对象,并将`ClLabel`对象的`VMItem`属性设置为`OpenCollectionView`的`customType`。然后,你将`VMItem`对象序列化为JSON字符串,并设置为`OpenCollectionView`的`txt`文本。最后,你返回`OpenCollectionView`对象,这个对象将用于编辑属性。
2.GetDependencyProperty方法返回一个`DependencyProperty`对象,该对象表示`OpenCollectionView`的`CustomTypeProperty`。`DependencyProperty`是WPF中的一个重要概念,它支持属性值的继承,数据绑定等功能。
这是代码:
public class OpenCollectionEditor : PropertyEditorBase
{
public override FrameworkElement CreateElement(PropertyItem propertyItem)
{
var model = propertyItem.Value as ClLabel;
OpenCollectionView openCollectionView = new OpenCollectionView();
openCollectionView.customType = model.VMItem;
openCollectionView.txt.Text=JsonConvert.SerializeObject(model.VMItem);
return openCollectionView;
}
public override DependencyProperty GetDependencyProperty() => OpenCollectionView.CustomTypeProperty;
}
第三步:实现显示的自定义属性。
public partial class OpenCollectionView : UserControl
{
public OpenCollectionView()
{
InitializeComponent();
DataContextChanged += OpenCollectionView_DataContextChanged;
}
private void OpenCollectionView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
RelayCommand<CustomType> cmd = new RelayCommand<CustomType>(param =>
{
var editor = new CustomControlWindow();
editor.ShowDialog();
return editor.selectedItem;
});
cmd.Executed += result =>
{
txt.Text = JsonConvert.SerializeObject(result);
};
btn.Command = cmd;
}
public CustomType customType
{
get => (CustomType)GetValue(CustomTypeProperty);
set => SetValue(CustomTypeProperty, value);
}
public static readonly DependencyProperty CustomTypeProperty = DependencyProperty.Register(nameof(customType), typeof(CustomType), typeof(OpenCollectionView), new PropertyMetadata(default(CustomType)));
public ICommand ServoOMCItemEditCommand { get; set; }
}