propertygrid是一个很强大的控件,使用该控件做属性设置面板的一个好处就是你只需要专注于代码而无需关注ui的呈现,propertygrid会默认根据变量类型选择合适的控件显示。但是这也带来了一个问题,就是控件的使用变得不是特别灵活,主要表现在你无法根据你的需求很好的选择控件,比如当你需要用slider控件来设置int型变量时,propertygrid默认的模板选择器是不支持的。网上找了许多资料基本都是介绍winform的实现方式,主要用到了iwindowfromservice这个接口,并未找到合适的适合wpf的demo,后来在参考了devexpress的官方demo之后我做了一个基于wpf和dev 16.2的propertygrid demo,基本实现了上述功能。
为了实现这一点,需要自定义一个DataTemplateSeletor类,这也是本文的核心代码。
1.创建一个CustomPropertyGrid自定义控件:
1 12 13 14 15 16 17 18 19 20 21 22 23 24 34 35
登录后复制CustomPropertyGrid
该控件使用的资源字典如下:
1 11 12 13 14 15 16 25 26 27 36 37 38 39 40 41 <!---->42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 <!--62 -->63 <!---->64 65 69 70 71 <!---->72 <!--73 -->74 75 76 77 82 83
登录后复制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 ///18 /// 当重写在派生类中,返回根据自定义逻辑的 。 19 /// 20 /// 数据对象可以选择模板。 21 /// 数据对象。 22 /// 23 /// 返回 或 null。默认值为 null。 24 /// 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 ///33 /// Tries the create resource. 34 /// 35 /// The item. 36 /// 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 ///49 /// Gets the data context. 50 /// 51 /// Name of the data context property. 52 /// 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 ///61 /// Creates the slider data template. 62 /// 63 /// The custom UI attribute. 64 /// 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 ///88 /// Creates the ComboBox edit template. 89 /// 90 /// The custom UI attribute. 91 /// 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 ///113 /// Creates the property definition template.114 /// 115 /// The custom UI attribute.116 /// 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 else140 {141 return null;142 }143 return dataTemplate;144 }145 }146 }
登录后复制DynamicallyAssignDataEditorsTemplateSelector
using System.Collections.Generic;using System.ComponentModel;using System.Linq;namespace PropertyGridDemo.PropertyGridControl{//////初始化所有属性并调用模板选择器进行匹配/// public class DataEditorsViewModel {public IEnumerable Properties { get { return TypeDescriptor.GetProperties(typeof(TestPropertyGrid)).Cast(); } } }}
登录后复制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; }////// 自定义控件属性构造函数/// /// The UI types./// Name of the data context property.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 DynamicallyAssignDataEditorsMetadata112 {113 public static void BuildMetadata(MetadataBuilder 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() { }////// Initializes a new instance of the class./// /// The minimum./// The maximum./// The value./// The small change./// The large change.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; }////// Gets or sets the small change./// /// /// The small change./// public double SmallChange {get => _smallChange;set{if (value == _min) return; _min = value; RaisePropertyChanged(nameof(SmallChange)); } }////// Gets or sets the large change./// /// /// The large change./// public double LargeChange {get => _largeChange;set{if (Value == _largeChange) return; _largeChange = value; RaisePropertyChanged(nameof(LargeChange)); } }////// Gets or sets the maximum./// /// /// The maximum./// public double Max {get => _max;set{if (value == _max) return; _max = value; RaisePropertyChanged(nameof(Max)); } }////// Gets or sets the minimum./// /// /// The minimum./// public double Min {get => _min;set{if (value == _min) return; _min = value; RaisePropertyChanged(nameof(Min)); } }////// Gets or sets the value./// /// /// The 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[] _itemSource;private Tuple _editValue;private int _selectedIndex;////// Initializes a new instance of the class./// /// The item source./// The edit value.public ComboBoxEditDataContext(Tuple[] itemSource,Tuple editValue) { _itemSource = itemSource; _editValue = _itemSource.FirstOrDefault(x => x?.Item1.ToString() == editValue?.Item1.ToString() && x?.Item2?.ToString() == x?.Item2?.ToString()); }////// Initializes a new instance of the class./// /// The item source./// The value.public ComboBoxEditDataContext(Tuple[] itemSource, object value) { _itemSource = itemSource; _editValue = _itemSource.FirstOrDefault(x => x?.Item2.ToString() == value.ToString() ); }public string Name {get;set; }////// Gets or sets the item source./// /// /// The item source./// public Tuple[] ItemSource {get => _itemSource;set{//if (_itemSource == value) return;_itemSource = value; RaisePropertyChanged(nameof(ItemSource)); } }////// Gets or sets the edit value./// /// /// The edit value./// public Tuple 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)); } }////// Gets or sets the index of the selected./// /// /// The index of the selected./// 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 {////// 在更改属性值时发生。/// public event PropertyChangedEventHandler PropertyChanged;////// 触发属性变化/// /// public virtual void RaisePropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }}
using System;namespace PropertyGridDemo.PropertyGridControl{internal static class ComboBoxEditItemSource {internal static Tuple[] TestItemSource = new Tuple[] {new Tuple("1",1),new Tuple("2",2),new Tuple("3",3) }; }}
登录后复制ComboBoxEditItemSource
5.将以上的CustomPropertyGrid丢进容器中即可,这里我直接用Mainwindow来演示:
1 14 15 16 17 18 19 20 25 26 27
登录后复制MainWindow
运行示意图:
以上就是自定义PropertyGrid控件的实现代码,本人只实现了简单的Slider和ComboBoxEdit控件,实际上可以根据自己的需要仿照以上的方法扩展到其他控件,这个就看需求了。
个人感觉以上方案还是有所欠缺,主要是自定义控件的模板是由代码生成的,如果可以直接从资源文件中读取将会更加方便,不过本人尝试了几次并不能成功的实现数据的绑定,如果大家有什么好的解决方案欢迎在评论区留言,也欢迎大家在评论区进行讨论。
以上内容均为原创,转发请注明出处,谢谢!
以上就是一个很强大的控件–PropertyGrid的详细内容,更多请关注【创想鸟】其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。
发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2439974.html