Low profile custom property panel PropertyGrid for wpf

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

copy code

public class Test:ViewModelBase
    {

        private string _Name;
        /// <summary>
        /// Name 属性更改通知
        /// </summary>
        public string Name
        {
            get
            {
                return _Name;
            }
            set
            {
                _Name = value;
                RaisePropertyChanged(() => Name);
            }
        }

    }

copy code

ViewModelBase is just to use RaisePropertyChanged to trigger property changes, referenced from GalaSoft.MvvmLight

1

既然是编辑对象的属性,那肯定少不了Attribute,所以需要写一个描述对象属性的Attribute,如下:

copy code

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

copy code

1

<br>那测试的类的name属性就可以添加上特征

copy code

public class Test:ViewModelBase
    {

        private string _Name;
        /// <summary>
        /// Name 属性更改通知
        /// </summary>
        [LsPropertyGrid("内容","名字")]
        public string Name
        {
            get
            {
                return _Name;
            }
            set
            {
                _Name = value;
                RaisePropertyChanged(() => Name);
            }
        }

    }

copy code

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.

copy code

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)
        {

        }
    }

copy code

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

copy code

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

copy code

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

copy code

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

copy code

copy code

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

copy code

copy code

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

copy code

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

copy code

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

copy code

The other is a trait class that binds the method

copy code

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

copy code

copy code

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

copy code

public class EventToken
    {
        public const string Click = nameof(Click);
    }
The DependencyPropertyToken and EventToken classes are string classes, just to avoid typos and create 
converters

copy code

/// <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();
        }
    }

copy code

copy code

/// <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();
        }
    }

copy code

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.

Guess you like

Origin blog.csdn.net/u014090257/article/details/119904555