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

对于上位机开发,我们有时候有这样的需求:如何显示所有的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.mzph.cn/web/13303.shtml

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

相关文章

霍庭格TruPlasma MF 7100 7050电源现货50KW

霍庭格TruPlasma MF 7100 7050电源现货50KW

【C语言深度解剖】:(11)函数指针、函数指针数组、指向函数指针数组的指针、回调函数

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《C语言深度解剖》《精通C指针》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多C语言深度解剖点击专栏…

AVDemo漏洞平台黑盒测试

信息收集 说明一下&#xff1a; 因为是本地的环境&#xff0c;端口这些就不扫描了&#xff0c; 还有这个是某个dalao写的平台&#xff0c;也就检测不到什么cms了&#xff0c; 信息收集&#xff0c;端口&#xff0c;cms这些是必做的&#xff0c; 首先&#xff0c;这里先简单的…

web3 ETF软件开发难点

开发一个涉及到 Web3 ETF&#xff08;Exchange-Traded Fund&#xff0c;交易所交易基金&#xff09;的软件可能会面临一些挑战和难点&#xff0c;特别是在整合 Web3 技术和金融服务方面。以下是一些可能的难点。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&am…

记一次:mysql统计的CAST函数与json字段中的某个字段

前言&#xff1a;因为需求的问题&#xff0c;会遇到将某个json存入到一个字段中&#xff0c;但在统计的时候&#xff0c;又需要将这个json中的某个字段作为条件来统计&#xff0c;所以整理了一下cast函数和json中某个字段的条件判断 一、浅谈mysql的json 1.1 上例子 SELECTli…

植物大战僵尸杂交版(含下载方式)

最近时间&#xff0c;一款很火的植物大战僵尸杂交版火爆出圈&#xff0c;在玩家之间疯狂扩散。各种奇特的杂交组合让游戏变得更加有趣。 游戏介绍 植物大战僵尸杂交版是一款将《植物大战僵尸》和植物杂交概念结合在一起的独特塔防策略游戏。它将《植物大战僵尸》中的植物与进行…

Minio 对象存储 OSS概述

系列文章目录 第五章 Minio 对象存储 OSS概述 Minio 对象存储 OSS概述 系列文章目录对象存储 OSS基本概念存储空间&#xff08;Bucket&#xff09;对象&#xff08;Object&#xff09;ObjectKeyRegion&#xff08;地域&#xff09;Endpoint&#xff08;访问域名&#xff09;Ac…

C#知识|上位机子窗体嵌入主窗体方法(实例)

哈喽,你好啊,我是雷工! 上位机开发中,经常会需要将子窗体嵌入到主窗体, 本节练习C#中在主窗体的某个容器中打开子窗体的方法。 01 需求说明 本节练习将【账号管理】子窗体在主窗体的panelMain容器中打开。 账号管理子窗体如下: 主窗体的panelMain容器位置如图: 02 实现…

一次JAVA接口优化记录

目录 一次接口优化记录首先考虑&#xff0c;添加缓存缓存策略方案一&#xff1a;本地缓存方案二&#xff1a;Redis缓存 优化结果原因分析&#xff1a;原因验证 接口数据分析将响应数据返回大小减少compression压缩配置完美&#xff08;代指这里的小系统&#xff09; 一次接口优…

CentOS 的常见命令

CentOS 是一种广泛使用的 Linux 发行版&#xff0c;特别在服务器环境中。本文将详细介绍 CentOS 中常见的命令&#xff0c;以便帮助用户在操作系统中有效地进行各种操作。下面介绍一下文件和目录操作、用户和权限管理、系统信息查看、软件包管理以及网络配置等方面的命令。 一…

应用层协议【HTTP和HTTPS】

1.概念 1.1 协议 协议是指在计算机通信和网络通信中&#xff0c;为了实现数据交换而建立的一套规则、约定或者标准。它定义了通信双方之间的通信格式、传输方式、数据的含义、错误处理等细节&#xff0c;从而确保通信的可靠性、有效性和安全性。 >1在计算机网络中&#x…

注册讲堂 | 体外诊断试剂分类目录的变化

5月11日&#xff0c;千呼万唤的《体外诊断试剂分类目录》&#xff08;2024年第58号&#xff09;终于发布&#xff01; 前世今生 2013年&#xff1a;《6840 体外诊断试剂分类子目录&#xff08;2013版&#xff09;》&#xff08;以下简称2013版目录&#xff09; 2017年&#xff…

苹果永久版安装PD虚拟机:Parallels Desktop 19 一键激活版

Parallels Desktop 19是一款功能强大的虚拟机软件&#xff0c;专为Mac用户设计&#xff0c;允许用户在同一台Mac电脑上同时运行Windows、Linux等多个操作系统&#xff0c;而无需额外的硬件设备。 下载地址&#xff1a;https://www.macz.com/mac/9581.html?idOTI2NjQ5Jl8mMjcuM…

Kubernetes入门:核心概念

集群架构与组件 一个kubernetes集群主要是由控制节点(master)、工作节点(node)构成&#xff0c;每个节点上都会安装不同的组件。 master&#xff1a;集群的控制平面&#xff0c;负责集群的决策 ( 管理 ) api-server : 资源操作的唯一入口&#xff0c;接收用户输入的命令&…

文本控件Text Control示例: 将图像插入 TX 的各种方法

TX Text Control 是一款功能类似于 MS Word 的文字处理控件&#xff0c;包括文档创建、编辑、打印、邮件合并、格式转换、拆分合并、导入导出、批量生成等功能。广泛应用于企业文档管理&#xff0c;网站内容发布&#xff0c;电子病历中病案模板创建、病历书写、修改历史、连续打…

在Linux上面部署ELK

注明&#xff1a;一下的软件需要自己准备 一、准备环境&#xff1a; 1.两台elasticsearch主机4G内存 2.两台elasticsearch配置主机名node1和node2(可以省略) #vim /etc/hostname #reboot 3. 两台elasticsearch配置hosts文件 #vim /etc/hosts 192.168.1.1 node1 192…

RTMP低延迟推流

人总是需要压力才能进步, 最近有个项目, 需要我在RK3568上, 推流到公网, 最大程度的降低延迟. 废话不多说, 先直接看效果: 数据经过WiFi发送到Inenter的SRS服务器, 再通过网页拉流的. 因为是打金任务, 所以逼了自己一把, 把RTMP推流好好捋一遍. 先说说任务目标, 首先是MPP编码…

【Altium】AD-检查原理图中元器件未连接的Passive Pin

1、 文档目标 如何让原理图编译时找出元器件上未连接的Passive Pin 2、 问题场景 当引脚属性&#xff08;Pin type&#xff09;为passive时&#xff0c;原理图编译的默认规则是不会去检查它们是否有连接的。在实际设计过程中&#xff0c;经常会有导线虚连&#xff0c;漏连的事…

医疗传感器种类不断增多 市场规模逐渐扩大

医疗传感器种类不断增多 市场规模逐渐扩大 医疗传感器是将人体的生理信息转换为电信息的变换装置。医疗传感器具有高灵敏度、高精度、实时监测等优点&#xff0c;可以检测佩戴者的心率、呼吸频率、活动量等&#xff0c;从而更加准确地了解身体情况。   经过多年发展&#…

【极简】docker常用操作

镜像images是静态的 容器container是动态的&#xff0c;是基于镜像的&#xff0c;类似于一个进程。 查看docker images&#xff1a; docker images 或者docker image ls 查看docker container情况&#xff1a;docker ps -a&#xff0c;-a意思是--all 运行一个container: doc…