WPF -- Xceed PropertyGrid应用详解

目录

1. Nuget安装

2. 调用

3. 常用属性

4. Model层设置

4.1 测试类

4.2  为对象添加相关特性

     4.2.1 添加描述特性

     4.2.2 添加分组

     4.2.3 添加属性是否显示标识

     4.2.4 添加类属性可展开特性

     4.2.5 添加类属性显示顺序

     4.2.6 对分组[Category]再进行排序

4.3 自定义List集合的显示

5. 代码动态获取/设置相关特性的值   

5.1  设置Browsable特性的值

5.2  设置Category特性的值

5.3  获取类的属性成员的Category特性的值

5.4  设置类的CategoryOrderAttribute特性的值

5.5  嵌套属性赋值

 6. 自定义Category样式

7. 自定义属性编辑模块

8. 最终效果图


1. Nuget安装

2. 调用

//xaml里 添加命名空间
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"



<Grid>
     <xctk:PropertyGrid  SelectedObject="{Binding PropeObject}" Name="PropertyGrid" 
SelectedPropertyItemChanged="PropertyGrid_SelectedPropertyItemChanged" Margin="10" ShowAdvancedOptions="True" ShowDescriptionByTooltip="True"  NameColumnWidth="270" 
FontSize="14"  ShowTitle="False" ShowSortOptions="False" ShowSearchBox="False" CategoryGroupHeaderTemplate="{StaticResource Category}">
    </xctk:PropertyGrid>
</Grid>

 SelectedPropertyItemChanged事件

private string _propname;

        public string PropName
        {
            get { return _propname; }
            set { _propname = value; }
        }

        private void PropertyGrid_SelectedPropertyItemChanged(object sender, RoutedPropertyChangedEventArgs<Xceed.Wpf.Toolkit.PropertyGrid.PropertyItemBase> e)
        {
            PropertyGrid propertyGrid = sender as PropertyGrid;
            //获取当前选择项的父类类型
            //需要做一次类型转换 否则获取不到相应属性
            PropertyItem item = propertyGrid.SelectedPropertyItem?.ParentElement as PropertyItem;
            PropName = item?.PropertyType.Name;
        }

3. 常用属性

IdPropertyDescription
1SelectedObjectGets or sets the current object the PropertyGrid is inspecting.
2IsReadOnlyGets or sets a value indicating whether the property grid is read-only.
3NameColumnWidthGets or sets the width of the property name column.
4ShowSearchBoxGets or sets a value indicating whether the search box is displayed. By default, true.
5ShowSortOptionsGets or sets a value indicating whether the show sort options are displayed (Categorized and Alphabetical). By default, true.
6ShowTitleGets or sets a value indicating whether the PropertyGrid's title is displayed.
7ShowDescriptionByTooltipGets or sets if the Description of the PropertyItem will be displayed as a tooltip on the PART_Name of the PropertyItem. When ShowDescriptionByTooltip is True and the DescriptionAttribute on the PropertyItem is not null and not empty, it will be displayed as a tooltip.

4. Model层设置

4.1 测试类

private TestPropertyGrid _pro;
        public TestPropertyGrid PropeObject
        {
            get => _pro;
            set => SetProperty(ref _pro, value);
        }



public class TestPropertyGrid
    {
        public int PropertyA { get; set; }
        public string PropertyB { get; set; }
        public TestA TestA { get; set; }
        public List<TestB> TestBList { get; set; }

    }

    public class TestA
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class TestB
    {
        public int Id { get; set; }
        public string Address { get; set; }
    }

4.2  为对象添加相关特性

添加引用

using System.ComponentModel;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;  

     4.2.1 添加描述特性

[Description("属性A")]
public int PropertyA { get; set; }

     4.2.2 添加分组

[Category("分组1")]
public int PropertyA { get; set; }

     4.2.3 添加属性是否显示标识


[Browsable(true)]
public int PropertyA { get; set; }

     4.2.4 添加类属性可展开特性

[ExpandableObject]
public class TestA
{
        
}

     4.2.5 添加类属性显示顺序

[PropertyOrder(2)]
public int Id { get; set; }
[PropertyOrder(1)]
public string Name { get; set; }

    4.2.6 对分组[Category]再进行排序

    说明:分组的优先级高于PropertyOrder,而分组是根据组名按拼音进行排序,部分情况需对其进行再排序

[CategoryOrder("分组1",2), CategoryOrder("分组2", 1)]
    public class TestPropertyGrid
    {
        [Category("分组1")]
        public int PropertyA { get; set; }
        [Category("分组2")]
        public string PropertyB { get; set; }
    }

4.3 自定义List集合的显示

  可展开的集合类

public class ExpandableObservableCollection<T> : ObservableCollection<T>,
                                                   ICustomTypeDescriptor
    {
        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
        {
            // Create a collection object to hold property descriptors
            PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);

            for (int i = 0; i < Count; i++)
            {
                pds.Add(new ItemPropertyDescriptor<T>(this, i));
            }

            return pds;
        }

        #region Use default TypeDescriptor stuff

        AttributeCollection ICustomTypeDescriptor.GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, noCustomTypeDesc: true);
        }

        string ICustomTypeDescriptor.GetClassName()
        {
            return TypeDescriptor.GetClassName(this, noCustomTypeDesc: true);
        }

        string ICustomTypeDescriptor.GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, noCustomTypeDesc: true);
        }

        TypeConverter ICustomTypeDescriptor.GetConverter()
        {
            return TypeDescriptor.GetConverter(this, noCustomTypeDesc: true);
        }

        EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, noCustomTypeDesc: true);
        }

        PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, noCustomTypeDesc: true);
        }

        object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, noCustomTypeDesc: true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
        {
            return TypeDescriptor.GetEvents(this, noCustomTypeDesc: true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, noCustomTypeDesc: true);
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
        {
            return TypeDescriptor.GetProperties(this, attributes, noCustomTypeDesc: true);
        }

        object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }

        #endregion
    }

    public class ItemPropertyDescriptor<T> : PropertyDescriptor
    {
        private readonly ObservableCollection<T> _owner;
        private readonly int _index;

        public ItemPropertyDescriptor(ObservableCollection<T> owner, int index)
          : base($"[{index}]" , null)
        {
            _owner = owner;
            _index = index;
        }

        public override AttributeCollection Attributes
        {
            get
            {
                var attributes = TypeDescriptor.GetAttributes(GetValue(null), false);


                if (!attributes.OfType<ExpandableObjectAttribute>().Any())
                {
                    // copy all the attributes plus an extra one (the
                    // ExpandableObjectAttribute)
                    // this ensures that even if the type of the object itself doesn't have the
                    // ExpandableObjectAttribute, it will still be expandable. 
                    var newAttributes = new Attribute[attributes.Count + 1];
                    attributes.CopyTo(newAttributes, 0);
                    newAttributes[newAttributes.Length - 1] = new ExpandableObjectAttribute();

                    // overwrite the original
                    attributes = new AttributeCollection(newAttributes);

                }

                return attributes;
            }
        }

        public override bool CanResetValue(object component)
        {
            return false;
        }

        public override object GetValue(object component)
        {
            return Value;
        }

        private T Value
          => _owner[_index];

        public override void ResetValue(object component)
        {
            throw new NotImplementedException();
        }

        public override void SetValue(object component, object value)
        {
            _owner[_index] = (T)value;
        }

        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }

        public override Type ComponentType
          => _owner.GetType();

        public override bool IsReadOnly
          => false;

        public override Type PropertyType
          => Value?.GetType();
    }

 调用:List类型改为ExpandableObservableCollection,再添加可展开特性

[ExpandableObject]
public ExpandableObservableCollection<TestB> TestBList { get; set; }

效果:

5. 代码动态获取/设置相关特性的值   

5.1 设置Browsable特性的值

public void SetPropertyVisibility(object obj, string propertyName, bool visible)
        {
            Type type = typeof(BrowsableAttribute);
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(obj);
            AttributeCollection attrs = props[propertyName].Attributes;
            FieldInfo fld = type.GetField("browsable", BindingFlags.Instance | BindingFlags.NonPublic);
            fld?.SetValue(attrs[type], visible);
        }

5.2 设置Category特性的值

public void SetCategoryValue(object obj, string propertyName,string str)
        {
            Type type = typeof(CategoryAttribute);
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(obj);
            AttributeCollection attrs = props[propertyName].Attributes;
            FieldInfo fld = type.GetField("categoryValue", BindingFlags.Instance | BindingFlags.NonPublic);
            fld?.SetValue(attrs[type], str);
        }

5.3  获取类的属性成员的Category特性的值

public string GetCategoryValue(object obj, string propertyName)
        {
            Type type = typeof(CategoryAttribute);
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(obj); 
            AttributeCollection attrs = props[propertyName].Attributes;
            FieldInfo fld = type.GetField("categoryValue", BindingFlags.Instance | BindingFlags.NonPublic);
            var str = fld.GetValue(attrs[type])?.ToString();
            return str;

        }

5.4 设置类的CategoryOrderAttribute特性的值

public void SetCategoryOrderValue(object obj,CategoryOrderAttribute[] categoryOrders)
        {
            TypeDescriptor.AddAttributes(obj.GetType(), categoryOrders);

        }

5.5  嵌套属性赋值

/// <summary>
        /// 使用反射设置嵌套属性的值
        /// </summary>
        /// <param name="obj">对象 </param>
        /// <param name="propename">第一层属性</param>
        /// <param name="targetname">目标属性(是第一层属性的成员)</param>
        /// <param name="val">赋的值</param>
        private void NestPropeSetValue(object obj, string propename, string targetname, object val)
        {
            PropertyInfo[] propertyInfos = obj.GetType().GetProperties();
            var prope = propertyInfos.Single(x => propename == x.PropertyType.Name);
            var value = prope.GetValue(obj, null);
            var pr = value.GetType().GetProperties().Single(x => x.Name == targetname);
            pr?.SetValue(value, val);

        }

 6. 自定义Category样式

<UserControl.Resources>
        <DataTemplate x:Key="Category">
            <TextBlock Text="{Binding}" FontSize="20" Foreground="#4EB9E4" />
        </DataTemplate>
</UserControl.Resources>

<!--调用:-->
CategoryGroupHeaderTemplate="{StaticResource Category}"

7. 自定义属性编辑模块

<xctk:PropertyGrid  SelectedObject="{Binding PropeObject}" >
            <xctk:PropertyGrid.EditorDefinitions>
               <!--EditorTemplateDefinition可添加多个-->                                     
                                                       <!--要修改编辑模板的属性的名称-->
                <xctk:EditorTemplateDefinition TargetProperties="PropertyA,PropertyB">
                    <xctk:EditorTemplateDefinition.EditingTemplate>
                        <DataTemplate>
                            <!--此处可自由发挥-->
                            <Grid>
                                <!-- Command生效:  DataTemplate的DataContext指代不明确,需要改为父类的DataContext。   参数Value表示原对象-->
                                <ToggleButton Command="{Binding Path=DataContext.PropeSetCommand ,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=xctk:PropertyGrid}}"
                                              CommandParameter="{Binding Value,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Height="25"  Content="点击"  Width="300" HorizontalAlignment="Left"></ToggleButton>
                            </Grid>
                        </DataTemplate>
                    </xctk:EditorTemplateDefinition.EditingTemplate>
                </xctk:EditorTemplateDefinition>
            </xctk:PropertyGrid.EditorDefinitions>
        </xctk:PropertyGrid>

 ToggleButton样式

<UserControl.Resources>
        <Style TargetType="{x:Type ToggleButton}" >
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="FontFamily" Value="Microsoft YaHei"/>
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToggleButton">
                        <Border BorderBrush="{TemplateBinding Control.BorderBrush}" BorderThickness="0" CornerRadius="2">
                            <Border.Background>
                                <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
                                    <GradientStop Color="#FF71E0C6" Offset="0.0" />
                                    <GradientStop Color="#FF43A88D" Offset="0.2" />
                                    <GradientStop Color="#FF5BB07D" Offset="0.0" />
                                </LinearGradientBrush>
                            </Border.Background>
                            <ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" ></ContentPresenter>
                        </Border>
                        <ControlTemplate.Triggers>

                            <Trigger Property="ButtonBase.IsPressed" Value="True">
                                <Setter Property="UIElement.Effect">
                                    <Setter.Value>
                                        <DropShadowEffect BlurRadius="10" Color="#276AB0" Direction="0" Opacity="0.9" RenderingBias="Performance" ShadowDepth="0" />
                                    </Setter.Value>
                                </Setter>
                                <Setter Property="RenderTransform">
                                    <Setter.Value>
                                        <ScaleTransform ScaleX="0.9" ScaleY="0.9" />
                                    </Setter.Value>
                                </Setter>
                                <Setter Property="RenderTransformOrigin" Value=".5,.5" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>

                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>

8. 最终效果图

参考:

【1】 PropertyGrid · xceedsoftware/wpftoolkit Wiki · GitHub

【2】 c# - Category Ordering In Xceed PropertyGrid - Stack Overflow

【3】 Xceed WPF PropertyGrid Show项目用于扩展收集 - IT宝库 (itbaoku.cn)

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值