PropertyGrid自定義控件使用詳解
PropertyGrid是一個(gè)很強(qiáng)大的控件,使用該控件做屬性設(shè)置面板的一個(gè)好處就是你只需要專注于代碼而無需關(guān)注UI的呈現(xiàn),PropertyGrid會(huì)默認(rèn)根據(jù)變量類型選擇合適的控件顯示。但是這也帶來了一個(gè)問題,就是控件的使用變得不是特別靈活,主要表現(xiàn)在你無法根據(jù)你的需求很好的選擇控件,比如當(dāng)你需要用Slider控件來設(shè)置int型變量時(shí),PropertyGrid默認(rèn)的模板選擇器是不支持的。網(wǎng)上找了許多資料基本都是介紹WinForm的實(shí)現(xiàn)方式,主要用到了IWindowFromService這個(gè)接口,并未找到合適的適合WPF的Demo,后來在參考了DEVExpress的官方Demo之后我做了一個(gè)基于WPF和DEV 16.2的PropertyGrid Demo,基本實(shí)現(xiàn)了上述功能。
為了實(shí)現(xiàn)這一點(diǎn),需要自定義一個(gè)DataTemplateSeletor類,這也是本文的核心代碼。
1.創(chuàng)建一個(gè)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:定義屬性描述的風(fēng)格模板 -->
<!-- PropertyDefinitionTemplateSelector:定義一個(gè)模板選擇器,對(duì)應(yīng)一個(gè)繼承自DataTemplateSelector的類 -->
<!-- PropertyDefinitionsSource:定義一個(gè)獲取數(shù)據(jù)屬性集合的類,對(duì)應(yīng)一個(gè)自定義類(本Demo中對(duì)應(yīng)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>
<!-- 設(shè)置控件的全局樣式和數(shù)據(jù)綁定 -->
<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>
<!-- 測(cè)試直接從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.編寫對(duì)應(yīng)的模板選擇類 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>
/// 當(dāng)重寫在派生類中,返回根據(jù)自定義邏輯的 <see cref="T:System.Windows.DataTemplate" /> 。
/// </summary>
/// <param name="item">數(shù)據(jù)對(duì)象可以選擇模板。</param>
/// <param name="container">數(shù)據(jù)對(duì)象。</param>
/// <returns>
/// 返回 <see cref="T:System.Windows.DataTemplate" /> 或 null。默認(rèn)值為 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>
///初始化所有屬性并調(diào)用模板選擇器進(jìn)行匹配
/// </summary>
public class DataEditorsViewModel
{
public IEnumerable<PropertyDescriptor> Properties { get { return TypeDescriptor.GetProperties(typeof(TestPropertyGrid)).Cast<PropertyDescriptor>(); } }
}
}
3.編寫一個(gè)可用于構(gòu)建模板的屬性 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>
/// 自定義控件屬性構(gòu)造函數(shù)
/// </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.編寫對(duì)應(yīng)的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();
}
}
}
該類中用到的其他類主要有以下幾個(gè),以下幾個(gè)類主要用于數(shù)據(jù)綁定:
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>
/// 在更改屬性值時(shí)發(fā)生。
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 觸發(fā)屬性變化
/// </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丟進(jìn)容器中即可,這里我直接用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>
運(yùn)行示意圖:
以上就是自定義PropertyGrid控件的實(shí)現(xiàn)代碼,本人只實(shí)現(xiàn)了簡(jiǎn)單的Slider和ComboBoxEdit控件,實(shí)際上可以根據(jù)自己的需要仿照以上的方法擴(kuò)展到其他控件,這個(gè)就看需求了。
個(gè)人感覺以上方案還是有所欠缺,主要是自定義控件的模板是由代碼生成的,如果可以直接從資源文件中讀取將會(huì)更加方便,不過本人嘗試了幾次并不能成功的實(shí)現(xiàn)數(shù)據(jù)的綁定,如果大家有什么好的解決方案歡迎在評(píng)論區(qū)留言,也歡迎大家在評(píng)論區(qū)進(jìn)行討論。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
欄 目:C#教程
下一篇:C# 通過 oledb 操作Excel實(shí)例代碼
本文標(biāo)題:PropertyGrid自定義控件使用詳解
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/5697.html
您可能感興趣的文章
- 01-10C#自定義簽名章實(shí)現(xiàn)方法
- 01-10WinForm實(shí)現(xiàn)自定義右下角提示效果的方法
- 01-10C#實(shí)現(xiàn)自定義windows系統(tǒng)日志的方法
- 01-10C#自定義事件監(jiān)聽實(shí)現(xiàn)方法
- 01-10C#編程實(shí)現(xiàn)自定義熱鍵的方法
- 01-10C#實(shí)現(xiàn)ComboBox控件顯示出多個(gè)數(shù)據(jù)源屬性的方法
- 01-10C#實(shí)現(xiàn)讀取DataSet數(shù)據(jù)并顯示在ListView控件中的方法
- 01-10超炫酷的WPF實(shí)現(xiàn)Loading控件效果
- 01-10輕松學(xué)習(xí)C#的方法
- 01-10C#自定義DataGridViewColumn顯示TreeView


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法實(shí)例總結(jié)
- 04-02jquery與jsp,用jquery
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?


