温馨提示×

温馨提示×

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

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

如何在PropertyGrid中自定义控件

发布时间:2021-03-30 16:51:41 来源:亿速云 阅读:343 作者: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