WPF使用ItemsControl显示Object的所有属性值

news2024/11/24 21:23:03

对于上位机开发,我们有时候有这样的需求:如何显示所有的IO点位?比如有10个IO点位,那我们要写10个TextBlock去绑定这10个点位的属性(本文暂时不考虑显示的样式,当然也可以考虑),当点位变成了20个,我们要再加10TextBlock去显示。
那么有没有一种方法,直接显示这个Object的所有属性的值,当值发生变化,自动更新界面呢?这个需求不就类似于PropertyGrid吗?但是我不想用PropertyGrid,我们使用ItemsControl去简单的实现一下。

定义要显示的IO点位

public class IOInfo : BindableBase
    {
        public IOInfo()
        {

        }
        private bool workLed1;
        public bool WorkLed1
        {
            get { return workLed1; }
            set { workLed1 = value; this.RaisePropertyChanged(nameof(WorkLed1)); }
        }

        private bool workLed2;
        public bool WorkLed2
        {
            get { return workLed2; }
            set { workLed2 = value; this.RaisePropertyChanged(nameof(WorkLed2)); }
        }

        private bool protectOn;
        public bool ProtectOn
        {
            get { return protectOn; }
            set { protectOn = value; this.RaisePropertyChanged(nameof(ProtectOn)); }
        }

        private bool pin1Status;
        public bool Pin1Status
        {
            get { return pin1Status; }
            set { pin1Status = value; this.RaisePropertyChanged(nameof(Pin1Status)); }
        }

        private bool pin2Status;
        public bool Pin2Status
        {
            get { return pin2Status; }
            set { pin2Status = value; this.RaisePropertyChanged(nameof(Pin2Status)); }
        }


        private bool cylinder1;
        public bool Cylinder1
        {
            get { return cylinder1; }
            set { cylinder1 = value; this.RaisePropertyChanged(nameof(Cylinder1)); }
        }

        private bool cylinder2;
        public bool Cylinder2
        {
            get { return cylinder2; }
            set { cylinder2 = value; this.RaisePropertyChanged(nameof(Cylinder2)); }
        }

        private bool bj;
        public bool BJ
        {
            get { return bj; }
            set { bj = value; this.RaisePropertyChanged(nameof(BJ)); }
        }

        private bool upSensor1;
        public bool UpSensor1
        {
            get { return upSensor1; }
            set { upSensor1 = value; this.RaisePropertyChanged(nameof(UpSensor1)); }
        }

        private bool upSensor2;
        public bool UpSensor2
        {
            get { return upSensor2; }
            set { upSensor2 = value; this.RaisePropertyChanged(nameof(UpSensor2)); }
        }

        private bool airPressure;
        public bool AirPressure
        {
            get { return airPressure; }
            set { airPressure = value; this.RaisePropertyChanged(nameof(AirPressure)); }
        }

        private bool doorStatus;
        public bool DoorStatus
        {
            get { return doorStatus; }
            set { doorStatus = value; this.RaisePropertyChanged(nameof(DoorStatus)); }
        }


        private bool smokeStatus;
        public bool SmokeStatus
        {
            get { return smokeStatus; }
            set { smokeStatus = value; this.RaisePropertyChanged(nameof(SmokeStatus)); }
        }

        private bool tempStatus;
        public bool TempStatus
        {
            get { return tempStatus; }
            set { tempStatus = value; this.RaisePropertyChanged(nameof(TempStatus)); }
        }

        private bool remoteStatus;
        public bool RemoteStatus
        {
            get { return remoteStatus; }
            set { remoteStatus = value; this.RaisePropertyChanged(nameof(RemoteStatus)); }
        }

        private bool trayStatus;
        public bool TrayStatus
        {
            get { return trayStatus; }
            set { trayStatus = value; this.RaisePropertyChanged(nameof(TrayStatus)); }
        }

        private bool startStatus;
        public bool StartStatus
        {
            get { return startStatus; }
            set { startStatus = value; this.RaisePropertyChanged(nameof(StartStatus)); }
        }

        private bool powerStatus;
        public bool PowerStatus
        {
            get { return powerStatus; }
            set { powerStatus = value; this.RaisePropertyChanged(nameof(PowerStatus)); }
        }

        private bool emergencyStatus;
        public bool EmergencyStatus
        {
            get { return emergencyStatus; }
            set { emergencyStatus = value; this.RaisePropertyChanged(nameof(EmergencyStatus)); }
        }

        private bool errTrace;
        public bool ErrorTrace
        {
            get { return errTrace; }
            set { errTrace = value; this.RaisePropertyChanged(nameof(ErrorTrace)); }
        }

    }

上述的BindableBase是引用了Prism框架

定义转换器

无论是ItemsControl还是PropertyGrid最终显示到界面的时候,都是一个集合。我们需要把object使用转换器转换为集合,转换为集合之前,首先定义ItemsControl要显示的Model:

public class PropertyAndValue : BindableBase
    {
        private string propertyName;
        /// <summary>
        /// 属性名称
        /// </summary>
        public string PropertyName
        {
            get { return propertyName; }
            set { propertyName = value; this.RaisePropertyChanged(nameof(PropertyName)); }
        }

        private string propertyNameAlias;
        /// <summary>
        /// 显示的别名,如果没有定义,则和属性名称一致
        /// </summary>
        public string PropertyNameAlias
        {
            get { return propertyNameAlias; }
            set { propertyNameAlias = value; this.RaisePropertyChanged(nameof(PropertyNameAlias)); }
        }


        private object propertyValue;
        /// <summary>
        /// 属性的值
        /// </summary>
        public object PropertyValue
        {
            get { return propertyValue; }
            set { propertyValue = value; this.RaisePropertyChanged(nameof(PropertyValue)); }
        }
    }

将转换器转换为Model的集合,这里重点要思考的是,IOInfo的属性变化,如何去更新界面,我采用简单粗暴的方式是手动订阅PropertyChanged事件:

public class IOStatusConverter : IValueConverter
    {
        private List<PropertyAndValue> values = new List<PropertyAndValue>();
        private IOInfo ioInfo = null;
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || !(value is IOInfo ioInfo)) return value;

            ioInfo.PropertyChanged += IoInfo_PropertyChanged;

            foreach (var property in typeof(IOInfo).GetProperties())
            {
                values.Add(new PropertyAndValue() { PropertyName = property.Name, PropertyNameAlias = property.Name, PropertyValue = property.GetValue(ioInfo) }); 
            }

            return values;
        }

        private void IoInfo_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            var v = values.FirstOrDefault(x => x.PropertyName == e.PropertyName);
            if (v == null) return;



            var property = typeof(IOInfo).GetProperty(e.PropertyName);
            if (property == null) return;

            v.PropertyValue = property.GetValue(sender);
        }
        

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Binding.DoNothing;
        }
    }

前台界面绑定

xaml的代码如下:

<ItemsControl Grid.Row="1" ItemsSource="{Binding IO,Converter={StaticResource IOStatusConverter}}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding PropertyName}" />
                <TextBlock Text=":" />
                <TextBlock Text="{Binding PropertyValue}" />
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

最终实现的效果

在这里插入图片描述
点击上方的修改按钮,它能更新界面的属性值。

改进

属性名称显示别名

要显示别名,我们要经过3个步骤。

  • 获取要显示的别名
  • 将别名赋值到PropertyNameAlias属性
  • PropertyNameAlias绑定到界面

我们使用DescriptionAttribute标签来实现别名,更改后的IOInfo类如下:

public class IOInfo : BindableBase
    {
        public IOInfo()
        {

        }
        private bool workLed1;
        [Description("灯1")]
        public bool WorkLed1
        {
            get { return workLed1; }
            set { workLed1 = value; this.RaisePropertyChanged(nameof(WorkLed1)); }
        }

        private bool workLed2;
        [Description("灯2")]
        public bool WorkLed2
        {
            get { return workLed2; }
            set { workLed2 = value; this.RaisePropertyChanged(nameof(WorkLed2)); }
        }

        private bool protectOn;
        public bool ProtectOn
        {
            get { return protectOn; }
            set { protectOn = value; this.RaisePropertyChanged(nameof(ProtectOn)); }
        }

        private bool pin1Status;
        public bool Pin1Status
        {
            get { return pin1Status; }
            set { pin1Status = value; this.RaisePropertyChanged(nameof(Pin1Status)); }
        }

        private bool pin2Status;
        public bool Pin2Status
        {
            get { return pin2Status; }
            set { pin2Status = value; this.RaisePropertyChanged(nameof(Pin2Status)); }
        }


        private bool cylinder1;
        public bool Cylinder1
        {
            get { return cylinder1; }
            set { cylinder1 = value; this.RaisePropertyChanged(nameof(Cylinder1)); }
        }

        private bool cylinder2;
        public bool Cylinder2
        {
            get { return cylinder2; }
            set { cylinder2 = value; this.RaisePropertyChanged(nameof(Cylinder2)); }
        }

        private bool bj;
        public bool BJ
        {
            get { return bj; }
            set { bj = value; this.RaisePropertyChanged(nameof(BJ)); }
        }

        private bool upSensor1;
        public bool UpSensor1
        {
            get { return upSensor1; }
            set { upSensor1 = value; this.RaisePropertyChanged(nameof(UpSensor1)); }
        }

        private bool upSensor2;
        public bool UpSensor2
        {
            get { return upSensor2; }
            set { upSensor2 = value; this.RaisePropertyChanged(nameof(UpSensor2)); }
        }

        private bool airPressure;
        public bool AirPressure
        {
            get { return airPressure; }
            set { airPressure = value; this.RaisePropertyChanged(nameof(AirPressure)); }
        }

        private bool doorStatus;
        public bool DoorStatus
        {
            get { return doorStatus; }
            set { doorStatus = value; this.RaisePropertyChanged(nameof(DoorStatus)); }
        }


        private bool smokeStatus;
        public bool SmokeStatus
        {
            get { return smokeStatus; }
            set { smokeStatus = value; this.RaisePropertyChanged(nameof(SmokeStatus)); }
        }

        private bool tempStatus;
        public bool TempStatus
        {
            get { return tempStatus; }
            set { tempStatus = value; this.RaisePropertyChanged(nameof(TempStatus)); }
        }

        private bool remoteStatus;
        public bool RemoteStatus
        {
            get { return remoteStatus; }
            set { remoteStatus = value; this.RaisePropertyChanged(nameof(RemoteStatus)); }
        }

        private bool trayStatus;
        public bool TrayStatus
        {
            get { return trayStatus; }
            set { trayStatus = value; this.RaisePropertyChanged(nameof(TrayStatus)); }
        }

        private bool startStatus;
        public bool StartStatus
        {
            get { return startStatus; }
            set { startStatus = value; this.RaisePropertyChanged(nameof(StartStatus)); }
        }

        private bool powerStatus;
        public bool PowerStatus
        {
            get { return powerStatus; }
            set { powerStatus = value; this.RaisePropertyChanged(nameof(PowerStatus)); }
        }

        private bool emergencyStatus;
        public bool EmergencyStatus
        {
            get { return emergencyStatus; }
            set { emergencyStatus = value; this.RaisePropertyChanged(nameof(EmergencyStatus)); }
        }

        private bool errTrace;
        public bool ErrorTrace
        {
            get { return errTrace; }
            set { errTrace = value; this.RaisePropertyChanged(nameof(ErrorTrace)); }
        }

    }

我们在WorkLed1WorkLed2这两个属性上打了两个标签,界面显示的时候,我们希望显示我们打上的标签的值。
现在我们把转换器修改下:

public class IOStatusConverter : IValueConverter
    {
        private List<PropertyAndValue> values = new List<PropertyAndValue>();
        private IOInfo ioInfo = null;
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || !(value is IOInfo ioInfo)) return value;

            ioInfo.PropertyChanged += IoInfo_PropertyChanged;

            foreach (var property in typeof(IOInfo).GetProperties())
            {
                var propertyName=property.Name;

                var propertyNameAlias = property.GetCustomAttribute<DescriptionAttribute>()?.Description ?? propertyName;

                values.Add(new PropertyAndValue() { PropertyName = propertyName, PropertyNameAlias = propertyNameAlias, PropertyValue = property.GetValue(ioInfo) }); 
            }

            return values;
        }

        private void IoInfo_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            var v = values.FirstOrDefault(x => x.PropertyName == e.PropertyName);
            if (v == null) return;



            var property = typeof(IOInfo).GetProperty(e.PropertyName);
            if (property == null) return;

            v.PropertyValue = property.GetValue(sender);
        }
        

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Binding.DoNothing;
        }
    }

界面显示绑定修改绑定到PropertyNameAlias属性:

<ItemsControl Grid.Row="1" ItemsSource="{Binding IO, Converter={StaticResource IOStatusConverter}}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding PropertyNameAlias}" />
                <TextBlock Text=":" />
                <TextBlock Text="{Binding PropertyValue}" />
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

最后的显示效果:
在这里插入图片描述

转换器的改进

通用转换器:

public class ObjectToListBaseConverter<T> : IValueConverter where T:INotifyPropertyChanged
    {
        private List<PropertyAndValue> values = new List<PropertyAndValue>();
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || !(value is T obj)) return value;//如果不是T的类型,则返回

            obj.PropertyChanged += Obj_PropertyChanged;

            foreach (var property in typeof(IOInfo).GetProperties())
            {
                var propertyName = property.Name;

                var propertyNameAlias = property.GetCustomAttribute<DescriptionAttribute>()?.Description ?? propertyName;

                values.Add(new PropertyAndValue() { PropertyName = propertyName, PropertyNameAlias = propertyNameAlias, PropertyValue = property.GetValue(obj) });
            }

            return values;
        }

        private void Obj_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var v = values.FirstOrDefault(x => x.PropertyName == e.PropertyName);
            if (v == null) return;

            var property = typeof(T).GetProperty(e.PropertyName);
            if (property == null) return;

            v.PropertyValue = property.GetValue(sender);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Binding.DoNothing;
        }
    }

IOStatusConverter修改如下,做到了代码的通用:

public class IOStatusConverter : ObjectToListBaseConverter<IOInfo>
{
    
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1672820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OBS插件--图片或视频源随着声音动态缩放

音效动态调整 应用此插件的源可以根据音效声音的高低进行动态的缩放。在本例中&#xff0c;我们在当前的场景里面添加了一个小喇叭的图片源&#xff0c;在这个图片源上引用这款滤镜插件&#xff0c;然后将VLC视频源的音效用于此插件的音效。设置完成后&#xff0c;场景中的小喇…

blender导出gltf模型混乱

最近用户给了几个blender文件&#xff0c;在blender打开是这样的&#xff1a; 我导出成gltf候&#xff0c;在本地打开时&#xff0c;底部发生了改变&#xff1a; 可以看出来&#xff0c;底部由原来的类型box变为了两个平面&#xff0c;后来我查了下blender里的属性设置&#xf…

三更草堂前后端分离个人博客项目的开发笔记

文章目录 项目实战-前后端分离博客系统1.课程介绍2.创建工程3.博客前台3.0 准备工作3.1 SpringBoot和MybatisPuls整合配置测试 3.1 热门文章列表3.1.0 文章表分析3.1.1 需求3.1.2 接口设计3.1.3 基础版本代码实现3.1.4 使用VO优化3.1.5 字面值处理 3.2 Bean拷贝工具类封装3.2 查…

flink优化案例

文章目录 一、flink join维表案例二、flink 双流join案例三、总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考(适用于flink1.13) 一、flink join维表案例 背景:flink sql join 维表。job业务不复杂&#xff0c;job写入性能比较差。维表数据大约每天…

【ubuntu20.04运行python文件时,报错No module named ‘rospkg’】

**问题原因&#xff1a;**一般来说&#xff0c;并不是真的缺少rospkg&#xff0c;而是系统中存在多个python版本导致的混乱 检查python版本 Ubuntu20.04 —— pyhon3.8 sudo apt-get install python3.8最新版本&#xff0c;如下图所示 查看python3.8的位置 whereis python…

C++ | Leetcode C++题解之第88题合并两个有序数组

题目&#xff1a; 题解&#xff1a; class Solution { public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {int p1 m - 1, p2 n - 1;int tail m n - 1;int cur;while (p1 > 0 || p2 > 0) {if (p1 -1) {cur nums2[p2-…

让创意在幻觉中肆虐: 认识Illusion Diffusion AI

人工智能新境界 在不断发展的人工智能领域,一款非凡的新工具应运而生,它能将普通照片转化为绚丽的艺术品。敬请关注Illusion Diffusion,这是一个将现实与想象力完美融合的AI驱动平台,可创造出迷人的视错觉和超现实意境。 AI算法的魔力所在 Illusion Diffusion 的核心是借助先进…

每日OJ题_贪心算法四⑧_力扣767. 重构字符串

目录 力扣767. 重构字符串 解析代码 力扣767. 重构字符串 767. 重构字符串 难度 中等 给定一个字符串 s &#xff0c;检查是否能重新排布其中的字母&#xff0c;使得两相邻的字符不同。 返回 s 的任意可能的重新排列。若不可行&#xff0c;返回空字符串 "" 。 …

测试页打印失败。是否要参阅打印疑难解答以获得帮助?服务器打印后台处理程序服务没有运行。请重新启动服务器上的打印后台处理程序或重新启动服务器计算机。

问题&#xff1f; 测试页打印失败。是否要参阅打印疑难解答以获得帮助? 解决办法&#xff1a; 方法1、 请重新启动服务器上的打印后台处理程序或重新启动服务器计算机。 方法2、 找到services服务找到print spooler停止运行&#xff0c; C:\Windows\System32\spool -------…

回炉重造java----JVM

为什么要使用JVM ①一次编写&#xff0c;到处运行&#xff0c;jvm屏蔽字节码与底层的操作差异 ②自动内存管理&#xff0c;垃圾回收功能 ③数组下边越界检查 ④多态 JDK&#xff0c;JRE&#xff0c;JVM的关系 JVM组成部分 JVM的内存结构 《一》程序计数器(PC Register) 作用…

记录MySQL数据库查询不等于xxx时的坑

目录 一、背景 二、需求 三、方法 四、示例 一、背景 在使用MySQL数据库查询数据时&#xff0c;需要查询字段name不等于xxx的记录&#xff0c;通过where name ! xxx查询出来的记录不符合预期&#xff0c;通过检查发现少了name字段为null的记录&#xff0c;后经查询得知在My…

Python | Leetcode Python题解之第88题合并两个有序数组

题目&#xff1a; 题解&#xff1a; class Solution:def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:"""Do not return anything, modify nums1 in-place instead."""p1, p2 m - 1, n - 1tail m n - 1whi…

【吃透Java手写】5-RPC-简易版

【吃透Java手写】RPC-简易版-源码解析 1 RPC1.1 RPC概念1.2 常用RPC技术或框架1.3 初始工程1.3.1 Productor-common&#xff1a;HelloService1.3.2 Productor&#xff1a;HelloServiceImpl1.3.3 Consumer 2 模拟RPC2.1 Productor2.2 模拟一个RPC框架2.2.1 HttpServer2.2.2 Http…

Elasticsearch解决字段膨胀问题

文章目录 背景Flattened类型的产生Flattened类型的定义基于Flattened类型插入数据更新Flattened字段并添加数据Flattened类型检索 Flattened类型的不足 背景 Elasticsearch映射如果不进行特殊设置&#xff0c;则默认为dynamic:true。dynamic:true实际上支持不加约束地动态添加…

【AI大模型】自动生成红队攻击提示--GPTFUZZER

本篇参考论文为&#xff1a; Yu J, Lin X, Xing X. Gptfuzzer: Red teaming large language models with auto-generated jailbreak prompts[J]. arXiv preprint arXiv:2309.10253, 2023. https://arxiv.org/pdf/2309.10253 一 背景 虽然LLM在今天的各个领域得到了广泛的运用…

AI办公自动化-用kimi自动清理删除重复文件

在kimichat中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个编写Python脚本的任务&#xff0c;具体步骤如下&#xff1a; 1、打开文件夹D:\downloads&#xff1b; 2、哈希值比较比较里面所有的文件&#xff0c;如果文件相同&#xff0c;那么移动多余…

3D Gaussian Splatting for Real-Time Radiance Field Rendering 论文阅读

如此热门的项目&#xff0c;网络上有很多大牛分析了这篇文章的做法&#xff0c;在这里简单记录一下个人粗浅的理解。 关于各种数学表达式的推导&#xff0c;论文和参考资料中都提供了较为详细的解读&#xff0c;本人能力有限&#xff0c;这一部分理解不够深刻&#xff0c;先不做…

绝地求生:艾伦格回归活动来了,持续近1个月,新版本皮肤、G币等奖励白嫖

嗨&#xff0c;我是闲游盒~ 29.2版本更新在即&#xff0c;新活动来啦&#xff01;目前这个活动国内官方还没发&#xff0c;我就去台湾官方搬来了中文版方便大家观看&#xff0c;也分析一下这些奖励应该怎样才能获得。 新版本将在周二进行约9小时的停机维护&#xff0c;请注意安…

centos7中如何优雅的动态切换jdk版本?

在 CentOS 7 中动态切换 JDK 版本可以通过多种方法实现&#xff0c;其中最常见的方法是使用 alternatives 命令&#xff0c;这是 CentOS 和其他基于 Red Hat 的系统中用于管理多个软件版本的标准工具。下面我会详细介绍如何使用 alternatives 命令来切换 JDK 版本。 步骤 1: 安…

如何通过 AWS Managed Apache Flink 实现 Iceberg 的实时同步

AWS Managed Apache Flink &#xff08;以下以 MAF 代指&#xff09;是 AWS 提供的一款 Serverless 的 Flink 服务。 1. 问题 大家在使用 MAF 的时候&#xff0c;可能遇到最大的一个问题就是 MAF 的依赖管理&#xff0c;很多时候在 Flink 上运行的代码&#xff0c;托管到 MAF…