亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

PropertyGrid自定義控件使用詳解

2019-10-29 21:09:56
字體:
來源:轉載
供稿:網友

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"  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控件的實現代碼,本人只實現了簡單的Slider和ComboBoxEdit控件,實際上可以根據自己的需要仿照以上的方法擴展到其他控件,這個就看需求了。

個人感覺以上方案還是有所欠缺,主要是自定義控件的模板是由代碼生成的,如果可以直接從資源文件中讀取將會更加方便,不過本人嘗試了幾次并不能成功的實現數據的綁定,如果大家有什么好的解決方案歡迎在評論區留言,也歡迎大家在評論區進行討論。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到c#教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美又大粗又爽又黄大片视频| 欧美激情在线视频二区| 国产色婷婷国产综合在线理论片a| 国产成人精品视| 亚洲综合在线做性| 亚洲人成网站在线播| 亚洲国产成人爱av在线播放| 国产美女久久久| 伊人伊成久久人综合网小说| 91久热免费在线视频| 色综合色综合久久综合频道88| 91国内在线视频| 欧美激情亚洲自拍| 日韩精品在线电影| 日韩欧美国产骚| www.欧美视频| 欧美性生交xxxxxdddd| 国产黑人绿帽在线第一区| 超碰97人人做人人爱少妇| 亚洲偷熟乱区亚洲香蕉av| 色偷偷噜噜噜亚洲男人的天堂| 57pao精品| 成人激情视频小说免费下载| 亚洲精品网址在线观看| 97热精品视频官网| 久久精品91久久久久久再现| 久久久女人电视剧免费播放下载| 一区二区欧美日韩视频| 国产精品亚洲一区二区三区| 精品视频9999| 久久影视电视剧免费网站| 欧美日本在线视频中文字字幕| 亚洲美女精品久久| 91精品久久久久久久久久| 国产欧美精品一区二区| 成人日韩av在线| 欧美性xxxx极品hd欧美风情| 91超碰caoporn97人人| 欧美资源在线观看| 色yeye香蕉凹凸一区二区av| 国产亚洲精品91在线| 国产不卡一区二区在线播放| 成人看片人aa| 国精产品一区一区三区有限在线| 欧洲美女免费图片一区| 亚洲午夜久久久久久久| 亚洲r级在线观看| www.欧美精品| 亚洲成人久久网| 免费av一区二区| 国产国语videosex另类| 中文字幕自拍vr一区二区三区| 色偷偷噜噜噜亚洲男人的天堂| 九九九热精品免费视频观看网站| 成人免费视频在线观看超级碰| 亚洲精品美女在线| 久久久久中文字幕2018| 国产精品久久婷婷六月丁香| 国产日产久久高清欧美一区| 欧美乱大交xxxxx另类电影| 久久精品电影网| 亚洲色图欧美制服丝袜另类第一页| y97精品国产97久久久久久| 欧美华人在线视频| 精品国产999| 亚洲美女精品成人在线视频| 国产精品99久久99久久久二8| 欧美福利在线观看| 亚洲黄色成人网| 国产精品极品美女粉嫩高清在线| 韩国精品美女www爽爽爽视频| 在线精品高清中文字幕| 国产精品美女www| 国产精品自拍小视频| 国产91在线高潮白浆在线观看| 亚洲国产中文字幕久久网| 欧美在线www| 亚洲成**性毛茸茸| 欧美精品一区在线播放| 人人爽久久涩噜噜噜网站| 日韩精品久久久久| 亚州av一区二区| 国产一区视频在线| 亚洲色图美腿丝袜| 国产亚洲a∨片在线观看| 国产午夜一区二区| 8x海外华人永久免费日韩内陆视频| 狠狠色狠狠色综合日日五| 亚洲高清av在线| 欧美性生交大片免网| 国模精品一区二区三区色天香| 久久精品视频99| 午夜精品久久久久久99热软件| 成人免费在线网址| 欧美日韩激情美女| 亚洲精品一区中文字幕乱码| 成人免费午夜电影| 亚洲人成电影网站色| 欧美性猛交xxxx富婆弯腰| 亚洲精品一区中文| 亚洲女人天堂网| 久久久久久成人精品| 国产一区二区在线免费| 精品久久久久久中文字幕大豆网| 日韩在线观看免费网站| 久久在线免费观看视频| 日韩欧美成人区| 久久久国产精品免费| 成人激情视频网| 大伊人狠狠躁夜夜躁av一区| 日韩中文字幕免费看| 97香蕉超级碰碰久久免费软件| 国产精品久久久久77777| 福利微拍一区二区| 亚洲精品天天看| 亚洲国产欧美一区二区丝袜黑人| 欧美性xxxx极品hd欧美风情| 狠狠爱在线视频一区| 91夜夜揉人人捏人人添红杏| 大荫蒂欧美视频另类xxxx| 国产精品成人一区| 国产精品视频免费在线| 97免费视频在线| 日韩网站免费观看| 精品视频在线导航| 久久伊人精品一区二区三区| 91超碰中文字幕久久精品| 成人美女免费网站视频| 亚洲欧洲在线视频| 国产精品久久久久久久久久东京| 国产精品成人免费电影| 国产精品99久久久久久久久久久久| 日韩av电影手机在线观看| 国产剧情日韩欧美| 亚洲欧美国产精品va在线观看| 亚洲娇小xxxx欧美娇小| 日韩国产一区三区| 久久在线免费视频| 欧美性理论片在线观看片免费| 欧美午夜视频在线观看| 日韩欧美高清在线视频| 亚洲影影院av| 欧美激情精品久久久久久蜜臀| 久久久久久久久久亚洲| 国产成人精品视频在线| 国产精品亚洲片夜色在线| 性色av一区二区咪爱| 欧美日韩国产一区在线| 欧美做受高潮电影o| 日韩欧美大尺度| 韩国国内大量揄拍精品视频| 日韩在线观看成人| 视频在线观看一区二区| 爽爽爽爽爽爽爽成人免费观看| 亚洲国产精品电影| 姬川优奈aav一区二区| 97久久久免费福利网址| 国产欧美精品日韩精品| 在线观看国产精品淫| 亚洲国产欧美一区二区丝袜黑人| 97在线观看视频国产| 精品久久久久久久久久| 亚洲人成绝费网站色www| 2018国产精品视频|