Maison développement back-end Tutoriel C#.Net Un contrôle-PropertyGrid très puissant

Un contrôle-PropertyGrid très puissant

Jun 24, 2017 am 09:49 AM
控件 自定义

PropertyGrid est un contrôle très puissant. L'un des avantages de l'utilisation de ce contrôle comme panneau de configuration des propriétés est que vous devez uniquement vous concentrer sur le code et non sur la présentation de l'interface utilisateur. Le PropertyGrid sélectionnera le contrôle approprié à afficher en fonction. sur le type de variable par défaut. Mais cela pose également un problème, c'est-à-dire que l'utilisation du contrôle n'est pas particulièrement flexible, principalement parce que vous ne pouvez pas choisir le contrôle en fonction de vos besoins. Par exemple, lorsque vous devez utiliser le contrôle Slider pour définir une variable int, la valeur par défaut de PropertyGrid est Le sélecteur de modèle n'est pas pris en charge. J'ai trouvé beaucoup d'informations sur Internet qui présentaient essentiellement l'implémentation de WinForm, principalement en utilisant l'interface IWindowFromService. Je n'ai pas trouvé de démo adaptée à WPF. Plus tard, après avoir consulté la démo officielle de DEVExpress, j'ai fait une démo basée. sur WPF et DEV 16.2. PropertyGrid Demo implémente essentiellement les fonctions ci-dessus.

Pour y parvenir, vous devez personnaliser une classe DataTemplateSeletor, qui est également le code principal de cet article.

1. Créez un contrôle personnalisé CustomPropertyGrid :

 1 <UserControl 2     x:Class="PropertyGridDemo.PropertyGridControl.CustomPropertyGrid" 3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6     xmlns:dxprg="http://schemas.devexpress.com/winfx/2008/xaml/propertygrid" 7     xmlns:local="clr-namespace:PropertyGridDemo.PropertyGridControl" 8     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 9     d:DesignHeight="300"10     d:DesignWidth="300"11     mc:Ignorable="d">12     <UserControl.Resources>13         <ResourceDictionary>14             <ResourceDictionary.MergedDictionaries>15                 <!--  资源字典  -->16                 <ResourceDictionary Source="../PropertyGridControl/DynamicallyAssignDataEditorsResources.xaml" />17             </ResourceDictionary.MergedDictionaries>18         </ResourceDictionary>19     </UserControl.Resources>20     <Grid>21         <!--  PropertyDefinitionStyle:定义属性描述的风格模板  -->22         <!--  PropertyDefinitionTemplateSelector:定义一个模板选择器,对应一个继承自DataTemplateSelector的类  -->23         <!--  PropertyDefinitionsSource:定义一个获取数据属性集合的类,对应一个自定义类(本Demo中对应DataEditorsViewModel)  -->24         <dxprg:PropertyGridControl25             x:Name="PropertyGridControl"26             Margin="24"27             DataContextChanged="PropertyGridControl_DataContextChanged"28             ExpandCategoriesWhenSelectedObjectChanged="True"29             PropertyDefinitionStyle="{StaticResource DynamicallyAssignDataEditorsPropertyDefinitionStyle}"30             PropertyDefinitionTemplateSelector="{StaticResource DynamicallyAssignDataEditorsTemplateSelector}"31             PropertyDefinitionsSource="{Binding Path=Properties, Source={StaticResource DemoDataProvider}}"32             ShowCategories="True"33             ShowDescriptionIn="Panel" />34     </Grid>35 </UserControl>
Copier après la connexion
CustomPropertyGrid

Le Le Le dictionnaire de ressources utilisé par le contrôle est le suivant :

 1 <ResourceDictionary 2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5     xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors" 6     xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid" 7     xmlns:dxprg="http://schemas.devexpress.com/winfx/2008/xaml/propertygrid" 8     xmlns:local="clr-namespace:PropertyGridDemo.PropertyGridControl" 9     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"10     mc:Ignorable="d">11 12     <local:DynamicallyAssignDataEditorsTemplateSelector x:Key="DynamicallyAssignDataEditorsTemplateSelector" />13     <local:DataEditorsViewModel x:Key="DemoDataProvider" />14 15     <DataTemplate x:Key="DescriptionTemplate">16         <RichTextBox17             x:Name="descriptionRichTextBox"18             MinWidth="150"19             HorizontalContentAlignment="Stretch"20             Background="Transparent"21             BorderThickness="0"22             Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource TemplatedParent}}"23             IsReadOnly="True"24             IsTabStop="False" />25     </DataTemplate>26     <DataTemplate x:Key="descriptionTemplate">27         <RichTextBox28             x:Name="descriptionRichTextBox"29             MinWidth="150"30             HorizontalContentAlignment="Stretch"31             Background="Transparent"32             BorderThickness="0"33             Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource TemplatedParent}}"34             IsReadOnly="True"35             IsTabStop="False" />36     </DataTemplate>37 38     <!--  设置控件的全局样式和数据绑定  -->39     <Style x:Key="DynamicallyAssignDataEditorsPropertyDefinitionStyle" TargetType="dxprg:PropertyDefinition">40         <Setter Property="Path" Value="{Binding Name}" />41         <!--<Setter Property="Header" Value="{Binding Converter={StaticResource PropertyDescriptorToDisplayNameConverter}}"/>-->42         <Setter Property="Description" Value="{Binding}" />43         <Setter Property="DescriptionTemplate" Value="{StaticResource descriptionTemplate}" />44     </Style>45     <Style x:Key="DescriptionContainerStyle" TargetType="dxprg:PropertyDescriptionPresenterControl">46         <Setter Property="ShowSelectedRowHeader" Value="False" />47         <Setter Property="MinHeight" Value="70" />48     </Style>49 50     <Style TargetType="Slider">51         <Setter Property="Margin" Value="2" />52     </Style>53     <Style TargetType="dxe:ComboBoxEdit">54         <Setter Property="IsTextEditable" Value="False" />55         <Setter Property="ApplyItemTemplateToSelectedItem" Value="True" />56         <Setter Property="Margin" Value="2" />57     </Style>58 59     <!--  测试直接从DataTemplate获取控件  -->60     <DataTemplate x:Key="SliderTemplate" DataType="local:SliderExtend">61         <!--<dxprg:PropertyDefinition>62             <dxprg:PropertyDefinition.CellTemplate>-->63         <!--<DataTemplate>-->64         <StackPanel x:Name="Root">65             <Slider66                 Maximum="{Binding Path=Max}"67                 Minimum="{Binding Path=Min}"68                 Value="{Binding Path=Value}" />69             <TextBlock Text="{Binding Path=Value}" />70         </StackPanel>71         <!--</DataTemplate>-->72         <!--</dxprg:PropertyDefinition.CellTemplate>73         </dxprg:PropertyDefinition>-->74     </DataTemplate>75 76     <DataTemplate x:Key="ComboBoxEditItemTemplate" DataType="Tuple">77         <TextBlock78             Height="20"79             Margin="5,3,0,0"80             VerticalAlignment="Center"81             Text="{Binding Item1}" />82     </DataTemplate>83 </ResourceDictionary>
Copier après la connexion
ResourceDictionary

2. Écrivez le modèle correspondant classe de sélection DynamicallyAssignDataEditorsTemplateSelector :

  1 using DevExpress.Xpf.Editors;  2 using DevExpress.Xpf.PropertyGrid;  3 using System.ComponentModel;  4 using System.Reflection;  5 using System.Windows;  6 using System.Windows.Controls;  7 using System.Windows.Data;  8   9 namespace PropertyGridDemo.PropertyGridControl 10 { 11     public class DynamicallyAssignDataEditorsTemplateSelector : DataTemplateSelector 12     { 13         private PropertyDescriptor _property = null; 14         private RootPropertyDefinition _element = null; 15         private PropertyDataContext _propertyDataContext => App.PropertyGridDataContext; 16  17         /// <summary> 18         /// 当重写在派生类中,返回根据自定义逻辑的 <see cref="T:System.Windows.DataTemplate" /> 。 19         /// </summary> 20         /// <param name="item">数据对象可以选择模板。</param> 21         /// <param name="container">数据对象。</param> 22         /// <returns> 23         /// 返回 <see cref="T:System.Windows.DataTemplate" /> 或 null。默认值为 null。 24         /// </returns> 25         public override DataTemplate SelectTemplate(object item, DependencyObject container) 26         { 27             _element = (RootPropertyDefinition)container; 28             DataTemplate resource = TryCreateResource(item); 29             return resource ?? base.SelectTemplate(item, container); 30         } 31  32         /// <summary> 33         /// Tries the create resource. 34         /// </summary> 35         /// <param name="item">The item.</param> 36         /// <returns></returns> 37         private DataTemplate TryCreateResource(object item) 38         { 39             if (!(item is PropertyDescriptor)) return null; 40             PropertyDescriptor pd = (PropertyDescriptor)item; 41             _property = pd; 42             var customUIAttribute = (CustomUIAttribute)pd.Attributes[typeof(CustomUIAttribute)]; 43             if (customUIAttribute == null) return null; 44             var customUIType = customUIAttribute.CustomUI; 45             return CreatePropertyDefinitionTemplate(customUIAttribute); 46         } 47  48         /// <summary> 49         /// Gets the data context. 50         /// </summary> 51         /// <param name="dataContextPropertyName">Name of the data context property.</param> 52         /// <returns></returns> 53         private object GetDataContext(string dataContextPropertyName) 54         { 55             PropertyInfo property = _propertyDataContext?.GetType().GetProperty(dataContextPropertyName); 56             if (property == null) return null; 57             return property.GetValue(_propertyDataContext, null); 58         } 59  60         /// <summary> 61         /// Creates the slider data template. 62         /// </summary> 63         /// <param name="customUIAttribute">The custom UI attribute.</param> 64         /// <returns></returns> 65         private DataTemplate CreateSliderDataTemplate(CustomUIAttribute customUIAttribute) 66         { 67             DataTemplate ct = new DataTemplate(); 68             ct.VisualTree = new FrameworkElementFactory(typeof(StackPanel)); 69             ct.VisualTree.SetValue(StackPanel.DataContextProperty, GetDataContext(customUIAttribute.DataContextPropertyName)); 70  71             FrameworkElementFactory sliderFactory = new FrameworkElementFactory(typeof(Slider)); 72             sliderFactory.SetBinding(Slider.MaximumProperty, new Binding(nameof(SliderUIDataContext.Max))); 73             sliderFactory.SetBinding(Slider.MinimumProperty, new Binding(nameof(SliderUIDataContext.Min))); 74             sliderFactory.SetBinding(Slider.SmallChangeProperty, new Binding(nameof(SliderUIDataContext.SmallChange))); 75             sliderFactory.SetBinding(Slider.LargeChangeProperty, new Binding(nameof(SliderUIDataContext.LargeChange))); 76             sliderFactory.SetBinding(Slider.ValueProperty, new Binding(nameof(SliderUIDataContext.Value))); 77             ct.VisualTree.AppendChild(sliderFactory); 78  79             FrameworkElementFactory textFacotry = new FrameworkElementFactory(typeof(TextBlock), "TextBlock"); 80             textFacotry.SetValue(TextBlock.TextProperty, new Binding(nameof(SliderUIDataContext.Value))); 81             //textBoxFactory.AddHandler(TextBox.IsVisibleChanged, new DependencyPropertyChangedEventHandler(SearchBoxVisibleChanged)); 82             ct.VisualTree.AppendChild(textFacotry); 83             ct.Seal(); 84             return ct; 85         } 86  87         /// <summary> 88         /// Creates the ComboBox edit template. 89         /// </summary> 90         /// <param name="customUIAttribute">The custom UI attribute.</param> 91         /// <returns></returns> 92         private DataTemplate CreateComboBoxEditTemplate(CustomUIAttribute customUIAttribute) 93         { 94             DataTemplate template = new DataTemplate(); 95             template.VisualTree = new FrameworkElementFactory(typeof(DockPanel)); 96             template.VisualTree.SetValue(DockPanel.DataContextProperty, GetDataContext(customUIAttribute.DataContextPropertyName)); 97  98             FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock)) ; 99             textFactory.SetValue(TextBlock.TextProperty, new Binding(nameof(ComboBoxEditDataContext.Name)));100             template.VisualTree.AppendChild(textFactory);101 102             FrameworkElementFactory comboBoxEditFactory = new FrameworkElementFactory(typeof(ComboBoxEdit));103             comboBoxEditFactory.SetBinding(ComboBoxEdit.ItemsSourceProperty, new Binding(nameof(ComboBoxEditDataContext.ItemSource)));104             comboBoxEditFactory.SetBinding(ComboBoxEdit.EditValueProperty, new Binding(nameof(ComboBoxEditDataContext.EditValue)));105             comboBoxEditFactory.SetBinding(ComboBoxEdit.SelectedIndexProperty, new Binding(nameof(ComboBoxEditDataContext.SelectedIndex)));106             comboBoxEditFactory.SetValue(ComboBoxEdit.ItemTemplateProperty, (DataTemplate)_element.TryFindResource("ComboBoxEditItemTemplate"));107             template.VisualTree.AppendChild(comboBoxEditFactory);108             template.Seal();109             return template;110         }111 112         /// <summary>113         /// Creates the property definition template.114         /// </summary>115         /// <param name="customUIAttribute">The custom UI attribute.</param>116         /// <returns></returns>117         private DataTemplate CreatePropertyDefinitionTemplate(CustomUIAttribute customUIAttribute)118         {119             DataTemplate dataTemplate = new DataTemplate();120             DataTemplate cellTemplate = null;//单元格模板121             FrameworkElementFactory factory = new FrameworkElementFactory(typeof(PropertyDefinition));122             dataTemplate.VisualTree = factory;123             switch (customUIAttribute.CustomUI)124             {125                 case CustomUITypes.Slider:126                     cellTemplate = CreateSliderDataTemplate(customUIAttribute); break;127                     //cellTemplate = (DataTemplate)_element.TryFindResource("SliderTemplate");break;128                 case CustomUITypes.ComboBoxEit:129                     cellTemplate = CreateComboBoxEditTemplate(customUIAttribute);break;130                 131             }132 133             if (cellTemplate != null)134             {135                 factory.SetValue(PropertyDefinition.CellTemplateProperty, cellTemplate);136                 dataTemplate.Seal();137 138             }139             else140             {141                 return null;142             }143             return dataTemplate;144         }145     }146 }
Copier après la connexion
DynamicallyAssignDataEditorsTemplateSelector
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>(); } }
    }
}
Copier après la connexion
DataEditorsViewModel

3. Écrivez une propriété CustomUIType qui peut être utilisée pour créer des modèles :

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;
        }
    }

}
Copier après la connexion
CustomUIType

4. Écrivez la classe DataContext correspondante TestPropertyGrid :

  1 using DevExpress.Mvvm.DataAnnotations;  2 using System;  3 using System.ComponentModel;  4 using System.ComponentModel.DataAnnotations;  5 using System.Timers;  6 using System.Windows;  7   8 namespace PropertyGridDemo.PropertyGridControl  9 { 10     [MetadataType(typeof(DynamicallyAssignDataEditorsMetadata))] 11     public class TestPropertyGrid : PropertyDataContext 12     { 13         private double _count = 0; 14         private SliderUIDataContext _countSource = null; 15         private ComboBoxEditDataContext _comboSource = null; 16         private double _value=1; 17  18         public TestPropertyGrid() 19         { 20             Password = "1111111"; 21             Notes = "Hello"; 22             Text = "Hello hi"; 23         } 24  25         [Browsable(false)] 26         public SliderUIDataContext CountSource 27         { 28             get 29             { 30                 if (_countSource != null) 31                 { 32  33                     return _countSource; 34                 } 35                 else 36                 { 37                     _countSource = new SliderUIDataContext(0, 100, Count, 0.1, 1); 38                     _countSource.PropertyChanged += (object o, PropertyChangedEventArgs e) => 39                     { 40                         this.Count = _countSource.Value; 41                     }; 42                     return _countSource; 43                 } 44             } 45         } 46  47         [Browsable(false)] 48         public ComboBoxEditDataContext ComboSource 49         { 50             get 51             { 52                 if(_comboSource==null) 53                 { 54                     _comboSource =new ComboBoxEditDataContext(ComboBoxEditItemSource.TestItemSource,Value); 55                     _comboSource.PropertyChanged += (object o, PropertyChangedEventArgs e) => 56                       { 57                           this.Value =Convert.ToDouble(_comboSource.EditValue.Item2); 
 58                       }; 59                     60                 } 61                 return _comboSource; 62             } 63         } 64  65         [Display(Name = "SliderEdit", GroupName = "CustomUI")] 66         [CustomUI(CustomUITypes.Slider, nameof(CountSource))] 67         public double Count 68         { 69             get => _count; 70             set 71             { 72                 _count = value; 73                 CountSource.Value = value; 
 74                 RaisePropertyChanged(nameof(Count)); 75             } 76         } 77  78         [Display(Name = "ComboBoxEditItem", GroupName = "CustomUI")] 79         [CustomUI(CustomUITypes.ComboBoxEit, nameof(ComboSource))] 80         public double Value 81         { 82             get => _value; 83             set 84             { 85                 if (_value == value) return; 86                 _value = value; 87                 //ComboSource.Value = value; 88                 RaisePropertyChanged(nameof(Value)); 89             } 90         } 91  92         [Display(Name = "Password", GroupName = "DefaultUI")] 93         public string Password { get; set; } 94  95         [Display(Name = "TextEdit", GroupName = "DefaultUI")] 96         public string Text { get; set; } 97  98         [Display(Name = "Notes", GroupName = "DefaultUI")] 99         public string Notes { get; set; }100 101 102         [Display(Name = "Double", GroupName = "DefaultUI")]103         [DefaultValue(1)]104         public double TestDouble { get; set; }105 106         [Display(Name = "Items", GroupName = "DefaultUI")]107         [DefaultValue(Visibility.Visible)]108         public Visibility TestItems { get; set; }109     }110 111     public static class DynamicallyAssignDataEditorsMetadata112     {113         public static void BuildMetadata(MetadataBuilder<TestPropertyGrid> builder)114         {115             builder.Property(x => x.Password)116                 .PasswordDataType();117 118             builder.Property(x => x.Notes)119                 .MultilineTextDataType();120         }121     }122 }
Copier après la connexion
TestPropertyGrid

Les autres classes utilisées dans cette classe sont principalement les suivantes. Les classes suivantes sont principalement utilisées pour la liaison de données :

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));
            }
        }
    }
}
Copier après la connexion
<.>SliderUIDataContext
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));
            }
        }
    }
}
Copier après la connexion
ComboBoxEditDataContext
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));
        }
    }
}
Copier après la connexion
PropertyDataContext
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)
        };
    }
}
Copier après la connexion
ComboBoxEditItemSource

5.将以上的CustomPropertyGrid丢进容器中即可,这里我直接用Mainwindow来演示:

 1 <Window 2     x:Class="PropertyGridDemo.MainWindow" 3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5     xmlns:PropertyGridControl="clr-namespace:PropertyGridDemo.PropertyGridControl" 6     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 7     xmlns:local="clr-namespace:PropertyGridDemo" 8     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 9     Title="MainWindow"10     Width="525"11     Height="350"12     WindowState="Maximized"13     mc:Ignorable="d">14     <Grid Margin="10">15         <Grid.ColumnDefinitions>16             <ColumnDefinition Width="259*" />17             <ColumnDefinition Width="259*" />18         </Grid.ColumnDefinitions>19 20         <TextBox21             x:Name="OutputBox"22             Grid.ColumnSpan="1"23             HorizontalScrollBarVisibility="Auto"24             ScrollViewer.CanContentScroll="True" />25         <PropertyGridControl:CustomPropertyGrid x:Name="PropertyGrid" Grid.Column="1" />26     </Grid>27 </Window>
Copier après la connexion
MainWindow

 

运行示意图:

以上就是自定义PropertyGrid控件的实现代码,本人只实现了简单的Slider和ComboBoxEdit控件,实际上可以根据自己的需要仿照以上的方法扩展到其他控件,这个就看需求了。

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

以上内容均为原创,转发请注明出处,谢谢!

 

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Comment réparer l'audio si vous n'entendez personne
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Comment déverrouiller tout dans Myrise
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Comment configurer rapidement un avatar personnalisé dans Netflix Comment configurer rapidement un avatar personnalisé dans Netflix Feb 19, 2024 pm 06:33 PM

Un avatar sur Netflix est une représentation visuelle de votre identité de streaming. Les utilisateurs peuvent aller au-delà de l'avatar par défaut pour exprimer leur personnalité. Continuez à lire cet article pour savoir comment définir une photo de profil personnalisée dans l'application Netflix. Comment définir rapidement un avatar personnalisé dans Netflix Dans Netflix, il n'y a pas de fonctionnalité intégrée pour définir une photo de profil. Cependant, vous pouvez le faire en installant l'extension Netflix sur votre navigateur. Tout d’abord, installez une photo de profil personnalisée pour l’extension Netflix sur votre navigateur. Vous pouvez l'acheter dans la boutique Chrome. Après avoir installé l'extension, ouvrez Netflix sur votre navigateur et connectez-vous à votre compte. Accédez à votre profil dans le coin supérieur droit et cliquez sur

Comment personnaliser l'image d'arrière-plan dans Win11 Comment personnaliser l'image d'arrière-plan dans Win11 Jun 30, 2023 pm 08:45 PM

Comment personnaliser l’image d’arrière-plan dans Win11 ? Dans le nouveau système win11, il existe de nombreuses fonctions personnalisées, mais de nombreux amis ne savent pas comment utiliser ces fonctions. Certains amis pensent que l'image d'arrière-plan est relativement monotone et souhaitent personnaliser l'image d'arrière-plan, mais ne savent pas comment personnaliser l'image d'arrière-plan. Si vous ne savez pas comment définir l'image d'arrière-plan, l'éditeur a compilé les étapes pour. personnalisez l'image d'arrière-plan dans Win11 ci-dessous. Si vous êtes intéressé, jetez un œil ci-dessous ! Étapes de personnalisation des images d'arrière-plan dans Win11 : 1. Cliquez sur le bouton Win sur le bureau et cliquez sur Paramètres dans le menu contextuel, comme indiqué sur la figure. 2. Entrez dans le menu des paramètres et cliquez sur Personnalisation, comme indiqué sur la figure. 3. Entrez Personnalisation et cliquez sur Arrière-plan, comme indiqué sur l'image. 4. Entrez les paramètres d'arrière-plan et cliquez pour parcourir les images

Comment créer et personnaliser des diagrammes de Venn en Python ? Comment créer et personnaliser des diagrammes de Venn en Python ? Sep 14, 2023 pm 02:37 PM

Un diagramme de Venn est un diagramme utilisé pour représenter les relations entre des ensembles. Pour créer un diagramme de Venn, nous utiliserons matplotlib. Matplotlib est une bibliothèque de visualisation de données couramment utilisée en Python pour créer des tableaux et des graphiques interactifs. Il est également utilisé pour créer des images et des graphiques interactifs. Matplotlib fournit de nombreuses fonctions pour personnaliser les tableaux et graphiques. Dans ce didacticiel, nous illustrerons trois exemples pour personnaliser les diagrammes de Venn. La traduction chinoise de l'exemple est : Exemple Il s'agit d'un exemple simple de création de l'intersection de deux diagrammes de Venn. Nous avons d'abord importé les bibliothèques nécessaires et importé les Venns. Ensuite, nous créons l'ensemble de données en tant qu'ensemble Python, après quoi nous utilisons la fonction "venn2()" pour créer

Comment créer une pagination personnalisée dans CakePHP ? Comment créer une pagination personnalisée dans CakePHP ? Jun 04, 2023 am 08:32 AM

CakePHP est un framework PHP puissant qui fournit aux développeurs de nombreux outils et fonctionnalités utiles. L'un d'eux est la pagination, qui nous aide à diviser de grandes quantités de données en plusieurs pages, facilitant ainsi la navigation et la manipulation. Par défaut, CakePHP fournit des méthodes de pagination de base, mais vous devrez parfois créer des méthodes de pagination personnalisées. Cet article va vous montrer comment créer une pagination personnalisée dans CakePHP. Étape 1 : Créer une classe de pagination personnalisée Tout d'abord, nous devons créer une classe de pagination personnalisée. ce

Comment personnaliser les paramètres des touches de raccourci dans Eclipse Comment personnaliser les paramètres des touches de raccourci dans Eclipse Jan 28, 2024 am 10:01 AM

Comment personnaliser les paramètres des touches de raccourci dans Eclipse ? En tant que développeur, la maîtrise des touches de raccourci est l'une des clés pour améliorer l'efficacité du codage dans Eclipse. En tant qu'environnement de développement intégré puissant, Eclipse fournit non seulement de nombreuses touches de raccourci par défaut, mais permet également aux utilisateurs de les personnaliser selon leurs propres préférences. Cet article explique comment personnaliser les paramètres des touches de raccourci dans Eclipse et donne des exemples de code spécifiques. Ouvrez Eclipse Tout d'abord, ouvrez Eclipse et entrez

Comment activer et personnaliser les fondus enchaînés dans Apple Music sur iPhone avec iOS 17 Comment activer et personnaliser les fondus enchaînés dans Apple Music sur iPhone avec iOS 17 Jun 28, 2023 pm 12:14 PM

La mise à jour iOS 17 pour iPhone apporte de gros changements à Apple Music. Cela inclut la collaboration avec d'autres utilisateurs sur des listes de lecture, le lancement de la lecture de musique à partir de différents appareils lors de l'utilisation de CarPlay, et bien plus encore. L'une de ces nouvelles fonctionnalités est la possibilité d'utiliser des fondus enchaînés dans Apple Music. Cela vous permettra de passer facilement d’une piste à l’autre, ce qui est une fonctionnalité intéressante lors de l’écoute de plusieurs pistes. Le fondu enchaîné contribue à améliorer l'expérience d'écoute globale, en vous assurant de ne pas être surpris ou abandonné de l'expérience lorsque la piste change. Alors si vous souhaitez profiter au maximum de cette nouvelle fonctionnalité, voici comment l'utiliser sur votre iPhone. Comment activer et personnaliser le fondu enchaîné pour Apple Music dont vous avez besoin de la dernière version

fonction de rendu dans Vue3 : fonction de rendu personnalisée fonction de rendu dans Vue3 : fonction de rendu personnalisée Jun 18, 2023 pm 06:43 PM

Vue est un framework JavaScript populaire qui fournit de nombreuses fonctions et API pratiques pour aider les développeurs à créer des applications frontales interactives. Avec la sortie de Vue3, la fonction de rendu est devenue une mise à jour importante. Cet article présentera le concept et le but de la fonction de rendu dans Vue3 et comment l'utiliser pour personnaliser la fonction de rendu. Qu'est-ce que la fonction de rendu ? Dans Vue, le modèle est la méthode de rendu la plus couramment utilisée, mais dans Vue3, vous pouvez utiliser une autre méthode : r

Comment implémenter un middleware personnalisé dans CodeIgniter Comment implémenter un middleware personnalisé dans CodeIgniter Jul 29, 2023 am 10:53 AM

Comment implémenter un middleware personnalisé dans CodeIgniter Introduction : Dans le développement Web moderne, le middleware joue un rôle essentiel dans l'application. Ils peuvent être utilisés pour exécuter une logique de traitement partagé avant ou après que la demande atteigne le contrôleur. CodeIgniter, en tant que framework PHP populaire, prend également en charge l'utilisation de middleware. Cet article explique comment implémenter un middleware personnalisé dans CodeIgniter et fournit un exemple de code simple. Présentation du middleware : le middleware est une sorte de requête

See all articles