WPF Binding 绑定

绑定是 wpf 开发中的精髓,有绑定才有所谓的数据驱动。

1 . 背景

目前 wpf 界面可视化的控件,继承关系如下,
在这里插入图片描述

控件的数据绑定,基本上都要借助于 FrameworkElement 的 DataContext 属性。

只有先设置了控件的 DataContext 属性,再设置绑定才能生效(TemplateBinding、使用 RelativeSource、后台 new Binding、静态资源等特殊情况另外讨论)。

2. 绑定的参与对象

绑定过程涉及到两个重要的对象

  1. 绑定目标

数据源绑定到的 依赖属性,只有依赖属性才能作为绑定目标。例如 TextBlock.TextPropertyItemsControl.ItemsSourceProperty

不是只有控件才有依赖属性,普通 CLR 类也可以包括依赖属性。

  1. 绑定源

依赖属性绑定的数据源,例如类中的 属性、界面中的 资源 等。

3. 使用 DataContext 的数据绑定

定义一个包含 2 个只读属性的类型,

public class SampleViewModel
{public string Title { get; }public int Count { get; }public SampleViewModel(){Title = "Hello~";Count = 100;}
}

界面以 UserControl 为例,在构造函数中设置整个界面的 DataContext。

DataContext 会自动从父类向子类传递,子元素也可重新设置 DataContext 属性为其它值。

设置界面控件的 DataContext,

public partial class BindingView : UserControl
{public BindingView(){InitializeComponent();DataContext = new SampleViewModel();}
}

3.1 在前端代码中设置绑定

<UserControlx:Class="WpfApp1.Views.BindingView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:viewModels="clr-namespace:WpfApp1.ViewModels"d:DataContext="{d:DesignInstance viewModels:SampleViewModel}"d:DesignHeight="450"d:DesignWidth="800"mc:Ignorable="d"><Grid Background="DeepPink" Width="100" Height="100"><StackPanel><TextBlock Margin="4" Text="{Binding Title}" /><TextBlock Margin="4" Text="{Binding Count}" /></StackPanel></Grid>
</UserControl>

显示结果,
在这里插入图片描述

3.2 在后端代码中设置绑定

将前端绑定代码去掉,并设置控件的 x:Name 属性值,

<Grid Background="DeepPink" Width="100" Height="100"><StackPanel><TextBlock x:Name="Text1" Margin="4" /><TextBlock x:Name="Text2" Margin="4" /></StackPanel>
</Grid>

在后端中手动绑定,

public BindingView()
{InitializeComponent();DataContext = new SampleViewModel();//后台代码绑定var binding1 = new Binding("Title"){Source = this.DataContext,};var binding2 = new Binding("Count"){Source = this.DataContext,};this.Text1.SetBinding(TextBlock.TextProperty, binding1);this.Text2.SetBinding(TextBlock.TextProperty, binding2);
}

同样可以实现绑定。

上面的例子,绑定参与对象是:

绑定目标:TextBlock.TextProperty 依赖属性
绑定源:this.DataContext 即 SampleViewModel 实例
绑定路径:TitleCount

也可以简单点说,

绑定目标:TextBlock.TextProperty 依赖属性
绑定源: SampleViewModel 实例的 TitleCount 属性

一般推荐直接在前端绑定,有特殊业务需求才会在后台动态绑定。

4. 不使用 DataContext 的数据绑定

不设置 DataContext 的情况下, 也可以使用绑定,比如绑定界面控件或控件的属性、页面资源等。

xmlns:system="clr-namespace:System;assembly=mscorlib"
<UserControl.Resources><system:String x:Key="Content">我是静态资源</system:String>
</UserControl.Resources><Grid Background="DeepPink" Width="100" Height="100"><StackPanel><!-- 绑定 CLR 属性 --><TextBlock x:Name="Text1" Margin="4" Text="{Binding Title}" /><!-- 绑定控件 --><TextBlock x:Name="Text2" Margin="4" Text="" Tag="{Binding ElementName=Text1}" /><!-- 绑定控件属性 --><TextBlock x:Name="Text3" Margin="4" Text="{Binding ElementName=Text2, Path=Tag.Text}" /><!-- 绑定页面资源 --><TextBlock x:Name="Text4" Margin="4" Text="{Binding ., Source={StaticResource Content}}" /></StackPanel>
</Grid>

显示,
在这里插入图片描述

总结,

绑定目标必须是依赖属性,绑定源可以是依赖属性也可以是普通 CLR 属性、静态资源。

5. 绑定模式

以上演示的是简单的数据绑定,仅仅是用来显示,如果我还想把界面数据传回数据源,该如何操作?

以 CheckBox 勾选框为例,

<Grid Background="DeepPink" Width="100" Height="100"><CheckBox Margin="4" Content="Mark" IsChecked="{Binding Mark}" />
</Grid>

在这里插入图片描述
绑定源:

public class BindingModeViewModel
{private bool _mark = true; //默认勾选public bool Mark{get => _mark;set{if (_mark != value){_mark = value;Console.WriteLine($"Mark changed to {_mark}");}}}
}

取消勾选并再次勾选,结果,

Mark changed to False
Mark changed to True

接下来对控件绑定做一些改动,

<CheckBox Margin="4" Content="Mark" IsChecked="{Binding Mark, Mode=OneWay}" />

发现操作 CheckBox,不再会打印信息了。那是因为设置绑定模式 Mode=OnWay 就表示只支持从数据源到界面目标的单向绑定。

绑定模式有如下几种,

public enum BindingMode
{TwoWay, //双向绑定OneWay, //单向绑定:从数据源到目标OneTime, //单次绑定:只绑定一次OneWayToSource, //单向绑定:从目标到数据源Default, //默认绑定:根据数据源和目标自动决定
}

所以,这里我把 Mode 设置为 TwoWay、OneWayToSource、Default(不设置就是默认绑定)都可以打印出来信息。

到此,演示了:

  1. 从数据源获取数据到目标(如 TextBlock)
  2. 从目标设置数据到数据源(如 CheckBox)

那如果,我要从数据源设置动态数据到目标,该如何操作?这就要用到一个接口。

6. INotifyPropertyChanged 接口

public interface INotifyPropertyChanged
{event PropertyChangedEventHandler PropertyChanged;
}

INotifyPropertyChanged 是一个接口,用于在属性值发生变化时通知绑定的客户端。在 WPF 应用程序中,这通常用于实现数据绑定,使得 UI 元素能够在源数据动态变化时自动更新。

简单实现,

public class MyNotifyClass : INotifyPropertyChanged
{public event PropertyChangedEventHandler PropertyChanged;public void RaisePropertyChanged(string propertyName){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
}

改动一下数据源,

public class BindingModeViewModel : MyNotifyClass
{private bool _mark = true;private string _printMessage;public bool Mark{get => _mark;set{if (_mark != value){_mark = value;PrintMessage = $"Mark changed to {_mark}";}}}public string PrintMessage{get => _printMessage;set{_printMessage = value;RaisePropertyChanged(nameof(PrintMessage));}}
}

调整一下界面,

<Grid Background="DeepPink" Width="200" Height="100"><StackPanel><CheckBox Margin="4" Content="Mark" IsChecked="{Binding Mark}" /><TextBlock Margin="4" Text="{Binding PrintMessage}" /></StackPanel>
</Grid>

点击 CheckBox , TextBlock 会动态显示信息。
在这里插入图片描述
CheckBox 只是通过绑定属性,利用属性 Set 方式触发数据源变化。

如果要像按钮 Button 那样,直接通过方法来触发数据源变化,如果操作呢?就要用到命令绑定。

7. 命令绑定

在 WPF 中,命令绑定是 MVVM 模式的核心部分之一,它允许开发者将视图中的事件如按钮点击与 ViewModel 中的命令逻辑相绑定。这样,当用户界面上的按钮被点击时,可以触发 ViewModel 中定义的命令执行相应的操作,而不需要在代码后面编写事件处理函数。

以按钮 Button 控件为例,按钮的 Command 属性类型是一个 ICommand 接口类型,

public ICommand Command
{get => (ICommand) this.GetValue(ButtonBase.CommandProperty);set => this.SetValue(ButtonBase.CommandProperty, (object) value);
}
  public interface ICommand{/// <summary>当出现影响是否应执行该命令的更改时发生。</summary>event EventHandler CanExecuteChanged;/// <summary>定义确定此命令是否可在其当前状态下执行的方法。</summary>/// <param name="parameter">此命令使用的数据。  如果此命令不需要传递数据,则该对象可以设置为 <see langword="null" />。</param>/// <returns>如果可执行此命令,则为 <see langword="true" />;否则为 <see langword="false" />。</returns>bool CanExecute(object parameter);/// <summary>定义在调用此命令时要调用的方法。</summary>/// <param name="parameter">此命令使用的数据。  如果此命令不需要传递数据,则该对象可以设置为 <see langword="null" />。</param>void Execute(object parameter);}

因此我们只要实现一下 ICommand 接口即可使用自己的命令。

例如,

public class MyCommand : ICommand
{private readonly Action<object> _executeAction;private readonly Func<object, bool> _canExecuteFunc;public event EventHandler CanExecuteChanged;public MyCommand(Action<object> executeAction, Func<object, bool> canExecuteFunc = null){_executeAction = executeAction;_canExecuteFunc = canExecuteFunc;}public void Execute(object parameter){_executeAction?.Invoke(parameter);}public bool CanExecute(object parameter){return _canExecuteFunc == null || _canExecuteFunc(parameter);}public void RaiseCanExecute(){CanExecuteChanged?.Invoke(this, EventArgs.Empty);}
}

在数据源增加相应的命令属性,

public class BindingModeViewModel : MyNotifyClass
{private bool _mark = true;private string _printMessage;public bool Mark{get => _mark;set{if (_mark != value){_mark = value;PrintMessage = $"Mark changed to {_mark}";//更新按钮可点击状态PrintCommand.RaiseCanExecute();}}}public string PrintMessage{get => _printMessage;set{_printMessage = value;RaisePropertyChanged(nameof(PrintMessage));}}public MyCommand PrintCommand { get; }public BindingModeViewModel(){PrintCommand = new MyCommand(Print, CanPrint);}private void Print(object obj){PrintMessage = "This is Button Print.";}private bool CanPrint(object arg){return _mark;}
}

界面增加一个按钮,

<Grid Background="DeepPink" Width="200" Height="100"><StackPanel><CheckBox Margin="4" Content="Mark" IsChecked="{Binding Mark}" /><TextBlock Margin="4" Text="{Binding PrintMessage}" /><Button Content="Print" Width="80" Height="32" Command="{Binding PrintCommand}" /></StackPanel>
</Grid>

点击按钮,命令调用成功,
在这里插入图片描述
取消勾选, 按钮变灰,不可执行命令,
在这里插入图片描述

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

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

相关文章

Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)

01.生命周期 Vue生命周期&#xff1a;就是一个Vue实例从创建 到 销毁 的整个过程 生命周期四个阶段&#xff1a;① 创建 ② 挂载 ③ 更新 ④ 销毁 1.创建阶段&#xff1a;创建响应式数据 2.挂载阶段&#xff1a;渲染模板 3.更新阶段&#xff1a;修改数据&#xff0c;更新视图 4…

AI芯片常见概念

文章目录 AI芯片常见概念前言常见概念AI芯片分类按照芯片的技术架构分GPU半定制化的 FPGA全定制化 ASIC神经拟态芯片 按应用场景分训练卡推理卡 按部署位置分国产AI卡资料汇总 AI芯片算力和能效比AI芯片算力AI芯片能效比 封装相关Chiplet技术3DIC三星多芯片集成联盟&#xff08…

边缘智能网关助力打造建筑智慧消防物联网

随着经济社会的快速发展&#xff0c;为了满足民众生产、生活、消费需求&#xff0c;高层建筑、大型综合连体建筑持续兴建&#xff0c;各类火灾风险和事故也越发增加。得益于物联网的普及应用&#xff0c;消防监测和管理迎来数字化、智慧化转型升级。 针对各类高层、大型建筑消防…

深度学习实战车辆目标跟踪【bytetrack/deepsort】

本文采用YOLOv8作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv8以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对车辆目标数据集进行训练和优化&#xff0c;该数据集包含丰富的车辆目标图像样本…

电脑开机提示error loading operating system怎么修复?

前一天电脑还能正常运行&#xff0c;但今天启动时却显示“Error loading operating system”&#xff08;加载操作系统错误&#xff09;。我已经仔细检查了硬盘、接线、内存、CPU和电源&#xff0c;确认这些硬件都没有问题。硬盘在其他电脑上可以正常使用&#xff0c;说明不是硬…

财会〔2024〕22号发布,全面提高管理会计数字化、智能化水平,泛微·齐业成来助力

自《财政部关于全面推进管理会计体系建设的指导意见》&#xff08;财会〔2014〕27号&#xff09;发布以来&#xff0c;我国管理会计体系建设取得明显成效。时隔十载&#xff0c;2024年12月16日财政部发布《关于全面深化管理会计应用的指导意见》财会〔2024〕22 号&#xff08;以…

数字经济下的 AR 眼镜

目录 1. &#x1f4c2; AR 眼镜发展历史 1.1 AR 眼镜相关概念 1.2 市面主流 XR 眼镜 1.3 AR 眼镜大事记 1.4 国内外 XR 眼镜 1.5 国内 AR 眼镜四小龙 2. &#x1f531; 关键技术 2.1 AR 眼镜近眼显示原理 2.2 AR 眼镜关键技术 2.3 AR 眼镜技术难点 3. &#x1f4a…

浅析InnoDB引擎架构(已完结)

大家好&#xff0c;我是此林。 今天来介绍下InnoDB底层架构。 1. 磁盘架构 我们所有的数据库文件都保存在 /var/lib/mysql目录下。 由于我这边是docker部署的mysql&#xff0c;用如下命令查看mysql数据挂载。 docker inspect mysql-master 如下图&#xff0c;目前只有一个数…

k8s迁移——岁月云实战笔记

新系统使用rockylinux9.5&#xff0c;旧系统虚拟机装的是centos7 1 目标服务器 1.1 禁止swap swapoff -a vi /etc/fstab #/dev/mapper/rl-swap none swap defaults 0 0 #执行&#xff0c;swap一行都是0 free -h 1.2 关闭防火墙 只是为了减…

砂轮磨料基础知识及发展学习笔记

平时接触磨削的工序有很多&#xff0c;像平面、外圆&#xff0c;齿轮的齿形磨削&#xff0c;刀具的前刃及齿形磨削等等&#xff0c;花了些时间&#xff0c;整理了一些资料&#xff0c;把关于磨料的内容整理了一下。有需要的小伙伴可以耐心阅读一下。 从古代使用的简陋石头到如今…

【Spring】Spring框架之-AOP

目录 1. AOP的引入 2. AOP相关的概念 2.1 AOP概述 2.2 AOP的优势 2.3. AOP的底层原理--目前先不具体阐述&#xff0c;后面讲 3. Spring的AOP技术-配置文件方式 3.1 AOP相关的术语 3.2 基本准备工作 3.3 AOP配置文件方式的入门 3.4 切入点的表达式 3.5 AOP的通知类型 …

Servlet学习中遇到的一些问题及解决

错误&#xff1a;JavaWeb-错误&#xff1a;类xxx不是Servlet 解决&#xff1a;可能是Tomcat版本不匹配导致&#xff0c;更换Tomcat版本解决问题 错误&#xff1a;在自定义的Servlet类中不能添加 WebServlet 注解 解决&#xff1a;可能是WebServlet版本不匹配&#xff0c;更换…

tomcat的安装以及配置(基于linuxOS)

目录 安装jdk环境 yum安装 验证JDK环境 安装tomcat应用 yum安装 ​编辑 使用yum工具进行安装 配置tomcat应用 关闭防火墙和selinux 查看端口开启情况 ​编辑 访问tomcat服务 安装扩展包 重启服务 查看服务 源码安装 进入tomcat官网进行下载 查找自己要用的to…

workman服务端开发模式-应用开发-gateway长链接端工作原理

一、长链接的工作原理 Register类其实也是基于基础的Worker开发的。Gateway进程和BusinessWorker进程启动后分别向Register进程注册自己的通讯地址&#xff0c;Gateway进程和BusinessWorker通过Register进程得到通讯地址后&#xff0c;就可以建立起连接并通讯了。而Gateway进程…

Chrome 浏览器原生功能截长屏

我偶尔需要截取一些网页内容作为素材&#xff0c;但偶尔内容很长无法截全&#xff0c;需要多次截屏再拼接&#xff0c;过于麻烦。所以记录下这个通过浏览器原生功能截长屏的方案。 注意 这种方案并不是百分百完美&#xff0c;如果涉及到一些需要滚动加载的数据或者悬浮区块&am…

Kubeadm+Containerd部署k8s(v1.28.2)集群(非高可用版)

Kubeadm+Containerd部署k8s(v1.28.2)集群(非高可用版) 文章目录 Kubeadm+Containerd部署k8s(v1.28.2)集群(非高可用版)一.环境准备1.服务器准备2.环境配置3.设置主机名4.修改国内镜像源地址5.配置时间同步6.配置内核转发及网桥过滤二.容器运行时Containerd安装(所有节点)…

Nuc9 Truenas 和 Macmini4组雷电网桥 上传速度异常 1Mbp/s 解决

link: 原创文章&#xff0c;转载之前请标注来源博客 helsonlin 前言 咸鱼整了一台 Nuc9 组了全闪的 Nas&#xff0c;想着和 Macmini 组雷电网桥&#xff0c;但是到手弄好之后发现 iperf3 和 openspeedtest 测试上传的速度都是 1Mbps 每秒。 Google 一下发现唯一的线索就是ht…

Python从0到100(七十八):神经网络--从0开始搭建全连接网络和CNN网络

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

Qt:QMetaObject::connectSlotsByName实现信号槽自动关联

简介 在Qt中&#xff0c;QMetaObject::connectSlotsByName 是一个便利的方法&#xff0c;它可以根据对象的对象名&#xff08;objectName&#xff09;自动将信号和槽连接起来。但是&#xff0c;要使用这个方法&#xff0c;必须确保&#xff1a; 1 控件&#xff08;如按钮&…

数据挖掘之认识数据

在数据挖掘过程中&#xff0c;数据的认识是非常重要的一步&#xff0c;它为后续的数据分析、建模、特征选择等工作奠定基础。以鸢尾花数据集&#xff08;Iris Dataset&#xff09;数据集之鸢尾花数据集&#xff08;Iris Dataset&#xff09;-CSDN博客为例&#xff0c;下面将介绍…