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.xdnf.cn/news/1421264.html

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

相关文章

快手短剧,和爱优腾踏入同一条河流

文丨黄小艺 “我们定制短剧的重心排序分别是抖音、淘宝、快手。”MCN机构从业者周明&#xff08;化名&#xff09;说道&#xff0c;“无论是单条还是品牌冠名剧&#xff0c;我们在快手短剧拿到的收益都相对偏低。” 近期&#xff0c;商业数据派和多家机构创作者沟通后发现&am…

基于springboot实现高校教师电子名片系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现高校教师电子名片系统演示 摘要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;名片信息因为其管理内容繁杂&#xff…

Java入门基础学习笔记19——关系运算符、逻辑运算符

关系运算符&#xff1a; 判断数据是否满足条件&#xff0c;最终会返回一个判断的结果&#xff0c;这个结果是布尔类型的值&#xff1a;true或false。 注意&#xff1a;在java中判断是否相等一定是“”&#xff0c;不要把“”写成“”&#xff0c;“”是赋值表达式。 package c…

FANUC机器人初始化系统的基本方法和步骤

FANUC机器人初始化系统的基本方法和步骤 首先,在做系统初始化之前,必须做好系统的备份,这里做个镜像备份,更详细的镜像备份步骤可参考以下链接中的内容: FANUC机器人进行全部备份和镜像备份以及加载备份文件的具体操作(图文) 如下图所示,在示教器右边的USB接口上插个…

【安全每日一讲】加强数据安全保护 共享数字化时代便利

前言 数据安全是数据治理的核心内容之一&#xff0c;随着数据治理的深入&#xff0c;我不断的碰到数据安全中的金发姑娘问题&#xff08;指安全和效率的平衡&#xff09;。 DAMA说&#xff0c;降低风险和促进业务增长是数据安全活动的主要驱动因素&#xff0c;数据安全是一种资…

数据结构(一)绪论

2024年5月11日 一稿 数据元素+数据项 逻辑结构 集合 线性结构 树形结构 图结构

其他的 框架安全:Apache Shiro 漏洞序列.(CVE-2016-2807)

什么是 Apache Shiro Apache Shiro 是一个强大且易用的Java安全框架&#xff0c;它为应用程序提供了身份验证、授权、加密和会话管理等常见的安全功能。漏洞大多会发生在登录处&#xff0c;返回包里包含remeberMedeleteMe字段.&#xff08; Shiro 这个属于第三方的&#xff0c…

Redis数据结构扩容源码分析

1 Redis数据结构 redis的数据存储在dict.中&#xff0c;其数据结构为(c源码) ypedef struct dict { dictType *type; //理解为面向对象思想&#xff0c;为支持不同的数据类型对应dictType抽象方法&#xff0c;不同的数据类型可以不同实现 void *privdata; //也可不同的数据类…

数据缓存,可以尝试RocksDB了

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen shigen在最近的学习中&#xff0c;接触到了一款新的缓存数据库RocksDB&#xff…

PyQt5中的LineEdit单行文本框

文章目录 1. 简介1.1 常用方法&#xff1a;1.2 常用信号&#xff1a; 2. LineEdit常用方法使用案例3. LineEdit常用信号使用案例 1. 简介 在PyQt5中&#xff0c;LineEdit&#xff08;单行文本框&#xff09;是一个常用的组件&#xff0c;它允许用户输入文本。以下是一些LineEd…

SpringBoot整合SpringScurity权限控制(菜单权限,按钮权限)以及加上SSH实现安全传输

文章目录 项目地址&#xff1a; 一、md5 与 先进的哈希算法的区别1.1. 安全性问题1.2. 设计目的1.3. 功能特性1.4. 适用性1.5. 总结 二、数据传输安全和数据加密实现&#xff1a;2.1 生成证书&#xff1a;2.2、在springboot中进行集成2.2.1 配置证书&#xff1a;2.2.2. 强制使用…

MySQL·索引

目录 索引的意义 索引的理解 为何IO交互要是 Page 理解Page 其他数据结构为何不行&#xff1f; 聚簇索引 VS 非聚簇索引 索引操作 主键索引操作 唯一键索引操作 普通索引的创建 总结 全文索引 索引的意义 索引&#xff1a;提高数据库的性能&#xff0c;索引是物美…

LangChain:模型 I/O 封装使用解析和感触

目录 模型 API&#xff1a;LLM vs. ChatModel OpenAI 模型封装 多轮对话 Session 封装 换个国产模型 模型的输入与输出 Prompt 模板封装 PromptTemplate ChatPromptTemplate MessagesPlaceholder 从文件加载 Prompt 模板 TXT模板 Yaml模板 Json模板 输出封装 Out…

240512-关于如何用VSCode编写C#程序的简单说明

240512-关于如何用VSCode编写C#程序的简单说明 从安装软件开始 &#xff0c;到编写一个HelloWorld的C#文件结束&#xff0c;介绍如何用VSCode编写C#程序 1 上官网下载一个安装包 官网地址&#xff1a;https://visualstudio.microsoft.com/zh-hans/downloads/ 2 打开安装包进…

嵌入式学习-中断控制系统

补充一下前面NVIC内嵌向量中断控制器的知识 中断 中断类型 中断控制 配置中断 优先级 分组问题 中断使能 NVIC相关库函数和作用 库函数 函数名 描述 NVIC_DeInit 将外设 NVIC 寄存器重设为初始值 NVIC_SCBDeInit 将外设 SCB 寄存器重设为初始值 NVIC_PriorityGroupCon…

Node.js全栈:从一个简单的例子开始

第一章&#xff1a;从一个简单的例子开始第二章&#xff1a;看官方文档的艺术第三章&#xff1a;浏览器显示一个网页 首先&#xff0c;在VSCode编辑器中打开一个没有任何文件的空目录&#xff0c;然后创建一个package.json文件。 为了方便大家复制&#xff0c;我把文件内容放到…

十进制整数转平衡三进制

求解原视频&#xff1a;平衡三进制 求赞&#xff01;100赞买个乒乓球拍&#xff01;_哔哩哔哩_bilibili 题目&#xff1a; 上海市计算机学会竞赛平台 | YACS 求解程序&#xff1a; using namespace std; #include <iostream> #include <cstring>string work(int n…

Zabbix6.0容器化部署(Docker-Composed)

Zabbix 为每个 Zabbix 组件提供 Docker image 作为可移植和自给自足的容器&#xff0c;以加快部署和更新过程。 Zabbix 组件在 Ubuntu、Alpine Linux 和 CentOS 基础 image 上提供:Zabbix 组件支持 MySQL 和 PostgreSQL 数据库、Apache2 和 Nginx Web 服务器。 1. Zabbix 组件…

开源流程引擎选型 —— Activiti、Flowable、Camunda

目录 一. 前言 二. 主流开源流程引擎介绍 2.1. Osworkflow 2.2. JBPM 2.3. Activiti 2.4. Flowable 2.5. Camunda 三. Flowable 与 Camunda 对比分析 3.1. 功能方面对比 3.2. 性能方面对比 四. 总结 一. 前言 市场上比较有名的开源流程引擎有 Osworkflow、JBPM、Act…

C控制语句:分支和跳转

1.1if语句 //colddays.c --找出0摄氏度以下的天数占总天数的百分比 #include <stdio.h>int main(void) {const int FREEZING 0;float temperature;int cold_days 0;int all_days 0;printf("Enter the list of daily low temperature.\n");printf("Use…