PropertyGrid是一个很强大的控件,使用该控件做属性设置面板的一个好处就是你只需要专注于代码而无需关注UI的呈现,PropertyGrid会默认根据变量类型选择合适的控件显示。但是这也带来了一个问题,就是控件的使用变得不是特别灵活,主要表现在你无法根据你的需求很好的选择控件,比如当你需要用Slider控件来设置int型变量时,PropertyGrid默认的模板选择器是不支持的。网上找了许多资料基本都是介绍WinForm的实现方式,主要用到了IWindowFromService这个接口,并未找到合适的适合WPF的Demo,后来在参考了DEVExpress的官方Demo之后我做了一个基于WPF和DEV 16.2的PropertyGrid Demo,基本实现了上述功能。

为了实现这一点,需要自定义一个DataTemplateSeletor类,这也是本文的核心代码。

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

 1 <UserControl
 2     x:Class="PropertyGridDemo.PropertyGridControl.CustomPropertyGrid"
 3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 5     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 6     xmlns:dxprg="http://schemas.devexpress.com/winfx/2008/xaml/propertygrid"
 7     xmlns:local="clr-namespace:PropertyGridDemo.PropertyGridControl"
 8     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 9     d:DesignHeight="300"
10     d:DesignWidth="300"
11     mc:Ignorable="d">
12     <UserControl.Resources>
13         <ResourceDictionary>
14             <ResourceDictionary.MergedDictionaries>
15                 <!--  资源字典  -->
16                 <ResourceDictionary Source="../PropertyGridControl/DynamicallyAssignDataEditorsResources.xaml" />
17             </ResourceDictionary.MergedDictionaries>
18         </ResourceDictionary>
19     </UserControl.Resources>
20     <Grid>
21         <!--  PropertyDefinitionStyle:定义属性描述的风格模板  -->
22         <!--  PropertyDefinitionTemplateSelector:定义一个模板选择器,对应一个继承自DataTemplateSelector的类  -->
23         <!--  PropertyDefinitionsSource:定义一个获取数据属性集合的类,对应一个自定义类(本Demo中对应DataEditorsViewModel)  -->
24         <dxprg:PropertyGridControl
25             x:Name="PropertyGridControl"
26             Margin="24"
27             DataContextChanged="PropertyGridControl_DataContextChanged"
28             ExpandCategoriesWhenSelectedObjectChanged="True"
29             PropertyDefinitionStyle="{StaticResource DynamicallyAssignDataEditorsPropertyDefinitionStyle}"
30             PropertyDefinitionTemplateSelector="{StaticResource DynamicallyAssignDataEditorsTemplateSelector}"
31             PropertyDefinitionsSource="{Binding Path=Properties, Source={StaticResource DemoDataProvider}}"
32             ShowCategories="True"
33             ShowDescriptionIn="Panel" />
34     </Grid>
35 </UserControl>

CustomPropertyGrid

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

 1 <ResourceDictionary
 2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5     xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
 6     xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
 7     xmlns:dxprg="http://schemas.devexpress.com/winfx/2008/xaml/propertygrid"
 8     xmlns:local="clr-namespace:PropertyGridDemo.PropertyGridControl"
 9     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
10     mc:Ignorable="d">
11
12     <local:DynamicallyAssignDataEditorsTemplateSelector x:Key="DynamicallyAssignDataEditorsTemplateSelector" />
13     <local:DataEditorsViewModel x:Key="DemoDataProvider" />
14
15     <DataTemplate x:Key="DescriptionTemplate">
16         <RichTextBox
17             x:Name="descriptionRichTextBox"
18             MinWidth="150"
19             HorizontalContentAlignment="Stretch"
20             Background="Transparent"
21             BorderThickness="0"
22             Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource TemplatedParent}}"
23             IsReadOnly="True"
24             IsTabStop="False" />
25     </DataTemplate>
26     <DataTemplate x:Key="descriptionTemplate">
27         <RichTextBox
28             x:Name="descriptionRichTextBox"
29             MinWidth="150"
30             HorizontalContentAlignment="Stretch"
31             Background="Transparent"
32             BorderThickness="0"
33             Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource TemplatedParent}}"
34             IsReadOnly="True"
35             IsTabStop="False" />
36     </DataTemplate>
37
38     <!--  设置控件的全局样式和数据绑定  -->
39     <Style x:Key="DynamicallyAssignDataEditorsPropertyDefinitionStyle" TargetType="dxprg:PropertyDefinition">
40         <Setter Property="Path" Value="{Binding Name}" />
41         <!--<Setter Property="Header" Value="{Binding Converter={StaticResource PropertyDescriptorToDisplayNameConverter}}"/>-->
42         <Setter Property="Description" Value="{Binding}" />
43         <Setter Property="DescriptionTemplate" Value="{StaticResource descriptionTemplate}" />
44     </Style>
45     <Style x:Key="DescriptionContainerStyle" TargetType="dxprg:PropertyDescriptionPresenterControl">
46         <Setter Property="ShowSelectedRowHeader" Value="False" />
47         <Setter Property="MinHeight" Value="70" />
48     </Style>
49
50     <Style TargetType="Slider">
51         <Setter Property="Margin" Value="2" />
52     </Style>
53     <Style TargetType="dxe:ComboBoxEdit">
54         <Setter Property="IsTextEditable" Value="False" />
55         <Setter Property="ApplyItemTemplateToSelectedItem" Value="True" />
56         <Setter Property="Margin" Value="2" />
57     </Style>
58
59     <!--  测试直接从DataTemplate获取控件  -->
60     <DataTemplate x:Key="SliderTemplate" DataType="local:SliderExtend">
61         <!--<dxprg:PropertyDefinition>
62             <dxprg:PropertyDefinition.CellTemplate>-->
63         <!--<DataTemplate>-->
64         <StackPanel x:Name="Root">
65             <Slider
66                 Maximum="{Binding Path=Max}"
67                 Minimum="{Binding Path=Min}"
68                 Value="{Binding Path=Value}" />
69             <TextBlock Text="{Binding Path=Value}" />
70         </StackPanel>
71         <!--</DataTemplate>-->
72         <!--</dxprg:PropertyDefinition.CellTemplate>
73         </dxprg:PropertyDefinition>-->
74     </DataTemplate>
75
76     <DataTemplate x:Key="ComboBoxEditItemTemplate" DataType="Tuple">
77         <TextBlock
78             Height="20"
79             Margin="5,3,0,0"
80             VerticalAlignment="Center"
81             Text="{Binding Item1}" />
82     </DataTemplate>
83 </ResourceDictionary>

ResourceDictionary

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

  1 using DevExpress.Xpf.Editors;
  2 using DevExpress.Xpf.PropertyGrid;
  3 using System.ComponentModel;
  4 using System.Reflection;
  5 using System.Windows;
  6 using System.Windows.Controls;
  7 using System.Windows.Data;
  8
  9 namespace PropertyGridDemo.PropertyGridControl
 10 {
 11     public class DynamicallyAssignDataEditorsTemplateSelector : DataTemplateSelector
 12     {
 13         private PropertyDescriptor _property = null;
 14         private RootPropertyDefinition _element = null;
 15         private PropertyDataContext _propertyDataContext => App.PropertyGridDataContext;
 16
 17         /// <summary>
 18         /// 当重写在派生类中,返回根据自定义逻辑的 <see cref="T:System.Windows.DataTemplate" /> 。
 19         /// </summary>
 20         /// <param name="item">数据对象可以选择模板。</param>
 21         /// <param name="container">数据对象。</param>
 22         /// <returns>
 23         /// 返回 <see cref="T:System.Windows.DataTemplate" /> 或 null。默认值为 null。
 24         /// </returns>
 25         public override DataTemplate SelectTemplate(object item, DependencyObject container)
 26         {
 27             _element = (RootPropertyDefinition)container;
 28             DataTemplate resource = TryCreateResource(item);
 29             return resource ?? base.SelectTemplate(item, container);
 30         }
 31
 32         /// <summary>
 33         /// Tries the create resource.
 34         /// </summary>
 35         /// <param name="item">The item.</param>
 36         /// <returns></returns>
 37         private DataTemplate TryCreateResource(object item)
 38         {
 39             if (!(item is PropertyDescriptor)) return null;
 40             PropertyDescriptor pd = (PropertyDescriptor)item;
 41             _property = pd;
 42             var customUIAttribute = (CustomUIAttribute)pd.Attributes[typeof(CustomUIAttribute)];
 43             if (customUIAttribute == null) return null;
 44             var customUIType = customUIAttribute.CustomUI;
 45             return CreatePropertyDefinitionTemplate(customUIAttribute);
 46         }
 47
 48         /// <summary>
 49         /// Gets the data context.
 50         /// </summary>
 51         /// <param name="dataContextPropertyName">Name of the data context property.</param>
 52         /// <returns></returns>
 53         private object GetDataContext(string dataContextPropertyName)
 54         {
 55             PropertyInfo property = _propertyDataContext?.GetType().GetProperty(dataContextPropertyName);
 56             if (property == null) return null;
 57             return property.GetValue(_propertyDataContext, null);
 58         }
 59
 60         /// <summary>
 61         /// Creates the slider data template.
 62         /// </summary>
 63         /// <param name="customUIAttribute">The custom UI attribute.</param>
 64         /// <returns></returns>
 65         private DataTemplate CreateSliderDataTemplate(CustomUIAttribute customUIAttribute)
 66         {
 67             DataTemplate ct = new DataTemplate();
 68             ct.VisualTree = new FrameworkElementFactory(typeof(StackPanel));
 69             ct.VisualTree.SetValue(StackPanel.DataContextProperty, GetDataContext(customUIAttribute.DataContextPropertyName));
 70
 71             FrameworkElementFactory sliderFactory = new FrameworkElementFactory(typeof(Slider));
 72             sliderFactory.SetBinding(Slider.MaximumProperty, new Binding(nameof(SliderUIDataContext.Max)));
 73             sliderFactory.SetBinding(Slider.MinimumProperty, new Binding(nameof(SliderUIDataContext.Min)));
 74             sliderFactory.SetBinding(Slider.SmallChangeProperty, new Binding(nameof(SliderUIDataContext.SmallChange)));
 75             sliderFactory.SetBinding(Slider.LargeChangeProperty, new Binding(nameof(SliderUIDataContext.LargeChange)));
 76             sliderFactory.SetBinding(Slider.ValueProperty, new Binding(nameof(SliderUIDataContext.Value)));
 77             ct.VisualTree.AppendChild(sliderFactory);
 78
 79             FrameworkElementFactory textFacotry = new FrameworkElementFactory(typeof(TextBlock), "TextBlock");
 80             textFacotry.SetValue(TextBlock.TextProperty, new Binding(nameof(SliderUIDataContext.Value)));
 81             //textBoxFactory.AddHandler(TextBox.IsVisibleChanged, new DependencyPropertyChangedEventHandler(SearchBoxVisibleChanged));
 82             ct.VisualTree.AppendChild(textFacotry);
 83             ct.Seal();
 84             return ct;
 85         }
 86
 87         /// <summary>
 88         /// Creates the ComboBox edit template.
 89         /// </summary>
 90         /// <param name="customUIAttribute">The custom UI attribute.</param>
 91         /// <returns></returns>
 92         private DataTemplate CreateComboBoxEditTemplate(CustomUIAttribute customUIAttribute)
 93         {
 94             DataTemplate template = new DataTemplate();
 95             template.VisualTree = new FrameworkElementFactory(typeof(DockPanel));
 96             template.VisualTree.SetValue(DockPanel.DataContextProperty, GetDataContext(customUIAttribute.DataContextPropertyName));
 97
 98             FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock)) ;
 99             textFactory.SetValue(TextBlock.TextProperty, new Binding(nameof(ComboBoxEditDataContext.Name)));
100             template.VisualTree.AppendChild(textFactory);
101
102             FrameworkElementFactory comboBoxEditFactory = new FrameworkElementFactory(typeof(ComboBoxEdit));
103             comboBoxEditFactory.SetBinding(ComboBoxEdit.ItemsSourceProperty, new Binding(nameof(ComboBoxEditDataContext.ItemSource)));
104             comboBoxEditFactory.SetBinding(ComboBoxEdit.EditValueProperty, new Binding(nameof(ComboBoxEditDataContext.EditValue)));
105             comboBoxEditFactory.SetBinding(ComboBoxEdit.SelectedIndexProperty, new Binding(nameof(ComboBoxEditDataContext.SelectedIndex)));
106             comboBoxEditFactory.SetValue(ComboBoxEdit.ItemTemplateProperty, (DataTemplate)_element.TryFindResource("ComboBoxEditItemTemplate"));
107             template.VisualTree.AppendChild(comboBoxEditFactory);
108             template.Seal();
109             return template;
110         }
111
112         /// <summary>
113         /// Creates the property definition template.
114         /// </summary>
115         /// <param name="customUIAttribute">The custom UI attribute.</param>
116         /// <returns></returns>
117         private DataTemplate CreatePropertyDefinitionTemplate(CustomUIAttribute customUIAttribute)
118         {
119             DataTemplate dataTemplate = new DataTemplate();
120             DataTemplate cellTemplate = null;//单元格模板
121             FrameworkElementFactory factory = new FrameworkElementFactory(typeof(PropertyDefinition));
122             dataTemplate.VisualTree = factory;
123             switch (customUIAttribute.CustomUI)
124             {
125                 case CustomUITypes.Slider:
126                     cellTemplate = CreateSliderDataTemplate(customUIAttribute); break;
127                     //cellTemplate = (DataTemplate)_element.TryFindResource("SliderTemplate");break;
128                 case CustomUITypes.ComboBoxEit:
129                     cellTemplate = CreateComboBoxEditTemplate(customUIAttribute);break;
130
131             }
132
133             if (cellTemplate != null)
134             {
135                 factory.SetValue(PropertyDefinition.CellTemplateProperty, cellTemplate);
136                 dataTemplate.Seal();
137
138             }
139             else
140             {
141                 return null;
142             }
143             return dataTemplate;
144         }
145     }
146 }

DynamicallyAssignDataEditorsTemplateSelector

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>(); } }}
}

DataEditorsViewModel

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;}}}

CustomUIType

4.编写对应的DataContext类 TestPropertyGrid:

  1 using DevExpress.Mvvm.DataAnnotations;
  2 using System;
  3 using System.ComponentModel;
  4 using System.ComponentModel.DataAnnotations;
  5 using System.Timers;
  6 using System.Windows;
  7
  8 namespace PropertyGridDemo.PropertyGridControl
  9 {
 10     [MetadataType(typeof(DynamicallyAssignDataEditorsMetadata))]
 11     public class TestPropertyGrid : PropertyDataContext
 12     {
 13         private double _count = 0;
 14         private SliderUIDataContext _countSource = null;
 15         private ComboBoxEditDataContext _comboSource = null;
 16         private double _value=1;
 17
 18         public TestPropertyGrid()
 19         {
 20             Password = "1111111";
 21             Notes = "Hello";
 22             Text = "Hello hi";
 23         }
 24
 25         [Browsable(false)]
 26         public SliderUIDataContext CountSource
 27         {
 28             get
 29             {
 30                 if (_countSource != null)
 31                 {
 32
 33                     return _countSource;
 34                 }
 35                 else
 36                 {
 37                     _countSource = new SliderUIDataContext(0, 100, Count, 0.1, 1);
 38                     _countSource.PropertyChanged += (object o, PropertyChangedEventArgs e) =>
 39                     {
 40                         this.Count = _countSource.Value;
 41                     };
 42                     return _countSource;
 43                 }
 44             }
 45         }
 46
 47         [Browsable(false)]
 48         public ComboBoxEditDataContext ComboSource
 49         {
 50             get
 51             {
 52                 if(_comboSource==null)
 53                 {
 54                     _comboSource =new ComboBoxEditDataContext(ComboBoxEditItemSource.TestItemSource,Value);
 55                     _comboSource.PropertyChanged += (object o, PropertyChangedEventArgs e) =>
 56                       {
 57                           this.Value =Convert.ToDouble(_comboSource.EditValue.Item2);
 58                       };
 59
 60                 }
 61                 return _comboSource;
 62             }
 63         }
 64
 65         [Display(Name = "SliderEdit", GroupName = "CustomUI")]
 66         [CustomUI(CustomUITypes.Slider, nameof(CountSource))]
 67         public double Count
 68         {
 69             get => _count;
 70             set
 71             {
 72                 _count = value;
 73                 CountSource.Value = value;
 74                 RaisePropertyChanged(nameof(Count));
 75             }
 76         }
 77
 78         [Display(Name = "ComboBoxEditItem", GroupName = "CustomUI")]
 79         [CustomUI(CustomUITypes.ComboBoxEit, nameof(ComboSource))]
 80         public double Value
 81         {
 82             get => _value;
 83             set
 84             {
 85                 if (_value == value) return;
 86                 _value = value;
 87                 //ComboSource.Value = value;
 88                 RaisePropertyChanged(nameof(Value));
 89             }
 90         }
 91
 92         [Display(Name = "Password", GroupName = "DefaultUI")]
 93         public string Password { get; set; }
 94
 95         [Display(Name = "TextEdit", GroupName = "DefaultUI")]
 96         public string Text { get; set; }
 97
 98         [Display(Name = "Notes", GroupName = "DefaultUI")]
 99         public string Notes { get; set; }
100
101
102         [Display(Name = "Double", GroupName = "DefaultUI")]
103         [DefaultValue(1)]
104         public double TestDouble { get; set; }
105
106         [Display(Name = "Items", GroupName = "DefaultUI")]
107         [DefaultValue(Visibility.Visible)]
108         public Visibility TestItems { get; set; }
109     }
110
111     public static class DynamicallyAssignDataEditorsMetadata
112     {
113         public static void BuildMetadata(MetadataBuilder<TestPropertyGrid> builder)
114         {
115             builder.Property(x => x.Password)
116                 .PasswordDataType();
117
118             builder.Property(x => x.Notes)
119                 .MultilineTextDataType();
120         }
121     }
122 }

TestPropertyGrid

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

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));}}}
}

SliderUIDataContext

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));}}}
}

ComboBoxEditDataContext

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));}}
}

PropertyDataContext

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)};}
}

ComboBoxEditItemSource

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

 1 <Window
 2     x:Class="PropertyGridDemo.MainWindow"
 3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 5     xmlns:PropertyGridControl="clr-namespace:PropertyGridDemo.PropertyGridControl"
 6     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 7     xmlns:local="clr-namespace:PropertyGridDemo"
 8     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 9     Title="MainWindow"
10     Width="525"
11     Height="350"
12     WindowState="Maximized"
13     mc:Ignorable="d">
14     <Grid Margin="10">
15         <Grid.ColumnDefinitions>
16             <ColumnDefinition Width="259*" />
17             <ColumnDefinition Width="259*" />
18         </Grid.ColumnDefinitions>
19
20         <TextBox
21             x:Name="OutputBox"
22             Grid.ColumnSpan="1"
23             HorizontalScrollBarVisibility="Auto"
24             ScrollViewer.CanContentScroll="True" />
25         <PropertyGridControl:CustomPropertyGrid x:Name="PropertyGrid" Grid.Column="1" />
26     </Grid>
27 </Window>

MainWindow

运行示意图:

以上就是自定义PropertyGrid控件的实现代码,本人只实现了简单的Slider和ComboBoxEdit控件,实际上可以根据自己的需要仿照以上的方法扩展到其他控件,这个就看需求了。

个人感觉以上方案还是有所欠缺,主要是自定义控件的模板是由代码生成的,如果可以直接从资源文件中读取将会更加方便,不过本人尝试了几次并不能成功的实现数据的绑定,如果大家有什么好的解决方案欢迎在评论区留言,也欢迎大家在评论区进行讨论。

以上内容均为原创,转发请注明出处,谢谢!

转载于:https://www.cnblogs.com/LinWorld/p/6876860.html

PropertyGrid自定义控件相关推荐

  1. PropertyGrid中的枚举显示为中文(转)

    本文转载:http://www.cnblogs.com/yank/archive/2011/09/17/2179598.html ropertyGrid中的枚举显示为中文 在系统开发中,经常会使用Pr ...

  2. 开发Eclipse自定义控件

    摘自:http://www.ibm.com/developerworks/cn/opensource/os-eclipcntl/ 我们在开发自定义控件时主要考虑以下问题: 1. 自定义控件的绘制:通常 ...

  3. qt获取当前系统音量值_Qt编写自定义控件50-迷你仪表盘

    一.前言 这个控件取名叫迷你仪表盘,是以为该控件可以缩小到很小很小的区域显示,非常适合小面积区域展示仪表数据使用,还可以手动触摸调节进度,是我个人觉得最漂亮小巧的一个控件.初次看到类似的控件是在一个音 ...

  4. 【iOS】自定义控件入门:可拖动的环形进度

    有时候UIKit的标准控件并不能满足我们的需求,因此我们可以通过自定义控件得到满足我们需求的控件,例如这篇文章将教你如何自定义一个圆形的进度条,并且用户可以通过拖动进度条上的手柄来改变进度值.主要参考 ...

  5. Android自定义控件系列之基础篇

    一.概述 在android开发中很多UI控件往往需要进行定制以满足应用的需要或达到更加的效果,接下来就通过一个系列来介绍自定义控件,这里更多是通过一些案例逐步去学习,本系列有一些典型的应用,掌握好了大 ...

  6. C#自定义控件四简易时钟

    C#自定义控件四简易时钟 效果图: 简易时钟,顾名思义,简单容易,简单到什么程度呢?界面只有数字和指针,甚至连与当前时间都不能匹配!呵呵!就这么简单,学习嘛,从简单开始. 毫无疑问,这里肯定要用到Ti ...

  7. 对做C#自定义控件的一点心得

    近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装这个COM组件,中间遇到很多曲折,研究了一个星期,终于完成了 下面总结一下我做DSOFramer这个自定义控件的注意地方: 1.在创建 ...

  8. ASP.NET自定义控件组件开发 第四章 组合控件开发CompositeControl

    第四章 组合控件开发CompositeControl 大家好,今天我们来实现一个自定义的控件,之前我们已经知道了,要开发自定义的控件一般继承三个基 类:Control,WebControl,还有一个就 ...

  9. Android自定义控件NumberCircleProgressBar(圆形进度条)的实现

    Android自定义控件NumberCircleProgressBar(圆形进度条)的实现

最新文章

  1. python26.dll没有发现_python26.dll 文件下载
  2. 9.28 linux系统基础优化
  3. 计算方法之迭代法求方程根
  4. extjs官网+extjs官网案例
  5. pytorch学习笔记(6):GPU和如何保存加载模型
  6. SpringBoot的自定义配置方法一,通过自定义配置文件
  7. Keras搭建M2Det目标检测平台(转载)
  8. 通过python实现txt中,字母概率的计算,以及信源熵的计算,并且输出
  9. 《尚硅谷最新版JavaScript基础全套教程完整版(140集实战教学,JS从入门到精通)》视频1
  10. 在SQL Sever中使用form membership认证
  11. MySql Lock wait timeout exceeded该如何处理
  12. 英雄联盟修改服务器封3年,LOL自定义也被封三年 竟然因为这个原因?
  13. 如何实现有效的项目进度控制
  14. 温度场有限容积法程序入门之六:后处理.花絮.Contour Plotter and 3D Function Grapher Together - the Applet and the Souce Co
  15. 平面、3D设计软件最全集子(Windows系32、64位)
  16. 计算机初二说课稿,初二数学说课稿
  17. 电脑使用cmd发送邮件——以QQ邮件为例
  18. 如何控制步进电机速度(即,如何计算脉冲频率):
  19. UITextField leftView 光标的边距 监听return键 字符长度的限制 以及其他的常用方法
  20. 渗透测试基础- - -web日志分析

热门文章

  1. 2005年4月全国计算机等级考试二级C语言笔试试题及答案
  2. minicom使用总结
  3. windows cmd color颜色设置
  4. 使用组策略配置Windows 7的高级防火墙
  5. MySQL实时复制成为可能?
  6. 推荐的 PHP 读物列表
  7. Python3 编程第一步
  8. node - 非阻塞的异步 IO
  9. 遗传算法来控制进入(一)
  10. 如何找到文件的家-打开文件对话框openFileDialog