惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

AI
AI
博客园 - 叶小钗
Blog — PlanetScale
Blog — PlanetScale
Microsoft Azure Blog
Microsoft Azure Blog
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
A
About on SuperTechFans
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
Martin Fowler
Martin Fowler
阮一峰的网络日志
阮一峰的网络日志
D
Docker
Jina AI
Jina AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The Register - Security
The Register - Security
J
Java Code Geeks
S
SegmentFault 最新的问题
月光博客
月光博客
G
Google Developers Blog
美团技术团队
Last Week in AI
Last Week in AI
L
LangChain Blog
Apple Machine Learning Research
Apple Machine Learning Research
T
The Blog of Author Tim Ferriss
腾讯CDC
Recent Announcements
Recent Announcements
Recorded Future
Recorded Future
The Cloudflare Blog
有赞技术团队
有赞技术团队
博客园_首页
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
B
Blog
I
InfoQ
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
F
Fortinet All Blogs
B
Blog RSS Feed
Engineering at Meta
Engineering at Meta
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Microsoft Security Blog
Microsoft Security Blog
MongoDB | Blog
MongoDB | Blog
爱范儿
爱范儿
D
DataBreaches.Net
F
Full Disclosure
M
MIT News - Artificial intelligence
博客园 - 司徒正美
H
Help Net Security

博客园 - HotSky

Chrome启用CDP(chrome-devtools-protocol)进行远程操控 WPF 让ScrollViewer支持按住鼠标中键拖拽滚动内容 WPF 让ScrollViewer支持鼠标中键滚动缩放内容 Nodejs使用 nodejs中写sql需要用in时的写法 C#城市最短路径 C#Animation Sqlite PDF附录A: 内容流操作码 WPF FPS类 生命模拟 C# Sql帮助类,可扩展 WPF WriteableBitmap通过GDI+绘制帮助类 Alpha混合 Bmp读写二值图 WPF支持任意快捷键+鼠标组合的绑定类 WPF阻止窗体被系统缩放,使用显示器DPI WPF DataGrid自动增长序号列 C#访问或修改私有类、函数、变量、属性
WPF一个简单的属性编辑控件
HotSky · 2024-05-25 · via 博客园 - HotSky

代码:

    public class PropertiesControl : Grid
    {

        [TypeConverter(typeof(LengthConverter))]
        public double RowHeight
        {
            get { return (double)GetValue(RowHeightProperty); }
            set { SetValue(RowHeightProperty, value); }
        }

        public static readonly DependencyProperty RowHeightProperty =
            DependencyProperty.Register("RowHeight", typeof(double), typeof(PropertiesControl), new PropertyMetadata(0d));

        public object Source
        {
            get { return (object)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        public static readonly DependencyProperty SourceProperty =
            DependencyProperty.Register("Source", typeof(object), typeof(PropertiesControl), new PropertyMetadata(SourceChangedCallBack));

        private static void SourceChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PropertiesControl control = (PropertiesControl)d;
            if (e.OldValue == e.NewValue) return;
            control.Load();
        }

        GridSplitter splitter = new GridSplitter { Width = 2, HorizontalAlignment = HorizontalAlignment.Right };

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            splitter.SetBinding(BackgroundProperty, new Binding { Source = this, Path = new PropertyPath(BackgroundProperty) });
        }

        void Load()
        {
            Children.Clear();
            RowDefinitions.Clear();
            ColumnDefinitions.Clear();
            ColumnDefinitions.Add(new ColumnDefinition());
            ColumnDefinitions.Add(new ColumnDefinition());
            if (Source == null) return;
            PropertyDocument doc = PropertyDocument.Load(Source);
            if (doc.Children != null)
                Load(doc.Children);
            SetRowSpan(splitter, RowDefinitions.Count);
            Children.Add(splitter);
        }

        void Load(List<Property> properties)
        {
            if (properties == null) return;
            foreach (var child in properties)
            {
                RowDefinitions.Add(new RowDefinition());
                if (child.Children != null && child.Children.Count > 0)
                {
                    TextBlock group = new TextBlock { Text = child.PropertyInfo.Name, };
                    SetColumn(group, 0);
                    SetRow(group, RowDefinitions.Count - 1);
                    Children.Add(group);
                    Load(child.Children);
                    continue;
                }
                TextBlock text = new TextBlock { Text = child.PropertyInfo.Name, VerticalAlignment = VerticalAlignment.Center };
                SetColumn(text, 0);
                SetRow(text, RowDefinitions.Count - 1);
                LoadValueControl(child);
                Children.Add(text);
            }
        }

        void LoadValueControl(Property child)
        {
            FrameworkElement element;
            Binding binding = new Binding(child.Path) { Source = Source, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
            if (child.PropertyInfo.PropertyType.IsEnum)
            {
                Array enmus = Enum.GetValues(child.PropertyInfo.PropertyType);
                ComboBox comboBox = new ComboBox { ItemsSource = enmus, VerticalContentAlignment = VerticalAlignment.Center, BorderThickness = new Thickness(0), IsEnabled = !child.CanSet() };
                comboBox.SetBinding(Selector.SelectedItemProperty, binding);
                element = comboBox;
            }
            else if (child.PropertyInfo.PropertyType == typeof(bool))
            {
                CheckBox cb = new CheckBox { VerticalAlignment = VerticalAlignment.Center, IsEnabled = !child.CanSet() };
                cb.SetBinding(CheckBox.IsCheckedProperty, binding);
                element = cb;
            }
            else if (!child.CanSet())
            {
                TextBlock control = new TextBlock { VerticalAlignment = VerticalAlignment.Center };
                control.SetBinding(TextBlock.TextProperty, binding);
                element = control;
            }
            else
            {
                TextBox textBox = new TextBox { VerticalContentAlignment = VerticalAlignment.Center, BorderThickness = new Thickness(0) };
                textBox.SetBinding(TextBox.TextProperty, binding);
                element = textBox;
            }
            SetColumn(element, 1);
            SetRow(element, RowDefinitions.Count - 1);
            if (RowHeight > 0d)
                element.Height = RowHeight;
            element.Margin = new Thickness(0, 2, 0, 0);
            Children.Add(element);
        }
    }

    public class PropertyDocument
    {
        public object Value { get; set; }
        public List<Property>? Children { get; private set; }

        private PropertyDocument(object instance)
        {
            Value = instance;
        }

        public static PropertyDocument? Load(object instance)
        {
            if (instance == null) return null;
            var doc = new PropertyDocument(instance);
            if (instance != null)
                doc.Children = Property.LoadProperties(instance, null, doc);
            return doc;
        }
    }

    public class Property
    {
        public PropertyInfo PropertyInfo { get; set; }
        public Property? Parent { get; set; }
        public List<Property>? Children { get; private set; }
        public PropertyDocument Document { get; set; }
        public string Path
        {
            get
            {
                if (Parent != null)
                    return $"{Parent.Path}.{PropertyInfo.Name}";
                else
                    return $"{PropertyInfo.Name}";
            }
        }

        public Property(PropertyInfo propertyInfo, Property? parent, PropertyDocument doc)
        {
            PropertyInfo = propertyInfo;
            Parent = parent;
            Document = doc;
            Children = LoadProperties(GetValue(), this, Document);
        }

        public object? GetValue()
        {
            return PropertyInfo.GetValue(Parent == null ? Document.Value : Parent.GetValue());
        }

        public bool CanSet()
        {
            return CanSet(PropertyInfo);
        }

        public static bool CanSet(PropertyInfo propertyInfo)
        {
            return propertyInfo.CanWrite && propertyInfo.SetMethod != null && (propertyInfo.SetMethod.Attributes & MethodAttributes.Public) > 0;
        }

        public static bool CanGet(PropertyInfo propertyInfo)
        {
            return propertyInfo.CanRead && propertyInfo.GetMethod != null && (propertyInfo.GetMethod.Attributes & MethodAttributes.Public) > 0;
        }

        public static List<Property>? LoadProperties(object? instance, Property? parent, PropertyDocument doc)
        {
            if(instance == null) return null;
            Type type = instance.GetType();
            object? value;
            List<Property> properties = new List<Property>();
            var ps = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            if (ps.Length <= 0 || type.IsValueType) return null;
            foreach (var p in ps)
            {
                if (!CanGet(p)) continue;
                value = p.GetValue(instance);
                Property property = new Property(p, parent, doc);
                properties.Add(property);
            }
            return properties;
        }
    }

View Code

使用方式:

<controls:PropertiesControl DockPanel.Dock="Right" Grid.Column="1" Source="{Binding Box}" RowHeight="30" Background="#FFEEEEEE" VerticalAlignment="Top"/>
    public class Box
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Width { get; set; }
        public double Height { get; set; }
        public Point Origin { get; set; }
        public bool Enabled { get; set; }
        public int Id { get; private set; }
    }
    public class TestVM : NotifyProperty, ITemplateView
    {
        public Box Box { get; set; }

        public TestVM()
        {
            Box = new Box { X = 100, Y = 100, Width = 100, Height = 100 };
        }
    }

直接绑定对象即可。