当前位置: 首页 > news >正文

【wpf】从 DataContext 到依赖属性:WPF 自定义控件 ImageView 的优化之路

从 DataContext 到依赖属性:WPF 自定义控件 ImageView 的优化之路

最近我在做一个 WPF 项目,需要封装一个 ImageView 控件,用来显示图像并处理鼠标交互。
在实际开发中,我遇到了一系列和 数据绑定 有关的问题:

  • 控件需要和 GraphicInfo 数据对象绑定,并且把自身写回 GraphicInfo.View
  • 控件内部要用到第三方控件 HSmartWindowControlWPF,需要在 OnApplyTemplate 里做初始化。
  • 控件需要支持在 ItemsControl(例如 ListBox)中批量使用。

在这个过程中,我从最初直接用 DataContext,一路优化到定义依赖属性、支持 ItemTemplate 绑定,踩了不少坑。
这篇文章就总结一下这个过程,给遇到类似问题的朋友做个参考。


1️⃣ 初版:直接使用 DataContext

最初我的写法是这样的:

public override void OnApplyTemplate()
{base.OnApplyTemplate();hSmart = (HSmartWindowControlWPF)GetTemplateChild("PART_hSmart");hSmart.Loaded += Hsmart_Loaded;hSmart.HMouseMove += HSmart_HMouseMove;// 用 DataContext 拿到当前 GraphicInfoif (DataContext is GraphicInfo info){info.View = this; // 把自己写回去}
}

然后在 XAML 使用:

<local:ImageView DataContext="{Binding MyGraphic}" />

或者是,ImageView 外部有个ItemList ,ImageView 就会自动关联到Itemlist的子项,ImageView 以ItemList 的子项为DataContext。

这种方式虽然能用,但很快就遇到了几个问题:

  • DataContext 被控件内部占用:如果控件内部也需要绑定自己的属性,就会和外部冲突。
  • 在 ItemList 中不直观:每个 Item 的 DataContext 都是当前 GraphicInfo,但从外部看不清楚绑定了什么。
  • 无法精确控制:如果控件将来要绑定别的对象,很难扩展。

2️⃣ 第一步优化:定义依赖属性

更好的做法是给控件定义一个依赖属性,例如 Graphic

public class ImageView : Control
{static ImageView(){DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageView),new FrameworkPropertyMetadata(typeof(ImageView)));}// 定义 Graphic 依赖属性public GraphicInfo Graphic{get => (GraphicInfo)GetValue(GraphicProperty);set => SetValue(GraphicProperty, value);}public static readonly DependencyProperty GraphicProperty =DependencyProperty.Register(nameof(Graphic), typeof(GraphicInfo), typeof(ImageView),new PropertyMetadata(null, OnGraphicChanged));private static void OnGraphicChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){if (d is ImageView view && e.NewValue is GraphicInfo info){// 在绑定时回写info.View = view;}}public override void OnApplyTemplate(){base.OnApplyTemplate();hSmart = (HSmartWindowControlWPF)GetTemplateChild("PART_hSmart");if (hSmart != null){hSmart.Loaded += Hsmart_Loaded;hSmart.HMouseMove += HSmart_HMouseMove;}}
}

单独使用

XAML 使用方式变成:

<local:ImageView Graphic="{Binding MyGraphic}" />

✅ 这样 ImageViewDataContext 可以自由使用,不再和外部冲突。
✅ 依赖属性还能在回调里做初始化逻辑,代码更加集中。


3️⃣ 在 ItemsControl 中使用

接下来要支持在 ItemsControl 中批量显示多个 GraphicInfo

<ItemsControl ItemsSource="{Binding saveInfo.Graphics}"><ItemsControl.ItemTemplate><DataTemplate><sw:ImageView Graphic="{Binding}" /></DataTemplate></ItemsControl.ItemTemplate>
</ItemsControl>

这里的 {Binding} 什么都不写,等价于 {Binding Path=.},也就是把当前 GraphicInfo 传给 Graphic 属性,非常简洁。

如果写成 Graphic="{Binding SomeProperty}",就只会绑定 GraphicInfo 的某个属性,而不是整个对象。


4️⃣ 为什么要用 {Binding}

很多朋友第一次看到这种写法可能会疑惑:啥都不写有啥意义?

其实意义很大!
ItemsControl.ItemTemplate 里,当前 DataContext 就是当前项本身,{Binding} 直接把整个对象传给控件的依赖属性。

这有几个好处:

  • 语义清晰:一眼就能看出控件拿到的是当前项本身,而不是某个属性。
  • 方便控件内部处理:可以在依赖属性回调里直接访问整个对象。
  • 和命令参数类似:就像我们常用的 CommandParameter="{Binding}",也是把当前项传给命令执行逻辑。

如果不用 {Binding} 会怎么样?

假设你不写 {Binding},直接这样:

<sw:ImageView />

那么 ImageView.Graphic 就是 null,控件里完全拿不到当前 GraphicInfo,也就无法回写 info.View = this。

你就得在后台代码里自己找当前项、手动设置,这就麻烦多了,也破坏了 MVVM 模式。

5️⃣ 优化结果

经过这一轮优化,ImageView 变得更健壮、可扩展,也能在 ItemList 中使用。

最终效果:

<ListBox ItemsSource="{Binding Graphics}"><ListBox.ItemTemplate><DataTemplate><sw:ImageView Graphic="{Binding}" /></DataTemplate></ListBox.ItemTemplate>
</ListBox>

每个 GraphicInfo 都会有对应的 ImageView,并且控件内部可以随时拿到 GraphicInfo.View


6️⃣ 总结

初版:

  • 直接用 DataContext,代码简单,但容易冲突。

优化版:

  • 定义 Graphic 依赖属性。
  • 在回调里处理 info.View = this
  • ItemTemplate 中用 Graphic="{Binding}" 绑定整个对象。

最终收获:

  • 控件内部的 DataContext 和外部彻底解耦。
  • 可以优雅支持 ItemsControlListBox 等批量场景。
  • 控件更通用、可维护性更好。

结论:

在 WPF 自定义控件中,推荐为关键数据对象定义依赖属性,而不是直接依赖 DataContext。
在 ItemTemplate 里,使用 Graphic="{Binding}" 是最简洁优雅的写法,能把当前项整个传给控件,便于控件内部逻辑处理。


文章转载自:

http://nNHUqY46.tnkwj.cn
http://JhiXF6Yy.tnkwj.cn
http://7E1zqywy.tnkwj.cn
http://Kewj1cjr.tnkwj.cn
http://gz1rDDBV.tnkwj.cn
http://2ViZedWf.tnkwj.cn
http://T6NrbRWG.tnkwj.cn
http://BS6RonUD.tnkwj.cn
http://hzZOajak.tnkwj.cn
http://nyVVC4Bq.tnkwj.cn
http://tDkGaSub.tnkwj.cn
http://xhPOSWum.tnkwj.cn
http://ZIUkocOV.tnkwj.cn
http://uBQhOxfX.tnkwj.cn
http://0wwi6yjV.tnkwj.cn
http://tPfTE8Qm.tnkwj.cn
http://w70zDbZ6.tnkwj.cn
http://7U7a9xb2.tnkwj.cn
http://Oll5NK4R.tnkwj.cn
http://bCsykPj9.tnkwj.cn
http://tdGBfjCx.tnkwj.cn
http://JaieJXK8.tnkwj.cn
http://JRaPz8A1.tnkwj.cn
http://zttF14ZV.tnkwj.cn
http://3JQo13uL.tnkwj.cn
http://kLZGQqiP.tnkwj.cn
http://87Af2XW6.tnkwj.cn
http://yGS9Y0KN.tnkwj.cn
http://ARQCPyAM.tnkwj.cn
http://s432zryH.tnkwj.cn
http://www.dtcms.com/a/384304.html

相关文章:

  • Sport Network 凭借 Akamai 实现卓越成就
  • Topaz Photo AI 人工智能图像处理(Mac)
  • LeetCode 第467场周赛 第13天
  • PINN物理信息神经网络锂电池剩余寿命预测模型(内含容量特征提取+两组电池剩余寿命预测实验),MATLAB实现
  • 「日拱一码」088 机器学习——蒙特卡洛树搜索MCTS
  • 简单聊聊神经网络中的反向传播
  • Java-Spring入门指南(九)反射与反射对象
  • 从 Vue 到 Java:前后端分离项目后端迁移完整教程
  • 在 IDEA 2024 创建 Vue 项目(保姆级)
  • Electron 常见问题排查:调试与错误处理
  • 学生管理系统
  • 软件测试的艺术——黑白盒测试学习笔记
  • Electron开源库入门教程:跨平台桌面应用框架
  • 基于Springboot企业车辆管理系统
  • MySQL面试(1)
  • ArcGIS定向影像(1)——非传统影像轻量级解决方案
  • 【Linux指南】Makefile进阶:通用化语法与实战技巧
  • 移相全桥模拟控制电路
  • ES6 面试题及详细答案 80题 (62-80)-- 模块化与其他特性
  • D005 vue+django音乐推荐大数据|推荐算法|多权限| 可视化|完整源码
  • 工厂自动化正从 “人工堆叠” 向 “设备替代” 快速转变
  • 栈-227.基本计算器II-力扣(LeetCode)
  • python 自动化从入门到实战-做一个超实用的二维码生成工具(5)
  • 今天开始学习新内容“服务集群与自动化”--crond服务、--syslog服务以及DHCP协议
  • LeetCode 379 - 电话目录管理系统(Design Phone Directory)
  • 《Python 自动化实战:从零构建一个文件同步工具》
  • 风险规则引擎-RPA 作为自动化依赖业务决策流程的强大工具
  • Vue: 模板引用 (Template Refs)
  • Web2 vs Web3
  • 上海交大3D体素赋能具身导航!BeliefMapNav:基于3D体素信念图的零样本目标导航