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,一经查实,立即删除!

相关文章

springboot整合swagger,jpa遇到的问题

1.整合jpa&#xff0c;版本问题导致Archive for required library: ‘C:/Users/Administrator/.m2/repository/org/aspectj/aspectjweaver/1.8.13/aspectjweaver-1.8.13.jar’ in project ‘money-server’ cannot be read or is not a valid ZIP file money-server Build path…

Leetcode 257:二叉树的所有路径

给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 思路&#xff1a; 先编辑所有节点&#xff0c;记录每一个节点的路径&#xff1b; 判断当前节点是否为叶子节点&#xff0c;如果是&…

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

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

mysql json 数组怎么搜索

在MySQL中&#xff0c;可以使用JSON_CONTAINS函数来搜索JSON数组中的元素。这里有一个简单的例子&#xff1a; 假设有一个名为items的表&#xff0c;其中有一个名为attributes的列&#xff0c;包含JSON数组。 CREATE TABLE items (id INT AUTO_INCREMENT PRIMARY KEY,attribu…

SQLSERVER 怎样使查询不占锁

对一些相对不怎么敏感的数据&#xff0c;不需要太及时性的数据&#xff0c;不需要占锁。 要在SQL Server中执行查询而不占用锁&#xff0c;可以采取以下几个策略&#xff1a; 1、使用NOLOCK提示&#xff1a; 最直接但风险较高的方法是在查询中使用WITH (NOLOCK)提示。这样&am…

练习题(2024/5/16)

1轮转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步: [5,…

【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;在玩家之间疯狂扩散。各种奇特的杂交组合让游戏变得更加有趣。 游戏介绍 植物大战僵尸杂交版是一款将《植物大战僵尸》和植物杂交概念结合在一起的独特塔防策略游戏。它将《植物大战僵尸》中的植物与进行…

什么是析构函数?

在编程语言C中&#xff0c;析构函数是一个特别重要的组件&#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…

Python简易图书管理系统重构

在本篇课文中&#xff0c;我们将使用Python语言结合MySQL数据库&#xff0c;从零开始构建一个简单的图书管理系统。该系统旨在帮助图书馆管理员轻松管理图书的借阅、归还以及查询图书信息等日常操作。我们将分步介绍需求分析、数据库设计、环境搭建、功能实现等关键环节&#x…

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

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