Low profile custom property panel PropertyGrid for wpf
Quote: https://www.cnblogs.com/lsgsanxiao/p/11776421.html
When you don't have wheels, make your own.
foreword
For the requirements of the project, I think I need to use such a thing as the vs attribute editor, professionally called, the attribute panel
How to do it?
Baidu, the PropertyGrid of wpf is as follows:
Three ways to implement PropertyGrid in WPF
The group asked wpf and vs attribute editing similar things, has anyone done it?
start
In order to reflect my humbleness, here is an explanation:
I just came into contact with wpf not long ago (no, I have seen this before, but there is no actual project), just two months ago, the project was to be done with wpf, and then I started working on it.
A lot of things are done while researching.
I wrote the above paragraph a year ago. I wanted to write a blog post after I finished it, but I didn’t finish it. Now I have finished it.
Here we first introduce HandyControl, a wpf control library. When I used it a year ago, there were not so many controls. Now there is PropertyGrid. The specific performance is as follows:
Isn't it cool, I only saw it in the latest update, it's a shame, it's a pity.
I wanted to replace what I wrote, but it was very troublesome: 1. Function 2. Existing project layout.
I wrote it like this:
The style is very different from HandyControl, that's because I set Style = null and use all native styles, so if you want to be cool, you can change it by yourself. Here I only talk about the implementation idea of this control.
How come? Take it easy.
1. Analyze the function of this control: display object properties, classify and edit them
2. Analyze the display layout of the control, you can refer to the property panel of vs
Someone must have a big head, and the VS attribute panel has so many functions, wow, annoying.
Some people can't get what they want, so they are irritable. Simply put, overthinking
Divide one thing, one thing into n things to do, and then do each step well, and this thing will be done well.
If it's messy, you write it down. The vs property panel is very complicated, so simplify it and display a property as follows:
From the above analysis, we know two important things of the control, logic and layout.
Step 1: Create a test class
public class Test:ViewModelBase { private string _Name; /// <summary> /// Name 属性更改通知 /// </summary> public string Name { get { return _Name; } set { _Name = value; RaisePropertyChanged(() => Name); } } }
ViewModelBase is just to use RaisePropertyChanged to trigger property changes, referenced from GalaSoft.MvvmLight
1 |
|
/// <summary> /// PropertyGrid features that can be applied to fields /// </summary> [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)] public class LsPropertyGridAttribute : Attribute { /// <summary> /// The corresponding section /// </summary> public string Plate; /// <summary> /// Display Name /// </summary> public string ShowName; public LsPropertyGridAttribute(string plate, string showName) { TypeName = type; ShowName = showName; } }
1 |
|
public class Test:ViewModelBase { private string _Name; /// <summary> /// Name 属性更改通知 /// </summary> [LsPropertyGrid("内容","名字")] public string Name { get { return _Name; } set { _Name = value; RaisePropertyChanged(() => Name); } } }
Next, write the PropertyGrid control. Here I inherit StackPanel, and you have to have a displayed dependency property for assigning objects, so its type is object. Don’t ask me how I know it, just ask.
public class PropertyGrid : StackPanel { static PropertyGrid() { //Set the key to reference the style for this control // set the key to reference the style for this control FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata( typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid))); } /// <summary> /// The type of property that needs to be displayed /// </summary> private object _ShowProp; #region dependency property /// <summary> /// Display the property edit of this class /// </summary> public object ShowProp { get { return (object)GetValue(ShowPropProperty); } set { SetValue(ShowPropProperty, value); } } // Using a DependencyProperty as the backing store for ShowProp. This enables animation, styling, binding, etc... public static readonly DependencyProperty ShowPropProperty = DependencyProperty.Register("ShowProp", typeof(object), typeof(PropertyGrid), new PropertyMetadata(default(object), new PropertyChangedCallback((d, e) => { //属性更改事件 OnShowPropChanged(d, e); }))); #endregion /// <summary> /// ShowProp属性更改事件 /// </summary> /// <param name="d"></param> /// <param name="e"></param> private static void OnShowPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { } }
The simple code above has actually set up the code structure of the entire property panel. Next, we will gradually improve it. Because the property panel is for all types of objects, we need to use reflection to get the information of this object
Get the editing properties of the object, then generate the layout, and bind
public class PropertyGrid : StackPanel { static PropertyGrid() { //Set the key to reference the style for this control // set the key to reference the style for this control FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata( typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid))); } /// <summary> /// The type of property that needs to be displayed /// </summary> private object _ShowProp; #region dependency property /// <summary> /// Display the property edit of this class /// </summary> public object ShowProp { get { return (object)GetValue(ShowPropProperty); } set { SetValue(ShowPropProperty, value); } } // Using a DependencyProperty as the backing store for ShowProp. This enables animation, styling, binding, etc... public static readonly DependencyProperty ShowPropProperty = DependencyProperty.Register("ShowProp", typeof(object), typeof(PropertyGrid), new PropertyMetadata(default(object), new PropertyChangedCallback((d, e) => { //属性更改事件 OnShowPropChanged(d, e); }))); #endregion /// <summary> /// ShowProp property change event /// </summary> /// <param name="d"></param> /// <param name="e"></param> private static void OnShowPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var sender = d as PropertyGrid; var newValue = e.New Value; if ( newValue != null) { Type t = newValue.GetType(); sender._ShowProp = newValue; Object[] obj = t.GetProperties(); //Get custom properties on properties foreach (PropertyInfo propInfo in obj) { object[] objAttrs = propInfo.GetCustomAttributes(typeof(LsPropertyGridAttribute), true); if (objAttrs.Length > 0) { //Get edited property features LsPropertyGridAttribute attr = objAttrs[0] as LsPropertyGridAttribute; if (attr != null) { double positionLeft = 10;//distance to the left double positionTop = 15;//distance up //Console.WriteLine("Type : {0}", attr.TypeName); //plate does not exist create a TextBlock label = new TextBlock(); s ender.Children.Add(label); // Grid of the plate Grid grid = new Grid(); //grid.Width = 200; grid.Margin = new Thickness(positionLeft, 0, 0, 2 ) ; grid.HorizontalAlignment = HorizontalAlignment.Left; grid.Background = Brushes.White; //添加列 var column = new ColumnDefinition(); column.Width = new GridLength(80); column.MinWidth = 80; column.MaxWidth = 100; grid.ColumnDefinitions.Add(column); var column2 = new ColumnDefinition(); //column.Width = new GridLength(1.0, GridUnitType.Star); column2.Width = new GridLength(1.0, GridUnitType.Auto); column2.MinWidth = 250; column2.MaxWidth = 250; grid.ColumnDefinitions.Add(column2); tb.VerticalAlignment = VerticalAlignment.Center; sender.Children.Add(grid); var row = new RowDefinition(); row.MinHeight = 22; grid.RowDefinitions.Add(row); //add row //left display name TextBlock tb = new TextBlock(); tb.Text = attr.ShowName; tb.HorizontalAlignment = HorizontalAlignment.Left; = new Thickness(0, 0, 0, 0); //Modify the Grid.Row property of the control through code Grid.SetRow(tb, grid.RowDefinitions.Count - 1); Grid.SetColumn(tb, 0); grid.Children.Add(tb); // Bind to the control according to the name of the execution property Binding binding = new Binding(propInfo.Name); binding.Source = newValue; binding.Mode = BindingMode.TwoWay; var control = new TextBox(); UpdateSourceTrigger.PropertyChanged; binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit; control.PreviewKeyDown += Control_PreviewKeyDown; control.SetBinding(System.Windows.Controls.TextBox.TextProperty, binding); control.VerticalAlignment = VerticalAlignment.Center; //Modify the Grid.Row property of the control through code Grid.SetRow(control, grid.RowDefinitions.Count - 1); Grid.SetColumn(control, 1); grid.Children.Add(control); } } } } } /// <summary> /// Enter to trigger data change /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void Control_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e) { if (e.Key == Key.Enter) { var temp = sender as WControls.TextBox; BindingExpression binding = temp.GetBindingExpression(WControls.TextBox.TextProperty); binding.UpdateSource(); } } }
A simplest attribute panel is born, there is only one attribute, and it can be used after generation
Reference control path in form xaml: xmlns:Data="clr-namespace: property panel Demo.Data"
<Data:PropertyGrid x:Name="pg" HorizontalAlignment="Left" Height="100" Margin="215,107,0,0" Grid.Row="1" VerticalAlignment="Top" Width="400"/>
var test = new Test(); test.Name = "wc"; pg.ShowProp = test;
Shown as follows:
Others can be added bit by bit, and the same is true.
Then I will directly upload the complete code below, um, you all agree (- -, how lazy you are)
There are drop-down boxes, selections, buttons (used to bind trigger methods), etc. in the control, which can control any bindable properties of the bound control, such as: hide and display
Complete PropertyGrid
/// <summary> /// Custom property display control /// </summary> public class PropertyGrid : StackPanel { static PropertyGrid() { //Set the key to reference the style of this control // set the key to reference the style for this control FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata( typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid))); } #region field /// <summary> /// Record the Grid corresponding to a section /// </summary> private Dictionary<string, Grid> _KeyValuePairs = new Dictionary<string, Grid>(); /// <summary> The type of property that needs to be displayed /// </summary> private object _ShowProp; #endregion #region Dependency property /// <summary> /// Display the property edit of this class /// </summary> public object ShowProp { get { return (object)GetValue(ShowPropProperty); } set { SetValue(ShowProp Property, value); } } // Using a DependencyProperty as the backing store for ShowProp. This enables animation, styling, binding, etc... public static readonly DependencyProperty ShowPropProperty = DependencyProperty.Register("ShowProp", typeof(object), typeof (PropertyGrid), new PropertyMetadata(default(object), new PropertyChangedCallback((d, e) => // { // attribute change event OnShowPropChanged(d, e); }))); #endregion #region private method /// <summary> /// ShowProp attribute change event /// </summary> /// <param name="d"></param> /// <param name="e"></param> private static void OnShowPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var sender = d as PropertyGrid; sender.Children.Clear(); sender._KeyValuePairs.Clear(); var newValue = e.NewValue; if (newValue != null) { Type t = newValue.GetType(); sender._ShowProp = newValue; Object[] obj = t.GetProperties(); // Get the custom properties on the property foreach (PropertyInfo propInfo in obj) { CreateControlByAttribute(sender, newValue, propInfo); } Object [] objFields = t.GetFields( ); // Get custom properties on public fields foreach (FieldInfo propInfo in objFields) { CreateControlByAttribute(sender, newValue, propInfo); } Object[] objMethods = t.GetMethods(); //Get custom attributes on public methods foreach (MethodInfo propInfo in objMethods) { CreateControlByAttribute(sender, newValue,propInfo); } } } /// <summary> /// Create controls based on attribute characteristics /// </summary> /// <param name="objAttrs"></param> /// <param name="sender"></param> /// <param name="Source"></param> /// <param name="path"></param> private static void CreateControlByAttri but(PropertyGrid sender, object Source, MemberInfo memberInfo) { object[] objAttrs = memberInfo.GetCustomAttributes(typeof(LsPropertyGridAttribute), true); if (objAttrs.Length > 0) { //Get edited attribute characteristics LsPropertyGridAttribute attr = objAttrs[0] as LsPropertyGridAttribute; if (attr != null) { //Console.WriteLine("Type : {0 }", attr.TypeName); Create(sender, attr, Source,memberInfo); } } } /// <summary> /// Create /// </summary> /// <param name="sender">PropertyGrid</param> /// <param name="attr"></param> /// <param name="Source">Bound object</param> /// <param name="path">Attributes of the object</param> public static void Create(PropertyGrid sender, LsPropertyGridAttribute attr, object Source, MemberInfo memberInfo) { double positionLeft = 10;//distance to the left double positionTop = 15;//distance on //judging whether the plate already exists if ( sender._KeyValuePairs.ContainsKey(attr.Plate)) { var grid = sender._KeyValuePairs[attr.Plate]; //There is a control added directly behind the Grid CreateControl(sender,grid, attr, Source, memberInfo); } else { //The plate does not exist to create a TextBlock label = new TextBlock(); label.Text = attr.Plate; label.HorizontalAlignment = HorizontalAlignment.Left; label.Margin = new Thickness(positionLeft, positionTop, 0, 2); label.FontSize = 16; // Thickness is only available when the thickness exceeds 400 label.FontWeight = FontWeight.FromOpenTypeWeight(600); sender.Children.Add(label); // Grid Grid grid = new Grid(); //grid.Width = 200; grid.Margin = new Thickness(positionLeft, 0, 0, 2); grid.HorizontalAlignment = HorizontalAlignment.Left; grid.Background = Brushes.White; //Add column var column = new ColumnDefinition(); column.Width = new GridLength(80); column.MinWidth = 80; column.MaxWidth = 100; grid.ColumnDefinitions.Add(column); var column2 = new ColumnDefinition(); //column.Width = new GridLength(1.0, GridUnitType.Star); column2.Width = new GridLength(1.0, GridUnitType.Auto); column2.MinWidth = 250; column2.MaxWidth = 250; grid.ColumnDefinitions.Add(column2); //添加记录模板 sender._KeyValuePairs[attr.Plate] = grid; sender.Children.Add(grid); CreateControl(sender,grid, attr, Source, memberInfo); } } /// <summary> /// 创建并绑定控件 /// </summary> /// <param name="pROPERTYType"></param> /// <param name="path"></param> /// <returns></returns> private static void CreateControl(PropertyGrid sender, Grid grid, LsPropertyGridAttribute attr, object Source, MemberInfo memberInfo) { Control control = new Control(); if (attr.TypeName != PROPERTYType.Size) { var row = new RowDefinition(); row.MinHeight = 22; grid.RowDefinitions.Add(row); //Add row //left display name TextBlock tb = new TextBlock(); tb.Text = attr.ShowName; tb.HorizontalAlignment = HorizontalAlignment.Left; tb.VerticalAlignment = VerticalAlignment.Center; Modify the Grid.Row property of the control Grid.SetRow(tb, grid.RowDefinitions.Count - 1); Grid.SetColumn(tb, 0); grid.Children.Add (tb); } // Bind to the control according to the name of the execution property Binding binding = new Binding(memberInfo.Name); binding.Source = Source; binding.Mode = BindingMode.TwoWay; //if ((attr.TypeName & PROPERTYType.TextBox) == PROPERTYType.TextBox) //{ // control = new WControls.TextBox(); // control.Style = null; // binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; // control.SetBinding(System.Windows.Controls.TextBox.TextProperty, binding); //} switch (attr.TypeName) { case PROPERTYType.Folder: #region Folder double tbFolderWidth = 210; var btnFolder = new WControls.Button(); btnFolder.Content = "..."; btnFolder.Width = 40; btnFolder.HorizontalAlignment = HorizontalAlignment.Left; btnFolder.Margin = new Thickness(tbFolderWidth, 0, 0, 0); // Modify the Grid.Row property of the control through code Grid.SetRow(btnFolder, grid.RowDefinitions.Count - 1); Grid.SetColumn(btnFolder, 1); btnFolder.Style = null; tbFolder = new WControls.TextBox(); tbFolder.Width = tbFolderWidth ; tbFolder.HorizontalAlignment = HorizontalAlignment.Left; ; Grid.SetRow(tbFolder, grid.RowDefinitions.Count - 1); Grid.SetColumn(tbFolder, 1); tbFolder.Style = null; sender.MethodSeBinding(btnFolder, memberInfo); //Both properties are bound. All bindings must bind both properties. Binding(btnFolder, memberInfo, grid , false); //Enter trigger binding tbFolder.PreviewKeyDown += Control_PreviewKeyDown; //binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit; tbFolder.SetBinding(System.Windows.Controls.TextBox.TextProperty, binding); grid.Children.Add(btnFolder); grid.Children.Add (tbFolder); #endregion return; case PROPERTYType.BoldItalic: //grid.Children.RemoveAt (grid.Children.Count - 1); string[] vsBoldItalic = attr.Tag.Split(','); controlBold = new WControls.Button(); controlBold.Width = 40; // Modify the Grid.Row property of the control through code Grid.SetRow(controlBold, grid.RowDefinitions.Count - 1); controlBold.Content = vsBold[1]; controlBold.HorizontalAlignment = HorizontalAlignment.Left; #region italics Grid.SetColumn(controlBold, 1); controlBold.Style = null; grid.Children.Add(controlBold); //Binding to the control according to the name of the execution property Binding bindingBold = new Binding(vsBold[0]); bindingBold.Source = Source; bindingBold.Mode = BindingMode.TwoWay; //Binding to the tag according to the binding Data change color controlBold.SetBinding(TagProperty, bindingBold); controlBold.Click += ControlBold_Click; #endregion //Italic string[] vsItalic = vsBoldItalic[1].Split(':'); var controlItalic = new WControls.Button(); controlItalic.Style = null; controlItalic.Width = 40; controlItalic.Content = vsItalic[1]; controlItalic.Margin = new Thickness(40, 0, 0, 0); controlItalic.HorizontalAlignment = HorizontalAlignment.Left; //Modify the Grid.Row property of the control through code Grid.SetRow(controlItalic, grid.RowDefinitions.Count - 1); Grid.SetColumn(controlItalic, 1); grid.Children.Add(controlItalic); //Bind to the control according to the name of the execution property Binding bindingItalic = new Binding(vsItalic[0]); bindingItalic.Source = Source; bindingItalic.Mode = BindingMode.TwoWay; //Binding to the tag changes the color according to the bound data controlItalic.SetBinding(TagProperty, bindingItalic); controlItalic.Click += ControlBold_Click; #endregion //These two buttons are bound to the same event, all need to judge sender.MethodSeBinding(controlBold, memberInfo); sender.RelationSeBinding(controlBold, memberInfo,grid); sender.MethodSeBinding(controlItalic, memberInfo); sender.RelationSeBinding(controlItalic, memberInfo, grid); return; case PROPERTYType.Button: control = new WControls.Button(); var tempbtn = control as Button; tempbtn.Width = 40; tempbtn.Content = attr.Content; tempbtn.HorizontalAlignment = HorizontalAlignment.Left; sender.MethodSeBinding(control, memberInfo); sender.RelationSeBinding(control, memberInfo, grid); control.Style = null; break; case PROPERTYType.TextBox: control = new WControls.TextBox(); sender.MethodSeBinding(control, memberInfo); sender.RelationSeBinding(control, memberInfo, grid); control.Style = null; //Enter trigger binding control.PreviewKeyDown += Control_PreviewKeyDown; //binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit; con trol.SetBinding (System.Windows.Controls.TextBox.TextProperty, binding); break; case PROPERTYType.Size: #region size, this function can be called back string[] vs = attr.ShowName.Split(','); if (vs.Length == 2) { attr.TypeName = PROPERTYType.TextBox; attr.ShowName = vs[0]; //宽度 CreateControl(sender,grid, attr, Source, memberInfo); //高度 attr.ShowName = vs[1]; CreateControl(sender,grid, attr, Source, memberInfo); } #endregion return; case PROPERTYType.Color: control = new Button(); control.MinHeight = 18; sender.MethodSeBinding(control, memberInfo); sender.RelationSeBinding(control, memberInfo, grid); control.Style = null; var temp = control as Button; temp.Click += Color_Click; temp.SetBinding(Button.BackgroundProperty, binding); break; case PROPERTYType.CheckBox: control = new CheckBox(); sender.MethodSeBinding(control, memberInfo); sender.RelationSeBinding(control, memberInfo, grid); control.Style = null; control.SetBinding(CheckBox.IsCheckedProperty, binding); break; case PROPERTYType.Label: control = new WControls.Label(); { sender.MethodSeBinding(control, memberInfo); sender.RelationSeBinding(control, memberInfo, grid); control.Style = null; var templb = control as Label; control.SetBinding(ContentControl.ContentProperty, binding); break; case PROPERTYType.ComboBox: control = new WControls.ComboBox(); l.Style = null; var tempCB = control as WControls.ComboBox; //This must be set before if (!attr.Tag.Equals("")) string[] attrMV = attr.Tag.Split(','); //Key tempCB.SelectedValuePath = attrMV[0]; //Value tempCB.DisplayMemberPath = attrMV[1]; } #region Binding association sender.MethodSeBinding(control, memberInfo); sender.RelationSeBinding(control, memberInfo, grid); #endregion //Considering that this property may bind Selected Value or SelectedItem, all here are not directly hard bound //tempCB.SetBinding(WControls.ComboBox.SelectedValueProperty, binding); break; } control.VerticalAlignment = VerticalAlignment.Center; //Modify the Grid.Row property of the control through code Grid.SetRow(control, grid.RowDefinitions.Count - 1); Grid.SetColumn(control, 1); grid.Children.Add(control); } #region 控件事件 /// <summary> /// 回车触发数据改变 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void Control_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e) { if (e.Key == Key.Enter) { var temp = sender as WControls.TextBox; BindingExpression binding = temp.GetBindingExpression(WControls.TextBox.TextProperty); binding.UpdateSource(); } } private static void ControlBold_Click(object sender, RoutedEventArgs e) { var btn = sender as Button; bool tag = (bool)btn.Tag; // bold and italic if (tag) { btn.Tag = false; btn.Background = Brushes.LightGray; } else { bt n.Tag = true; btn.Background = Brushes.Gold; } } #endregion /// <summary> /// Set associated events /// </summary> /// <param name="control"></param> public void MethodSeBinding(Control control, MemberInfo memberInfo) { //Type t = _ShowProp.GetType(); //Object[] obj = t.GetProperties(); // Get custom attributes on properties object[] objAttrs = memberInfo.GetCustomAttributes(typeof(RelationMethodAttribute), true) ; if (objAttrs.Length > 0) { //Get edited attribute characteristics for (int i = 0; i < objAttrs.Length; i++) { RelationMethodAttribute attrTemp = objAttrs[i] as RelationMethodAttribute; //Reflect as control event, add specified method var click = control.GetType().GetEvents().FirstOrDefault(ei => ei.Name.ToLower() == attrTemp.CrEventName.ToLower()); if (click != null) { // find method by name var method = _ShowProp.GetType().GetMethod(attrTemp.ClMethodName); // create Delegate var handler = Delegate.CreateDelegate(click.EventHandlerType, _ShowProp, method); click.AddEventHandler(control, handler); } } } } /// <summary> /// Set associated properties /// </summary> /// <param name="control"></param> public void RelationSeBinding(Control control, MemberInfo memberInfo,Grid grid, bool IsVisibility = true) { //Get the custom attribute object[] objAttrs = memberInfo.GetCustomAttributes(typeof(RelationAttribute), true); if (objAttrs.Length > 0) { //Get edited attribute features for (int i = 0; i < objAttrs.Length; i++) { RelationAttribute attrTemp = objAttrs[i] as RelationAttribute; RelationSeBinding(control, attrTemp, grid); } } } /// <summary> /// Visibility Converter /// </summary> private VisibilityBoolConverter _VisibilityBool = new VisibilityBoolConverter(); private VisibilityValueConverter _VisibilityValue = new VisibilityValueConverter(); /// <summary> /// Set associated properties /// </summary> /// <param name="control"></param> /// <param name="IsVisibility">If you bind the Visibility attribute, this can be true to set whether you need to hide the control in the first column of the grid ///true to hide /// </param> public void RelationSeBinding(Control control, RelationAttribute attr, Grid grid, bool IsVisibility = true) { if (attr != null) { //Get the associated properties of the class and the associated properties of the control string[] crName = attr.CrPropName.Split(','); string[] clName = attr.ClPropName.Split(','); for (int i = 0; i < crName.Length; i++) { //Bind to the control according to the name of the execution property Binding binding = new Binding(clName[i]); binding.Source = _ShowProp; binding.Mode = BindingMode.TwoWay; #region display hidden property processing //If bool is used to control display and hide VisibilityBool if (crName[i] == "VisibilityBool") { //use converter crName[i] = "Visibility"; binding.Converter = _VisibilityBool; }else if (crName[i] == "VisibilityValue") { //Use the converter crName[i] = "Visibility"; binding.Converter = _VisibilityValue; binding.ConverterParameter = attr.VisibilityValue; } //Bind the grid line to hide the display attribute if (crName[i] == "Visibility" && IsVisibility) { grid.RowDefinitions[grid.RowDefinitions.Count - 1].MinHeight = 0; var cr = grid.Children[grid.Children.Count - 1] as TextBlock; cr.SetBinding(Control.VisibilityProperty, binding); } #endregion //Get dependency property BindingFlags mPropertyFlags = BindingFlags.Instance | BindingFlags.Public| BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.NonPublic;//Filter //Get control associated properties var fieldInfo = control.GetType().GetField(crName[i] + "Property", mPropertyFlags); if (fieldInfo != null) { ((DependencyProperty)fieldInfo.GetValue(control), binding); } } } } /// <summary> /// Select color /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void Color_Click(object sender, RoutedEventArgs e) { var tempBtn = sender as Button; //var picker = SingleOpenHelper.CreateControl<ColorPicker>(); //var window = new PopupWindow //{ // PopupElement = picker, // WindowStartupLocation = WindowStartupLocation.CenterScreen, // AllowsTransparency = true, // WindowStyle = WindowStyle.None, // MinWidth = 0, // MinHeight = 0, // Title = "颜色选择器" //}; //picker.SelectedColorChanged += delegate //{ // window.Close(); //}; //picker.Canceled += delegate { window.Close(); }; //window.Show(); var picker = SingleOpenHelper.CreateControl<ColorPicker>(); var window = new PopupWindow { PopupElement = picker }; picker.SelectedColorChanged += delegate { tempBtn.Background = picker.SelectedBrush; window.Close(); }; picker.Canceled += delegate { window.Close(); }; window.ShowDialog(tempBtn, false); } #endregion #region public method #endregion }
/// <summary> /// Generate control type to calculate control type by digit /// </summary> public enum PROPERTYType { Label = 1, TextBox = 2, /// <summary> /// Size, width and height of control /// </summary> Size = 4, /// <summary> /// Optional color /// </summary> Color = 8, /// <summary> /// Drop-down box /// Considering two situations, using this type of attribute does not bind the attribute, and the specific binding uses the associated feature for binding /// That is to say, this type of drop-down box can be assigned to any attribute, but if it is not bound by RelationAttribute, it has nothing to do with the control /// </summary> ComboBox = 16 , /// <summary> /// Optional color /// </summary> CheckBox = 32, /// <summary> /// Folder Type /// </summary> Folder = 64, /// <summary> /// Button /// </summary> Button = 128, /// <summary> /// Bold Italic This type cannot use VisibilityValue to display hidden controls (because both places use tags to save data), VisibilityBool /// </summary > BoldItalic = 256, /// <summary> /// Bindable control type, need to be assigned with other control types /// </summary> Relation = 2048 }
/// <summary> /// PropertyGrid features that can be applied to fields /// </summary> [AttributeUsage(AttributeTargets.All|AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true, Inherited =true)] public class LsPropertyGrid Attribute : Attribute { /// <summary> /// Generated control type /// </summary> public PROPERTYType TypeName; /// <summary> /// Corresponding plate /// </summary> public string Plate; /// <summary> /// Display name /// </summary> public string ShowName; /// <summary> /// Generate the display content of the control. Different controls can be used differently. Currently, it is used with button /// </summary> public string Content; /// <summary> /// Reserve Tag to carry data objects /// </summary> public string Tag; public LsPropertyGridAttribute(PROPERTYType type,string plate,string showName) { TypeName = type; #region language switching, look for dynamic resources var tempStr = ResourceHelper.GetResource<string>(plate); Plate = tempStr != null && tempStr != "" ? tempStr : plate; tempStr = ResourceHelper.GetResource<string>(showName); = tempStr != null && tempStr != "" ? tempStr :showName; #endregion } }
The above language switching uses the tool class of the hc control library, which is actually an assignment. If there is no reference to the PropertyGrid class, the color selection must be removed and the color control of hc is referenced.
It is recommended to use hc control library
Two special feature classes are introduced below
Regarding multiple properties, bind the display, hide or availability of the same property panel
/// <summary> /// There is a situation where /// 1. The property of the control whose own property is bound to other properties /// PropertyGrid association feature that can be applied to the field property /// Association feature function: the value of the modified field or property can be used to bind the value of the control generated by other properties /// It is mostly used to check the box in the property editing control to control the display (or other values) of other controls. Inheritance, AllowMultiple=Whether to allow multiple descriptions. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property| AttributeTargets.Method, AllowMultiple = true,Inherited = true)] public class RelationAttribute : Attribute { /// <summary> /// 1. The same control needs to be associated with the attribute name, use English commas to separate different write multiple Relation Attribute /// eg: Text, Size /// </summary> public string CrPropName ; /// <summary> /// 1. The name of the class property associated with the control property name, separated by English commas, corresponding to CrPropName /// eg: Name, Size /// </summary> public string ClPropName; /// <summary> /// When using binding to display and hide, CrPropName=VisibilityValue /// must set the value of this field, which is the value displayed by the control /// </summary> public object VisibilityValue ; public string Tag; public RelationAttribute(string clPropName, string crPropName) { CrPropName = crPropName; ClPropName = clPropName; } }
The other is a trait class that binds the method
/// <summary> /// Class method and control event binding /// </summary> [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)] public class RelationMethodAttribute : Attribute { /// <summary> /// 1. The name of the event that needs to be associated with the same control, use English commas to separate different write multiple RelationMethodAttribute /// eg : Click,Click /// </summary> public string CrEventName; /// <summary> /// 1. The class method associated with the control event, use English commas to separate, corresponding to CrPropName /// eg: ControlSelect_Click, ControlSelect_Click /// </summary> pub lic string ClMethodName; public string Tag; public RelationMethodAttribute(string clEventName, string crMethodName) { CrEventName = crMethodName; ClMethodName = clEventName; } }
/// <summary> /// The binding attribute string used to describe the property panel /// </summary> public class DependencyPropertyToken { /// <summary> /// /// </summary> public const string ItemsSource = nameof(ItemsSource); public const string Visibility = nameof(Visibility); /// <summary> /// Use bool binding to control display /// </summary> public const string VisibilityBool = nameof(VisibilityBool); /// <summary> /// Use a certain value to bind control display, as long as this value appears, it will be displayed, and other values will be hidden /// </summary> public const string VisibilityValue = nameof(VisibilityValue); public const string IsEnabled = nameof(IsEnabled); public const string SelectedItem = nameof(SelectedItem); public const string SelectedValue = nameof(SelectedValue); public const string SelectedText = nameof(SelectedText); public const string Tag = nameof(Tag); }
public class EventToken { public const string Click = nameof(Click); }
The DependencyPropertyToken and EventToken classes are string classes, just to avoid typos and create converters
/// <summary> /// Use bool to control the hidden display control /// </summary> public class VisibilityBoolConverter : IValueConverter { /// <summary> /// When the value is propagated from the binding source to the binding target, call the method Convert /// </summary> /// <param name="value"></param> /// <param name="targetType"></param> /// <param name ="parameter"></param> /// <param name="culture"></param> /// <returns></returns> public object Convert(object value, Type targetType, object parameter,CultureInfo culture) { if (value is bool boolValue) { if (boolValue) public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
/// <summary> /// Use bool to control the hidden display control /// </summary> public class VisibilityValueConverter : IValueConverter { /// <summary> /// When the value is propagated from the binding source to the binding target, call the method Convert /// </summary> /// <param name="value"></param> /// <param name="targetType"></param> /// <param name ="parameter"></param> /// <param name="culture"></param> /// <returns></returns> public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value != null) { if (parameter != null) { string tempStr = parameter.ToString(); string valueStr = value.ToString(); if (valueStr == tempStr) { return Visibility.Visible; } else { return Visibility.Collapsed; } } } return Visibility.Collapsed; When binding the source, call this method ConvertBack, the implementation of the method ConvertBack must be the reverse implementation of the method Convert . /// </summary> /// <param name="value"></param> /// <param name="targetType"></param> /// <param name="parameter"></param> /// <param name="culture"></param> /// <returns></returns> public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
One uses bool to control the hidden display, and the other uses any value to control
The above is all the code. Since it was completed, it has not been modified. It is very comfortable to use. If you want to change the style, you can also change it, depending on the specific needs.
Simple and simple Demo
Link: https://pan.baidu.com/s/1jRxi-u3ORyETwRoh8VLp9Q Extraction code: fsb3
By the way, I will give you a program for reading novels:
You can crawl the novels of any (most) websites, why do you do this, because most of the current novel websites have a bunch of advertisement pop-ups except for the starting point, I just try to read the mystery by the way when I am bored with reptiles, ahem.
Click Custom Get, after the setting is complete, return to the main interface, continue to click Get, if the settings are correct, the novel will be downloaded automatically. Purely for entertainment, not for profit.
Link: https://pan.baidu.com/s/1vWWntkqukBMva3N-b3WSTA Extraction code: ruqr
Links are only valid for seven days, otherwise comments are required.
Isn't the property panel very simple: traits, reflections, bindings. You should understand, let's call it a day.