【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://www.pswp.cn/pingmian/97750.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/97750.shtml

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

相关文章

人力资源管理的思维方式学习笔记2

战略人力资源管理的思维方式——北京师范大学政府管理学院——王建民 教授3.1.理念&#xff1a;人力资源是第一战略资源 我们先来了解海尔集团公司实施发展战略的情况。海尔集团创立于1984年&#xff0c;根据官方网站的介绍&#xff0c;目前是一家全球领先的美好生活解决方案服…

汽车网络安全 CyberSecurity ISO/SAE 21434 测试之一

一、什么是网络安全&#xff1f; 在智能网联和自动驾驶技术进入汽车行业之前&#xff0c;功能安全 一直是汽车开发的核心。 简单来说&#xff0c;功能安全的目标是确保车辆的系统在出现故障时&#xff0c;不会对人、环境或者设备造成危害。比如&#xff0c;刹车失灵了&#xff…

力扣(LeetCode) ——217. 存在重复元素(C++)

题目&#xff1a;217. 存在重复元素 示例1&#xff1a; 输入&#xff1a; nums [1,2,3,1] 输出&#xff1a; true 解释&#xff1a; 元素 1 在下标 0 和 3 出现。 示例2&#xff1a; 输入&#xff1a; nums [1,2,3,4] 输出&#xff1a; false 解释&#xff1a; 所有元素都…

Redis 高可用与集群原理

Redis 高可用与集群原理1. 前言 Redis 单机虽然高性能&#xff0c;但一旦节点宕机&#xff0c;数据丢失或服务不可用问题会非常严重。为了解决这一问题&#xff0c;Redis 提供了 主从复制、哨兵&#xff08;Sentinel&#xff09;、Cluster 集群 等高可用机制。 这一篇文章我们重…

Oracle OCP认证考试题目详解082系列第31题

考察知识点:SET VERIFY ON命令 英文题目 Which two statements are true about the SET VERIFY ON command? A.It displays values for variables prefixed with && B.It displays values for variables created by the DEFINE command C.It can be used in SQL Dev…

【WebSocket✨】入门之旅(五):WebSocket 的安全性

本篇文章将讲解 WebSocket 的安全性&#xff0c;特别是如何防止常见的 WebSocket 安全漏洞&#xff0c;如中间人攻击&#xff08;MITM&#xff09;、XSS、CSRF 等。我们将介绍如何使用 wss:// 协议保障数据传输的安全&#xff0c;并给出一些安全最佳实践&#xff0c;帮助你在使…

流程图用什么工具做?免费/付费工具对比,附在线制作与下载教程

在日常工作和学习中&#xff0c;流程图是一种高效的可视化工具——项目开发时梳理需求逻辑、业务推进中拆解步骤节点、学术研究里呈现论证框架&#xff0c;甚至是生活中规划旅行路线&#xff0c;都能借助流程图让复杂信息变得清晰易懂。但面对市面上五花八门的流程图工具&#…

Oracle重做日志(Redo Log):数据一致性的“守护者“

在Oracle数据库的三大核心文件中&#xff0c;数据文件承载着最终的业务数据&#xff0c;控制文件记录着数据库的"身份档案"&#xff0c;而重做日志&#xff08;Redo Log&#xff09;则扮演着"事务日记"的关键角色——它以不可篡改的方式记录每一次数据变更…

C++学习:map/set源码剖析+利用红黑树封装map/set

前面我们已经学习了红黑树这个高级数据结构的实现。我们知道STL的map/set的底层数据结构为红黑树&#xff0c;本期就查看STL源码的map/set&#xff0c;并结合着这之前的红黑树的实现&#xff0c;模拟实现map和set的一部分功能 STL源码&#xff1a;楼田莉子/CPP代码学习 作者的个…

【c++进阶系列】:map和set的模拟实现(附模拟实现的源码)

&#x1f525; 本文专栏&#xff1a;c &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a;每一次抉择&#xff0c;都是将未来的自己轻轻推向某个方向 ★★★ 本文前置知识&#xff1a; 红黑树 原理 那么在上一期博客中&#xf…

JVM默认栈大小

JVM 里线程栈的大小不是一个固定值&#xff0c;而是由 操作系统平台、JVM 实现版本、以及启动参数 共同决定的。 常见情况&#xff08;以 HotSpot 为例&#xff09;&#xff1a; Linux / macOS 64 位 JVM 默认大约是 1M &#xff08;1024 KB&#xff09;32 位 JVM 默认大约是 3…

AI 机器视觉检测方案:破解食物包装四大质检难题,筑牢食品安全防线

在食品生产领域&#xff0c;包装盒或包装袋作为食品的直接包装载体&#xff0c;其质量优劣直接关系到食品安全与企业声誉。传统人工质检在应对食物包装生产的高速节奏与复杂质量问题时&#xff0c;逐渐暴露出诸多局限性&#xff0c;成为企业发展的瓶颈。而 AI 视频检测技术的出…

嵌入式 Linux 安全简介-第二部分

大家好&#xff01;我是大聪明-PLUS&#xff01;这是有关嵌入式Linux安全性的文章的第二部分。在第一部分中&#xff0c;我们讨论了一些安全概念、威胁建模、安全启动、代码和数据加密、加密密钥和密钥存储技术。在第二部分中&#xff0c;让我们继续讨论提高嵌入式 Linux 设备安…

Vue3+JS 复杂表单实战:从验证到性能优化的全流程方案

继上一篇分享组合式 API Hook 封装后&#xff0c;这次想聚焦前端开发中 “让人又爱又恨” 的场景 —— 复杂表单。不管是管理后台的配置表单&#xff0c;还是用户中心的多步骤提交&#xff0c;表单处理都占了业务开发的 40% 以上。这篇文章会从实际项目痛点出发&#xff0c;分享…

[特殊字符] Python在CentOS系统执行深度指南

文章目录1 Python环境安装与配置问题1.1 系统自带Python的限制1.2 安装Python 3的常见问题及解决方案1.3 SSL模块问题解决方案1.4 环境变量配置与管理1.5 软件集合&#xff08;SCL&#xff09;替代方案2 包管理与虚拟环境问题2.1 pip包管理器问题与解决方案2.2 虚拟环境的最佳实…

ptx 简介03,ldmatrix 的应用实例解析

1. 实例编译 运行 main.cu //nvcc -g -lineinfo -stdc17 -archnative main.cu -o main#include <iostream> #include <thrust/device_vector.h>/* ldmatrix.sync.aligned.shape.num{.trans}{.ss}.type r, [p];.shape {.m8n8}; .num {.x1, .…

PostgreSQL 的核心优势数据库优化与面试问题解析

Part0: PostgreSQL 的核心优势PostgreSQL 的核心优势可以总结为&#xff1a;它不仅仅是一个关系型数据库&#xff0c;更是一个功能极其强大、设计高度严谨、且具有无限扩展潜力的数据平台。其核心优势主要体现在以下几个方面&#xff1a;1. 高度符合 SQL 标准与可靠性&#xff…

牛客周赛 Round 109 (小红的直角三角形

小红的直角三角形思路&#xff1a;当作向量来求&#xff0c;向量乘为0&#xff1b;#include<bits/stdc.h> #define ll long long #define endl "\n" using namespace std; typedef pair<ll, ll> pll; int n; vector<pll> u; void solve() {int x,…

efcore 对象内容相同 提交MSSQL后数据库没有更新

一、efcore 对象内容相同 提交MSSQL后数据库没有更新在.net6EFcore6环境&#xff0c;遇到一个问题&#xff0c;当界面UI传给EF的对象值没有变化&#xff0c;它居然不去更新数据库。那我有2个EFcore实例都在更新数据库&#xff0c;值一直不变&#xff0c;程序就更新不到数据库中…

DockerComposeUI+cpolar:容器管理的远程可视化方案

前言&#xff1a;DockerComposeUI作为Docker容器的可视化管理工具&#xff0c;通过直观的Web界面实现容器的启动、暂停、终止等操作&#xff0c;支持镜像管理和Compose文件编辑。特别适合开发团队和运维人员&#xff0c;其图形化操作简化了复杂的命令行操作&#xff0c;状态面板…