温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何在PropertyGrid中自定义控件

发布时间:2021-03-30 16:51:41 来源:亿速云 阅读:371 作者:Leah 栏目:编程语言

本篇文章为大家展示了如何在PropertyGrid中自定义控件,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

1.创建一个CustomPropertyGrid自定义控件:

<UserControl   x:Class="PropertyGridDemo.PropertyGridControl.CustomPropertyGrid"   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   xmlns:dxprg="http://schemas.devexpress.com/winfx/2008/xaml/propertygrid"   xmlns:local="clr-namespace:PropertyGridDemo.PropertyGridControl"   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   d:DesignHeight="300"   d:DesignWidth="300"   mc:Ignorable="d">   <UserControl.Resources>     <ResourceDictionary>       <ResourceDictionary.MergedDictionaries>         <!-- 资源字典 -->         <ResourceDictionary Source="../PropertyGridControl/DynamicallyAssignDataEditorsResources.xaml" />       </ResourceDictionary.MergedDictionaries>     </ResourceDictionary>   </UserControl.Resources>   <Grid>     <!-- PropertyDefinitionStyle:定义属性描述的风格模板 -->     <!-- PropertyDefinitionTemplateSelector:定义一个模板选择器,对应一个继承自DataTemplateSelector的类 -->     <!-- PropertyDefinitionsSource:定义一个获取数据属性集合的类,对应一个自定义类(本Demo中对应DataEditorsViewModel) -->     <dxprg:PropertyGridControl       x:Name="PropertyGridControl"       Margin="24"       DataContextChanged="PropertyGridControl_DataContextChanged"       ExpandCategoriesWhenSelectedObjectChanged="True"       PropertyDefinitionStyle="{StaticResource DynamicallyAssignDataEditorsPropertyDefinitionStyle}"       PropertyDefinitionTemplateSelector="{StaticResource DynamicallyAssignDataEditorsTemplateSelector}"       PropertyDefinitionsSource="{Binding Path=Properties, Source={StaticResource DemoDataProvider}}"       ShowCategories="True"       ShowDescriptionIn="Panel" />   </Grid> </UserControl>

该控件使用的资源字典如下:

<ResourceDictionary   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"   xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"   xmlns:dxprg="http://schemas.devexpress.com/winfx/2008/xaml/propertygrid"   xmlns:local="clr-namespace:PropertyGridDemo.PropertyGridControl"   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   mc:Ignorable="d">   <local:DynamicallyAssignDataEditorsTemplateSelector x:Key="DynamicallyAssignDataEditorsTemplateSelector" />   <local:DataEditorsViewModel x:Key="DemoDataProvider" />   <DataTemplate x:Key="DescriptionTemplate">     <RichTextBox       x:Name="descriptionRichTextBox"       MinWidth="150"       HorizontalContentAlignment="Stretch"       Background="Transparent"       BorderThickness="0"       Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource TemplatedParent}}"       IsReadOnly="True"       IsTabStop="False" />   </DataTemplate>   <DataTemplate x:Key="descriptionTemplate">     <RichTextBox       x:Name="descriptionRichTextBox"       MinWidth="150"       HorizontalContentAlignment="Stretch"       Background="Transparent"       BorderThickness="0"       Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource TemplatedParent}}"       IsReadOnly="True"       IsTabStop="False" />   </DataTemplate>   <!-- 设置控件的全局样式和数据绑定 -->   <Style x:Key="DynamicallyAssignDataEditorsPropertyDefinitionStyle" TargetType="dxprg:PropertyDefinition">     <Setter Property="Path" Value="{Binding Name}" />     <!--<Setter Property="Header" Value="{Binding Converter={StaticResource PropertyDescriptorToDisplayNameConverter}}"/>-->     <Setter Property="Description" Value="{Binding}" />     <Setter Property="DescriptionTemplate" Value="{StaticResource descriptionTemplate}" />   </Style>   <Style x:Key="DescriptionContainerStyle" TargetType="dxprg:PropertyDescriptionPresenterControl">     <Setter Property="ShowSelectedRowHeader" Value="False" />     <Setter Property="MinHeight" Value="70" />   </Style>   <Style TargetType="Slider">     <Setter Property="Margin" Value="2" />   </Style>   <Style TargetType="dxe:ComboBoxEdit">     <Setter Property="IsTextEditable" Value="False" />     <Setter Property="ApplyItemTemplateToSelectedItem" Value="True" />     <Setter Property="Margin" Value="2" />   </Style>   <!-- 测试直接从DataTemplate获取控件 -->   <DataTemplate x:Key="SliderTemplate" DataType="local:SliderExtend">     <!--<dxprg:PropertyDefinition>       <dxprg:PropertyDefinition.CellTemplate>-->     <!--<DataTemplate>-->     <StackPanel x:Name="Root">       <Slider         Maximum="{Binding Path=Max}"         Minimum="{Binding Path=Min}"         Value="{Binding Path=Value}" />       <TextBlock Text="{Binding Path=Value}" />     </StackPanel>     <!--</DataTemplate>-->     <!--</dxprg:PropertyDefinition.CellTemplate>     </dxprg:PropertyDefinition>-->   </DataTemplate>   <DataTemplate x:Key="ComboBoxEditItemTemplate" DataType="Tuple">     <TextBlock       Height="20"       Margin="5,3,0,0"       VerticalAlignment="Center"       Text="{Binding Item1}" />   </DataTemplate> </ResourceDictionary>

2.编写对应的模板选择类 DynamicallyAssignDataEditorsTemplateSelector:

using DevExpress.Xpf.Editors; using DevExpress.Xpf.PropertyGrid; using System.ComponentModel; using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Data; namespace PropertyGridDemo.PropertyGridControl {   public class DynamicallyAssignDataEditorsTemplateSelector : DataTemplateSelector   {     private PropertyDescriptor _property = null;     private RootPropertyDefinition _element = null;     private PropertyDataContext _propertyDataContext => App.PropertyGridDataContext;     /// <summary>     /// 当重写在派生类中,返回根据自定义逻辑的 <see cref="T:System.Windows.DataTemplate" /> 。     /// </summary>     /// <param name="item">数据对象可以选择模板。</param>     /// <param name="container">数据对象。</param>     /// <returns>     /// 返回 <see cref="T:System.Windows.DataTemplate" /> 或 null。默认值为 null。     /// </returns>     public override DataTemplate SelectTemplate(object item, DependencyObject container)     {       _element = (RootPropertyDefinition)container;       DataTemplate resource = TryCreateResource(item);       return resource ?? base.SelectTemplate(item, container);     }     /// <summary>     /// Tries the create resource.     /// </summary>     /// <param name="item">The item.</param>     /// <returns></returns>     private DataTemplate TryCreateResource(object item)     {       if (!(item is PropertyDescriptor)) return null;       PropertyDescriptor pd = (PropertyDescriptor)item;       _property = pd;       var customUIAttribute = (CustomUIAttribute)pd.Attributes[typeof(CustomUIAttribute)];       if (customUIAttribute == null) return null;       var customUIType = customUIAttribute.CustomUI;       return CreatePropertyDefinitionTemplate(customUIAttribute);     }     /// <summary>     /// Gets the data context.     /// </summary>     /// <param name="dataContextPropertyName">Name of the data context property.</param>     /// <returns></returns>     private object GetDataContext(string dataContextPropertyName)     {       PropertyInfo property = _propertyDataContext?.GetType().GetProperty(dataContextPropertyName);       if (property == null) return null;       return property.GetValue(_propertyDataContext, null);     }     /// <summary>     /// Creates the slider data template.     /// </summary>     /// <param name="customUIAttribute">The custom UI attribute.</param>     /// <returns></returns>     private DataTemplate CreateSliderDataTemplate(CustomUIAttribute customUIAttribute)     {       DataTemplate ct = new DataTemplate();       ct.VisualTree = new FrameworkElementFactory(typeof(StackPanel));       ct.VisualTree.SetValue(StackPanel.DataContextProperty, GetDataContext(customUIAttribute.DataContextPropertyName));       FrameworkElementFactory sliderFactory = new FrameworkElementFactory(typeof(Slider));       sliderFactory.SetBinding(Slider.MaximumProperty, new Binding(nameof(SliderUIDataContext.Max)));       sliderFactory.SetBinding(Slider.MinimumProperty, new Binding(nameof(SliderUIDataContext.Min)));       sliderFactory.SetBinding(Slider.SmallChangeProperty, new Binding(nameof(SliderUIDataContext.SmallChange)));       sliderFactory.SetBinding(Slider.LargeChangeProperty, new Binding(nameof(SliderUIDataContext.LargeChange)));       sliderFactory.SetBinding(Slider.ValueProperty, new Binding(nameof(SliderUIDataContext.Value)));       ct.VisualTree.AppendChild(sliderFactory);       FrameworkElementFactory textFacotry = new FrameworkElementFactory(typeof(TextBlock), "TextBlock");       textFacotry.SetValue(TextBlock.TextProperty, new Binding(nameof(SliderUIDataContext.Value)));       //textBoxFactory.AddHandler(TextBox.IsVisibleChanged, new DependencyPropertyChangedEventHandler(SearchBoxVisibleChanged));       ct.VisualTree.AppendChild(textFacotry);       ct.Seal();       return ct;     }     /// <summary>     /// Creates the ComboBox edit template.     /// </summary>     /// <param name="customUIAttribute">The custom UI attribute.</param>     /// <returns></returns>     private DataTemplate CreateComboBoxEditTemplate(CustomUIAttribute customUIAttribute)     {       DataTemplate template = new DataTemplate();       template.VisualTree = new FrameworkElementFactory(typeof(DockPanel));       template.VisualTree.SetValue(DockPanel.DataContextProperty, GetDataContext(customUIAttribute.DataContextPropertyName));       FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock)) ;       textFactory.SetValue(TextBlock.TextProperty, new Binding(nameof(ComboBoxEditDataContext.Name)));       template.VisualTree.AppendChild(textFactory);       FrameworkElementFactory comboBoxEditFactory = new FrameworkElementFactory(typeof(ComboBoxEdit));       comboBoxEditFactory.SetBinding(ComboBoxEdit.ItemsSourceProperty, new Binding(nameof(ComboBoxEditDataContext.ItemSource)));       comboBoxEditFactory.SetBinding(ComboBoxEdit.EditValueProperty, new Binding(nameof(ComboBoxEditDataContext.EditValue)));       comboBoxEditFactory.SetBinding(ComboBoxEdit.SelectedIndexProperty, new Binding(nameof(ComboBoxEditDataContext.SelectedIndex)));       comboBoxEditFactory.SetValue(ComboBoxEdit.ItemTemplateProperty, (DataTemplate)_element.TryFindResource("ComboBoxEditItemTemplate"));       template.VisualTree.AppendChild(comboBoxEditFactory);       template.Seal();       return template;     }     /// <summary>     /// Creates the property definition template.     /// </summary>     /// <param name="customUIAttribute">The custom UI attribute.</param>     /// <returns></returns>     private DataTemplate CreatePropertyDefinitionTemplate(CustomUIAttribute customUIAttribute)     {       DataTemplate dataTemplate = new DataTemplate();       DataTemplate cellTemplate = null;//单元格模板       FrameworkElementFactory factory = new FrameworkElementFactory(typeof(PropertyDefinition));       dataTemplate.VisualTree = factory;       switch (customUIAttribute.CustomUI)       {         case CustomUITypes.Slider:           cellTemplate = CreateSliderDataTemplate(customUIAttribute); break;           //cellTemplate = (DataTemplate)_element.TryFindResource("SliderTemplate");break;         case CustomUITypes.ComboBoxEit:           cellTemplate = CreateComboBoxEditTemplate(customUIAttribute);break;                }       if (cellTemplate != null)       {         factory.SetValue(PropertyDefinition.CellTemplateProperty, cellTemplate);         dataTemplate.Seal();       }       else       {         return null;       }       return dataTemplate;     }   } }
using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace PropertyGridDemo.PropertyGridControl {   /// <summary>   ///初始化所有属性并调用模板选择器进行匹配   /// </summary>   public class DataEditorsViewModel   {     public IEnumerable<PropertyDescriptor> Properties { get { return TypeDescriptor.GetProperties(typeof(TestPropertyGrid)).Cast<PropertyDescriptor>(); } }   } }

3.编写一个可用于构建模板的属性 CustomUIType:

using System; namespace PropertyGridDemo.PropertyGridControl {   public class CustomUIType   {   }   public enum CustomUITypes   {     Slider,     ComboBoxEit,     SpinEdit,     CheckBoxEdit   }   [AttributeUsage(AttributeTargets.Property)]   internal class CustomUIAttribute : Attribute   {     public string DataContextPropertyName { get; set; }     public CustomUITypes CustomUI { get; set; }     /// <summary>     /// 自定义控件属性构造函数     /// </summary>     /// <param name="uiTypes">The UI types.</param>     /// <param name="dataContextPropertyName">Name of the data context property.</param>     internal CustomUIAttribute(CustomUITypes uiTypes, string dataContextPropertyName)     {       CustomUI = uiTypes;       DataContextPropertyName = dataContextPropertyName;     }   } }

4.编写对应的DataContext类 TestPropertyGrid:

using DevExpress.Mvvm.DataAnnotations; using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Timers; using System.Windows; namespace PropertyGridDemo.PropertyGridControl {   [MetadataType(typeof(DynamicallyAssignDataEditorsMetadata))]   public class TestPropertyGrid : PropertyDataContext   {     private double _count = 0;     private SliderUIDataContext _countSource = null;     private ComboBoxEditDataContext _comboSource = null;     private double _value=1;     public TestPropertyGrid()     {       Password = "1111111";       Notes = "Hello";       Text = "Hello hi";     }     [Browsable(false)]     public SliderUIDataContext CountSource     {       get       {         if (_countSource != null)         {           return _countSource;         }         else         {           _countSource = new SliderUIDataContext(0, 100, Count, 0.1, 1);           _countSource.PropertyChanged += (object o, PropertyChangedEventArgs e) =>           {             this.Count = _countSource.Value;           };           return _countSource;         }       }     }     [Browsable(false)]     public ComboBoxEditDataContext ComboSource     {       get       {         if(_comboSource==null)         {           _comboSource =new ComboBoxEditDataContext(ComboBoxEditItemSource.TestItemSource,Value);           _comboSource.PropertyChanged += (object o, PropertyChangedEventArgs e) =>            {              this.Value =Convert.ToDouble(_comboSource.EditValue.Item2);             };                    }         return _comboSource;       }     }     [Display(Name = "SliderEdit", GroupName = "CustomUI")]     [CustomUI(CustomUITypes.Slider, nameof(CountSource))]     public double Count     {       get => _count;       set       {         _count = value;         CountSource.Value = value;          RaisePropertyChanged(nameof(Count));       }     }     [Display(Name = "ComboBoxEditItem", GroupName = "CustomUI")]     [CustomUI(CustomUITypes.ComboBoxEit, nameof(ComboSource))]     public double Value     {       get => _value;       set       {         if (_value == value) return;         _value = value;         //ComboSource.Value = value;         RaisePropertyChanged(nameof(Value));       }     }     [Display(Name = "Password", GroupName = "DefaultUI")]     public string Password { get; set; }     [Display(Name = "TextEdit", GroupName = "DefaultUI")]     public string Text { get; set; }     [Display(Name = "Notes", GroupName = "DefaultUI")]     public string Notes { get; set; }     [Display(Name = "Double", GroupName = "DefaultUI")]     [DefaultValue(1)]     public double TestDouble { get; set; }     [Display(Name = "Items", GroupName = "DefaultUI")]     [DefaultValue(Visibility.Visible)]     public Visibility TestItems { get; set; }   }   public static class DynamicallyAssignDataEditorsMetadata   {     public static void BuildMetadata(MetadataBuilder<TestPropertyGrid> builder)     {       builder.Property(x => x.Password)         .PasswordDataType();       builder.Property(x => x.Notes)         .MultilineTextDataType();     }   } }

 该类中用到的其他类主要有以下几个,以下几个类主要用于数据绑定:

 namespace PropertyGridDemo.PropertyGridControl {   public class SliderUIDataContext:PropertyDataContext   {     private double _value = 0;     private double _max = 0;     private double _min = 0;     private double _smallChange = 1;     private double _largeChange=1;     public SliderUIDataContext()     {     }     /// <summary>     /// Initializes a new instance of the <see cref="SliderUIDataContext"/> class.     /// </summary>     /// <param name="min">The minimum.</param>     /// <param name="max">The maximum.</param>     /// <param name="value">The value.</param>     /// <param name="smallChange">The small change.</param>     /// <param name="largeChange">The large change.</param>     public SliderUIDataContext(double min, double max, double value,double smallChange=0.01,double largeChange=0.1)     {       SmallChange = smallChange;       LargeChange = largeChange;       Max = max;       Min = min;       Value = value;     }     /// <summary>     /// Gets or sets the small change.     /// </summary>     /// <value>     /// The small change.     /// </value>     public double SmallChange     {       get => _smallChange;       set       {         if (value == _min) return;         _min = value;         RaisePropertyChanged(nameof(SmallChange));       }     }     /// <summary>     /// Gets or sets the large change.     /// </summary>     /// <value>     /// The large change.     /// </value>     public double LargeChange     {       get => _largeChange;       set       {         if (Value == _largeChange) return;         _largeChange = value;         RaisePropertyChanged(nameof(LargeChange));       }     }     /// <summary>     /// Gets or sets the maximum.     /// </summary>     /// <value>     /// The maximum.     /// </value>     public double Max     {       get => _max;       set       {         if (value == _max) return;         _max = value;         RaisePropertyChanged(nameof(Max));       }     }     /// <summary>     /// Gets or sets the minimum.     /// </summary>     /// <value>     /// The minimum.     /// </value>     public double Min     {       get => _min;       set       {         if (value == _min) return;         _min = value;         RaisePropertyChanged(nameof(Min));       }     }     /// <summary>     /// Gets or sets the value.     /// </summary>     /// <value>     /// The value.     /// </value>     public double Value     {       get => _value;       set       {         if (value == _value) return;         _value = value;         RaisePropertyChanged(nameof(Value));       }     }   } }
using System; using System.Linq; namespace PropertyGridDemo.PropertyGridControl {   public class ComboBoxEditDataContext:PropertyDataContext   {     private Tuple<string, object>[] _itemSource;     private Tuple<string, object> _editValue;     private int _selectedIndex;     /// <summary>     /// Initializes a new instance of the <see cref="ComboBoxEditDataContext"/> class.     /// </summary>     /// <param name="itemSource">The item source.</param>     /// <param name="editValue">The edit value.</param>     public ComboBoxEditDataContext(Tuple<string,object>[] itemSource,Tuple<string,object> editValue)     {       _itemSource = itemSource;       _editValue = _itemSource.FirstOrDefault(x => x?.Item1.ToString() == editValue?.Item1.ToString() && x?.Item2?.ToString() == x?.Item2?.ToString());     }     /// <summary>     /// Initializes a new instance of the <see cref="ComboBoxEditDataContext" /> class.     /// </summary>     /// <param name="itemSource">The item source.</param>     /// <param name="value">The value.</param>     public ComboBoxEditDataContext(Tuple<string, object>[] itemSource, object value)     {       _itemSource = itemSource;       _editValue = _itemSource.FirstOrDefault(x => x?.Item2.ToString() == value.ToString() );     }     public string Name     {       get;set;     }     /// <summary>     /// Gets or sets the item source.     /// </summary>     /// <value>     /// The item source.     /// </value>     public Tuple<string,object>[] ItemSource     {       get => _itemSource;       set       {         //if (_itemSource == value) return;         _itemSource = value;         RaisePropertyChanged(nameof(ItemSource));       }     }     /// <summary>     /// Gets or sets the edit value.     /// </summary>     /// <value>     /// The edit value.     /// </value>     public Tuple<string,object> EditValue     {       get => _editValue;       set       {         if (_editValue == value) return;         _editValue = value;         RaisePropertyChanged(nameof(EditValue));       }     }     public object Value     {       set       {         EditValue = ItemSource.FirstOrDefault(x => x.Item2.Equals(value));       }     }     /// <summary>     /// Gets or sets the index of the selected.     /// </summary>     /// <value>     /// The index of the selected.     /// </value>     public int SelectedIndex     {       get => _selectedIndex;       set       {         if (_selectedIndex == value || value==-1) return;         _selectedIndex = value;         EditValue = ItemSource[value];         RaisePropertyChanged(nameof(SelectedIndex));       }     }   } }
using System.ComponentModel; namespace PropertyGridDemo.PropertyGridControl {   public class PropertyDataContext:INotifyPropertyChanged   {     /// <summary>     /// 在更改属性值时发生。     /// </summary>     public event PropertyChangedEventHandler PropertyChanged;     /// <summary>     /// 触发属性变化     /// </summary>     /// <param name="propertyName"></param>     public virtual void RaisePropertyChanged(string propertyName)     {       PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));     }   } }
using System; namespace PropertyGridDemo.PropertyGridControl {   internal static class ComboBoxEditItemSource   {     internal static Tuple<string, object>[] TestItemSource = new Tuple<string, object>[] {       new Tuple<string, object>("1",1),       new Tuple<string, object>("2",2),       new Tuple<string, object>("3",3)     };   } }

5.将以上的CustomPropertyGrid丢进容器中即可,这里我直接用Mainwindow来演示:

<Window   x:Class="PropertyGridDemo.MainWindow"   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   xmlns:PropertyGridControl="clr-namespace:PropertyGridDemo.PropertyGridControl"   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   xmlns:local="clr-namespace:PropertyGridDemo"   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   Title="MainWindow"   Width="525"   Height="350"   WindowState="Maximized"   mc:Ignorable="d">   <Grid Margin="10">     <Grid.ColumnDefinitions>       <ColumnDefinition Width="259*" />       <ColumnDefinition Width="259*" />     </Grid.ColumnDefinitions>     <TextBox       x:Name="OutputBox"       Grid.ColumnSpan="1"       HorizontalScrollBarVisibility="Auto"       ScrollViewer.CanContentScroll="True" />     <PropertyGridControl:CustomPropertyGrid x:Name="PropertyGrid" Grid.Column="1" />   </Grid> </Window>

运行示意图:

如何在PropertyGrid中自定义控件

上述内容就是如何在PropertyGrid中自定义控件,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI