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>
{
    
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

绝地求生PUBG新老艾伦格有什么差别 老艾伦格什么时候回归

复古风格的艾伦格原始地图携带着那些标志性的记忆符号华丽回归&#xff0c;邀请您沉浸于往昔的每一处细节探索中。我们不仅还原了游戏诞生的起点&#xff0c;还在其中巧妙融入现代游戏元素&#xff0c;构筑一座连接昔日与今朝的桥梁&#xff0c;完美融合了经典与创新的游戏体验…

【AI大模型应用开发】LATS:比ToT和ReAct更强大的大模型思维框架

大家好&#xff0c;我是 同学小张&#xff0c;持续学习C进阶知识和AI大模型应用实战案例&#xff0c;持续分享&#xff0c;欢迎大家点赞关注&#xff0c;共同学习和进步。 我们在大模型中常听说CoT&#xff08;思维链&#xff09;、ToT&#xff08;思维树&#xff09;&#xff…

深入理解C#中的IO操作:Path类的详解

文章目录 前言一、Path类的概述二、Path类的主要方法2.1 Path.GetFullPath(string relativePath)2.2 Path.GetDirectoryName(string path)2.3 Path.GetFileName(string path)2.4 Path.GetFileNameWithoutExtension(string path)2.5 Path.GetExtension(string path)2.6 Path.Com…

JUC下的Future 详解

java.util.concurrent.Future 是Java并发编程中一个重要的接口&#xff0c;它代表一个异步计算的结果。当你提交一个任务到执行器&#xff08;如ExecutorService的submit方法&#xff09;&#xff0c;它会返回一个Future对象。这个对象允许你查询任务是否完成、取消任务、获取任…

【2024年5月备考新增】】 考前篇(4)《官方平台 - 考生模拟练习平台常用操作(三)》

9 如何绘制七格图 第一步:选择图形元件“网络计划”中的七格图元件,在绘图区点击 第二步:批量复制(先选中元件,按Ctrl+C, 再 Ctrl+V) 七格形状,有多少活动复制多少个 选中多个图形后,可点左上角的对齐工具进行对齐; 第三步:逐个标注每个活动的参数,使用箭线连接每个…

vscode无法连接 , .vscode-server版本问题

vscode无法连接 &#xff0c; .vscode-server版本问题 解决办法 &#xff1a; 查看自己的版本号 2. 两边vscode版本号需要一致 找一台vscode可以远程连接的&#xff0c; 将它的.vscode-server/bin/b06ae3b2d2dbfe28bca3134cc6be65935cdfea6a 传到 远程服务器上 或者 本地的…

2024统计建模成品论文39页(附带完整数据集和代码)

2024统计建模成品论文完整版一等奖论文【1.5w字全网最佳】2024统计建模大赛高质量成品论文39页配套完整代码运行全套数据集https://www.jdmm.cc/file/2710661/

Vue的学习 —— <vue指令>

目录 前言 正文 内容渲染指令 内容渲染指令的使用方法 v-text v-html 属性绑定指令 双向数据绑定指令 事件绑定指令 条件渲染指令 循环列表渲染指令 侦听器 前言 在完成Vue开发环境的搭建后&#xff0c;若想将Vue应用于实际项目&#xff0c;首要任务是学习Vue的基…

内存屏障 - LINUX KERNEL MEMORY BARRIERS 上 与 下

内存屏障&#xff08;Memory Barrier&#xff09;是在计算机体系结构中使用的一种同步机制&#xff0c;用于确保在多线程或多核处理器环境中&#xff0c;对共享内存的操作按照预期顺序进行。它们通过强制在特定点执行一些指令来规定内存访问的顺序&#xff0c;并防止内存乱序执…

干货分享:AI知识库-从认识到搭建

随着知识库的出现&#xff0c;人工智能也逐渐加入进来&#xff0c;形成了“AI知识库”。也许将AI和知识库拆开&#xff0c;你能理解是什么意思&#xff0c;但是当两个词结合在一起时&#xff0c;你又真的能理解它是做什么的吗&#xff1f;这就是今天我们要来聊的话题&#xff0…

未来互联网:Web3的技术革新之路

引言 随着技术的不断发展和社会的日益数字化&#xff0c;互联网作为信息交流和社交媒介的重要平台已经成为我们生活中不可或缺的一部分。然而&#xff0c;传统的互联网架构在数据安全、隐私保护和去中心化等方面存在着诸多挑战。为了解决这些问题&#xff0c;Web3技术应运而生…

【PB案例学习笔记】-01创建应用、窗口与控件

写在前面 这是PB案例学习笔记系列文章的第一篇&#xff0c;也是最基础的一篇。后续文章中【创建程序基本框架】部分操作都跟这篇文章一样&#xff0c; 将不再重复。该系列文章是针对具有一定PB基础的读者&#xff0c;通过一个个由浅入深的编程实战案例学习&#xff0c;提高编…

uniapp编译H5解决ios的border-radius失效问题,以及ios满屏显示不全的问题

1.解决方案 .card-itemA {width: 650rpx;height: 326rpx;box-shadow: 0rpx 0rpx 30rpx 14rpx rgba(236, 235, 236, 0.25);background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0) 100%);border-radius: 60rpx;overflow: hidden;// 兼容ios的圆角问题transfor…

Centos7 配置 DNS服务器

Centos 7 配置DNS服务器 环境描述&#xff1a; 一台服务器和一台用于测试的客户机 服务器IP&#xff1a;192.168.200.132 客户机IP&#xff1a;192.168.200.143 服务器配置 yum install bind bind-utils -y #安装软件包vim /etc/named.conf //编辑named主配置文件listen-on p…

d18(169-174)-勇敢开始Java,咖啡拯救人生

目录 特殊文件 .properties 属性文件 读取属性文件 写出属性文件 .xml XML文件 读取XML文件 ​编辑 写出XML文件 约束XML文件 日志技术 Logback 日志级别 特殊文件 .properties 属性文件 每行都是一个键值对 键不能重复 文件后缀一般是.properties 读取属性文件 …

LeetCode 力扣题目:买卖股票的最佳时机 IV

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

华为手机恢复出厂设置后怎么还原数据?该如何预防数据丢失?

华为手机恢复出厂设置是将手机恢复到出厂时的初始状态&#xff0c;同时会删除所有用户数据和个人设置。如果不做任何预防措施&#xff0c;在恢复出厂设置后&#xff0c;您将丢失手机上的所有数据。那华为手机恢复出厂设置后怎么还原数据呢&#xff1f;以下是关于如何在华为手机…

Mac下安装ffmpeg

1、安装gedit brew install gedit2、配置环境变量&#xff0c;打开~/.zshrc&#xff0c;在末尾添加语句 export PATH$PATH:/usr/local/ffmpeg/bin3、执行语句&#xff0c;使环境变量生效 source ~/.zshrc 4、终端输入 ffmpeg &#xff0c;看环境变量是否配置成功。 至此&a…

c++高级篇(一) —— 初识Linux下的进程控制

linux的信号 信号的概念 在Linux中&#xff0c;信号是一种用于进程间通信和处理异步事件的机制&#xff0c;用于进程之间相互传递消息和通知进程发生了事件&#xff0c;但是&#xff0c;它不能给进程传递任何数据。 信号产生的原因有很多种&#xff0c;在shell中&#xff0c…

栈和队列的基础知识,C语言实现及经典OJ题

题目来源&#xff1a;力扣 基础知识 一.栈 1.栈的概念 定义&#xff1a;堆栈又名栈&#xff08;stack&#xff09;&#xff0c;它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶&#xff0c;相对地&#xff0c;把另一端称为栈底。 压…