PropertyGrid自定义控件使用详解

 更新时间:2017年06月07日 11:07:15   作者:WCode  
这篇文章主要为大家详细介绍了PropertyGrid自定义控件的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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

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

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控件的实现代码,本人只实现了简单的Slider和ComboBoxEdit控件,实际上可以根据自己的需要仿照以上的方法扩展到其他控件,这个就看需求了。

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • C# 文件下载之断点续传实现代码

    C# 文件下载之断点续传实现代码

    本篇文章主要介绍了C# 文件下载之断点续传实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • 关于C#中使用Oracle存储过程返回结果集的问题

    关于C#中使用Oracle存储过程返回结果集的问题

    Oracle中可以使用游标(Cursor)对数据集进行操作,但在存储过程输出参数中直接使用Cursor错误,下面小编给大家带来了C#中使用Oracle存储过程返回结果集的问题,感兴趣的朋友一起看看吧
    2021-10-10
  • C#求解哈夫曼树,实例代码

    C#求解哈夫曼树,实例代码

    C#求解哈夫曼树,实例代码,需要的朋友可以参考一下
    2013-04-04
  • c#实现汉诺塔问题示例

    c#实现汉诺塔问题示例

    这篇文章主要介绍了c#实现汉诺塔问题示例,需要的朋友可以参考下
    2014-04-04
  • 关于C#连接FTP时路径问题的解决方法

    关于C#连接FTP时路径问题的解决方法

    最近在工作中遇到一个需求,需要利用C#连接FTP,在连接过程中遇到一个问题,所以下面这篇文章主要给大家介绍了关于C#连接FTP时路径问题的解决方法,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-08-08
  • C#虚函数用法实例分析

    C#虚函数用法实例分析

    这篇文章主要介绍了C#虚函数用法,实例分析了C#中虚函数的功能与基本使用技巧,需要的朋友可以参考下
    2015-07-07
  • 通过C#实现发送自定义的html格式邮件

    通过C#实现发送自定义的html格式邮件

    本篇文章主要介绍了通过C#实现发送自定义的html格式邮件,详细的介绍了发送HTML格式邮件的方法,有兴趣的可以了解一下。
    2017-02-02
  • c#冒泡排序算法示例

    c#冒泡排序算法示例

    这篇文章主要介绍了c#冒泡排序算法示例,需要的朋友可以参考下
    2014-04-04
  • WPF基础教程之元素绑定详解

    WPF基础教程之元素绑定详解

    这篇文章主要给大家介绍了关于WPF基础教程之元素绑定的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-01-01
  • C#实现对AES加密和解密的方法

    C#实现对AES加密和解密的方法

    C#实现对AES加密和解密的方法,需要的朋友可以参考一下
    2013-04-04

最新评论