HandyControl PropertyGrid及自定义编辑器

在这里插入图片描述

前提条件

项目引入对应HandyControl对应版本包。

使用案例

UI部分

<Window xmlns:hc="https://handyorg.github.io/handycontrol">
    <hc:TabControl>
        <hc:TabItem Header="默认样式">
            <hc:PropertyGrid Width="380" SelectedObject="{Binding DemoModel}"/>
        </hc:TabItem>
    </hc:TabControl>
</Window>

数据实体

实体类PropertyGridDemoModel.cs

public class PropertyGridDemoModel
{
    [Category("类别1")]
    [DisplayName("字符串")]
    public string String { get; set; }

    [Category("类别2")]
    [DisplayName("整型")]
    public int Integer { get; set; }

    [Category("类别3")]
    [DisplayName("布尔型")]
    public bool Boolean { get; set; }

    [Category("类别1")]
    [DisplayName("枚举型")]
    public Gender Enum { get; set; }
    [DisplayName("枚举型")]
    public HorizontalAlignment HorizontalAlignment { get; set; }
    [DisplayName("枚举型")]
    public VerticalAlignment VerticalAlignment { get; set; }
    [DisplayName("图像类型")]
    public ImageSource ImageSource { get; set; }
}

public enum Gender
{
    [Description("男性")] //可考虑自定义编辑器,3.2不支持
    Male,
    [Description("女性")] //可考虑自定义编辑器,3.2不支持
    Female
}

设置数据上下文

当前为简化案例,直接在窗口后台进行上下文设置。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DemoModel = new PropertyGridDemoModel
        {
            String = "TestString",
            Enum = Gender.Female,
            Boolean = true,
            Integer = 98,
            VerticalAlignment = VerticalAlignment.Stretch
        };
        DataContext = this;
    }

    public PropertyGridDemoModel DemoModel { get; private set; }
}

运行效果一

在这里插入图片描述

内置编辑器

HandyControl 内置了一下基础类型的编辑器,具体如下:

名称说明
DatePropertyEditor日期编辑器
DateTimePropertyEditor日期时间编辑器
EnumPropertyEditor枚举编辑器
HorizontalAlignmentPropertyEditor水平对齐方式编辑器
ImagePropertyEditor图片编辑器
NumberPropertyEditor数字编辑器
PlainTextPropertyEditor纯文本编辑器
ReadOnlyTextPropertyEditor只读文本编辑器
SwitchPropertyEditor布尔编辑器(开关风格)
TimePropertyEditor时间编辑器
VerticalAlignmentPropertyEditor垂直对齐方式编辑器

自定义编辑器

内置编辑器毕竟是有限的,不少需求需要进行编辑器自定义,自定义编辑器需要实现基类 PropertyEditorBase,假定需要实现一个带进度条的属性值,定义一个进度条编辑器,代码如下:

public class ProgressPropertyEditor : PropertyEditorBase
{
	// 重写对应的控件构建类,用于返回UI需要显示的控件实例
    public override FrameworkElement CreateElement(PropertyItem propertyItem)
    {
        var bar = new ProgressBar();
        bar.Maximum = 100;
        return bar;
    }
	// 设置对应实体属性与控件关联的依赖属性
    public override DependencyProperty GetDependencyProperty()
    {
       return System.Windows.Controls.ProgressBar.ValueProperty;
    }
}

添加属性并指定编辑器

PropertyGridDemoModel中,添加一个属性ProgressValue,并指定编辑器类型。

public class PropertyGridDemoModel
{
    //省略重复内容
    [Editor(typeof(ProgressPropertyEditor)
    , typeof(ProgressPropertyEditor))] // 必须指定编辑器!!!
	[DisplayName("自定义")]
	public int ProgressValue { get; set; }
    //省略重复内容
}

//省略重复内容

MainWindow.cs设置属性值。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        //省略重复
        DemoModel = new PropertyGridDemoModel
        {
	        //省略重复
            ProgressValue = 12,// 模拟设置属性值
	        //省略重复
        };
        //省略重复
    }
    //省略重复
}

运行效果二

在这里插入图片描述

代码解析

控件PropertyGrid逻辑代码

// 应用模板
public override void OnApplyTemplate()
{
	// 省略代码
    UpdateItems(SelectedObject);
}

private void UpdateItems(object obj)
{
    if (obj != null && _itemsControl != null)
    {
        _dataView = CollectionViewSource.GetDefaultView((from item in TypeDescriptor.GetProperties(obj.GetType()).OfType<PropertyDescriptor>(
                                                         where PropertyResolver.ResolveIsBrowsable(item)
                                                         select item).Select(CreatePropertyItem).Do(delegate (PropertyItem item)
                                                     {
                                                         item.InitElement();
                                                     }));
        SortByCategory(null, null);
        _itemsControl.ItemsSource = _dataView;
    }
}

protected virtual PropertyItem CreatePropertyItem(PropertyDescriptor propertyDescriptor)
{
    return new PropertyItem
    {
		// 省略代码
		// 获取编辑器
        Editor = PropertyResolver.ResolveEditor(propertyDescriptor),
    };
}

属性解析器PropertyResolver

public PropertyEditorBase ResolveEditor(PropertyDescriptor propertyDescriptor)
{
    EditorAttribute editorAttribute = propertyDescriptor.Attributes.OfType<EditorAttribute>().FirstOrDefault();
    if (editorAttribute != null && !string.IsNullOrEmpty(editorAttribute.EditorTypeName))
    {
	    // 获取自定义编辑器
        return CreateEditor(Type.GetType(editorAttribute.EditorTypeName));
    }
	// 创建内置编辑器实例
    return CreateDefaultEditor(propertyDescriptor.PropertyType);
}

// 创建自定义编辑器实例
public virtual PropertyEditorBase CreateEditor(Type type)
{
    return (Activator.CreateInstance(type) as PropertyEditorBase) ?? new ReadOnlyTextPropertyEditor();
}

问题思考

编辑器如何外部传入控件的多参数值,可以考虑自定义解析器。

首先,我们需要创建一个自定义类来作为 PropertyGrid 的对象。这个类可以包含多个属性以及子属性。例如: ```c# public class Person { public string Name { get; set; } public int Age { get; set; } public Address Address { get; set; } } public class Address { public string Street { get; set; } public string City { get; set; } public string State { get; set; } } ``` 接下来,我们需要创建一个自定义PropertyDescriptor 类,用于控制 PropertyGrid 的显示方式。这个类需要继承自 PropertyDescriptor 类,并重写几个方法,包括 GetValue()、SetValue()、CanResetValue()、ResetValue()、ShouldSerializeValue() 等方法。例如: ```c# public class NestedPropertyDescriptor : PropertyDescriptor { private PropertyDescriptor _parent; private PropertyDescriptor _child; public NestedPropertyDescriptor(PropertyDescriptor parent, PropertyDescriptor child) : base(child.Name, null) { _parent = parent; _child = child; } public override object GetValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return null; return _child.GetValue(parentValue); } public override void SetValue(object component, object value) { object parentValue = _parent.GetValue(component); if (parentValue == null) return; _child.SetValue(parentValue, value); OnValueChanged(component, EventArgs.Empty); } public override bool CanResetValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return false; return _child.CanResetValue(parentValue); } public override void ResetValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return; _child.ResetValue(parentValue); OnValueChanged(component, EventArgs.Empty); } public override bool ShouldSerializeValue(object component) { object parentValue = _parent.GetValue(component); if (parentValue == null) return false; return _child.ShouldSerializeValue(parentValue); } public override Type ComponentType { get { return _parent.ComponentType; } } public override bool IsReadOnly { get { return _child.IsReadOnly; } } public override Type PropertyType { get { return _child.PropertyType; } } public override string DisplayName { get { return _child.DisplayName; } } } ``` 最后,我们需要创建一个自定义的 TypeConverter 类,用于控制 PropertyGrid 的显示方式。这个类需要继承自 ExpandableObjectConverter 类,并重写几个方法,包括 GetProperties()、GetPropertiesSupported() 等方法。例如: ```c# public class NestedTypeConverter : ExpandableObjectConverter { public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { PropertyDescriptorCollection properties = base.GetProperties(context, value, attributes); List<PropertyDescriptor> nestedProperties = new List<PropertyDescriptor>(); foreach (PropertyDescriptor property in properties) { if (property.PropertyType.IsClass && property.PropertyType != typeof(string)) { PropertyDescriptorCollection nestedProps = TypeDescriptor.GetProperties(property.PropertyType, new Attribute[] { new BrowsableAttribute(true) }); foreach (PropertyDescriptor nestedProp in nestedProps) { nestedProperties.Add(new NestedPropertyDescriptor(property, nestedProp)); } } else { nestedProperties.Add(property); } } return new PropertyDescriptorCollection(nestedProperties.ToArray()); } public override bool GetPropertiesSupported(ITypeDescriptorContext context) { return true; } } ``` 最后,我们可以在窗体中使用 PropertyGrid 控件来显示我们自定义的类。例如: ```c# private void Form1_Load(object sender, EventArgs e) { PropertyGrid1.SelectedObject = new Person { Name = "John", Age = 30, Address = new Address { Street = "123 Main St", City = "Anytown", State = "CA" } }; PropertyGrid1.BrowsableAttributes = new Attribute[] { new BrowsableAttribute(true) }; PropertyGrid1.ExpandAllGridItems(); } ``` 以上就是 C# PropertyGrid 自定义多层显示的示例。希望能对你有所帮助。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值