目录

介绍

背景

附加行为

示范

结论

参考


介绍

本文解释了什么是附加行为,以及如何在WPF应用程序中实现它们。本文的读者应该对WPF、XAML、附加属性和模型-视图-视图模型(MVVM)模式有些熟悉。我强烈建议您也阅读我的“使用ViewModel模式简化WPF TreeView”一文,因为这里的材料是其中介绍的材料的扩展。

背景

早在2008年5月,我发表了一篇名为“使用ViewModel模式简化WPF TreeView”的文章。那篇文章专注于MVVM模式。今天早上,我醒来发现一个名叫Pascal Binggeli的家伙在那篇文章的留言板上提出了一个很好的问题。

Pascal想知道当其关联的ViewModel对象选择它时如何滚动一个TreeViewItem到TreeView控件的可视区域中。这似乎很简单,但经过进一步研究,它并不像人们最初期望的那样简单。目标和问题是找到合适的位置,将调用BringIntoView()的代码放在选中的TreeViewItem上,这样就不会违背MVVM模式的原则。

例如,假设用户通过TreeView 搜索其显示文本与用户定义的搜索字符串匹配的项。当搜索逻辑找到匹配项时,匹配的ViewModel对象将其IsSelected属性设置为true。然后,通过数据绑定的魔力,TreeViewItem与该ViewModel对象关联的对象进入选定状态(即,它的IsSelected属性也设置为true)。但是,TreeViewItem不一定会在视图中,这意味着用户不会看到与其搜索字符串匹配的项目。当ViewModel确定它处于选定状态时,Pascal想要一个显示TreeViewItem。

ViewModel对象不知道TreeViewItem存在,并且绑定到它们,因此期望ViewModel对象将TreeViewItem带入视图是没有意义的。现在,问题变成了,当ViewModel强制选择它时,谁负责将其TreeViewItem放入可视区?

我们当然不想将该代码放入ViewModel中,因为它在ViewModel对象和可视元素之间引入了人为且不必要的耦合。我们不想将该代码放在TreeView绑定到ViewModel的每个位置的代码隐藏中,因为它重新引入了我们首先使用ViewModel避免的一些问题。我们可以创建一个具有内置支持的TreeViewItem子类,以便在选择时将其自身显示出来,但是,在WPF世界中,这绝对是一个轻量级问题的强硬解决方案。

我们如何以一种轻量级和可重用的方式优雅地解决这个问题?

附加行为

上述问题的解决方案是使用附加行为。将行为附加到对象只是意味着让对象做一些它自己不会做的事情。以下是我在“使用WPF TreeView中的复选框”一文中所写的附加行为的解释:

这个想法是您在元素上设置附加属性,以便您可以从公开附加属性的类中访问该元素。一旦该类可以访问该元素,它就可以在其上挂钩事件,并响应这些事件触发,使该元素执行它通常不会执行的操作。它是创建和使用子类的一种非常方便的替代方法,并且对XAML非常友好。

在那篇文章中,演示应用程序以复杂的方式使用附加行为,但在本文中,我们将保持简单。足够的背景和理论,让我们看看如何创建一个附加的行为来解决我们的朋友Pascal提出的问题。

示范

本文的演示应用程序(可在本页顶部下载)使用“使用ViewModel模式简化WPF TreeView”一文提供的文本搜索演示。我做了一些更改,例如向TreeView中添加更多项目、增加字体大小以及添加附加行为。附加的行为在一个名为TreeViewItemBehavior的新的静态类中。该类公开了一个Boolean附加属性,该属性可以在上TreeViewItem设置,名为IsBroughtIntoViewWhenSelected 。下面是TreeViewItemBehavior类:

/// <summary>
/// Exposes attached behaviors that can be
/// applied to TreeViewItem objects.
/// </summary>
public static class TreeViewItemBehavior
{#region IsBroughtIntoViewWhenSelectedpublic static bool GetIsBroughtIntoViewWhenSelected(TreeViewItem treeViewItem){return (bool)treeViewItem.GetValue(IsBroughtIntoViewWhenSelectedProperty);}public static void SetIsBroughtIntoViewWhenSelected(TreeViewItem treeViewItem, bool value){treeViewItem.SetValue(IsBroughtIntoViewWhenSelectedProperty, value);}public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty =DependencyProperty.RegisterAttached("IsBroughtIntoViewWhenSelected",typeof(bool),typeof(TreeViewItemBehavior),new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged));static void OnIsBroughtIntoViewWhenSelectedChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e){TreeViewItem item = depObj as TreeViewItem;if (item == null)return;if (e.NewValue is bool == false)return;if ((bool)e.NewValue)item.Selected += OnTreeViewItemSelected;elseitem.Selected -= OnTreeViewItemSelected;}static void OnTreeViewItemSelected(object sender, RoutedEventArgs e){// Only react to the Selected event raised by the TreeViewItem// whose IsSelected property was modified. Ignore all ancestors// who are merely reporting that a descendant's Selected fired.if (!Object.ReferenceEquals(sender, e.OriginalSource))return;TreeViewItem item = e.OriginalSource as TreeViewItem;if (item != null)item.BringIntoView();}#endregion // IsBroughtIntoViewWhenSelected
}

上面看到的附加行为基本上只是一种奇特的方式,用来连接TreeViewItem的Selected属性,当事件被引发时,调用该项上的BringIntoView()。这个难题的最后一部分是查看TreeViewItemBehavior类是如何获得对TreeView中每个TreeViewItem的引用的。我们通过给TreeView中每个项目的Style添加一个Setter来实现这一点,如下所示:

<TreeView.ItemContainerStyle><Style TargetType="{x:Type TreeViewItem}"><!--This Setter applies an attached behavior to all TreeViewItems.--><Setter Property="local:TreeViewItemBehavior.IsBroughtIntoViewWhenSelected" Value="True" /><!-- These Setters bind a TreeViewItem to a PersonViewModel.--><Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /><Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /><Setter Property="FontWeight" Value="Normal" /><Style.Triggers><Trigger Property="IsSelected" Value="True"><Setter Property="FontWeight" Value="Bold" /></Trigger></Style.Triggers></Style>
</TreeView.ItemContainerStyle>

当演示应用程序加载时,搜索文本将自动设置为字母Y。单击Find按钮几次,您会看到每次选择一个项目时,它将包含字母Y并滚动到视图中。它在被选中时滚动到视图中的事实意味着附加的行为正常工作。

结论

将一个事件挂在一个物体上并在它触发时做某事肯定不是一个突破性的创新,无论想象如何。从这个意义上说,附加行为只是做同样的事情的另一种方式。然而,这种技术的重要性在于它有一个名称,这可能是任何设计模式中最重要的方面。此外,您可以创建附加行为并将它们应用于任何元素,而无需修改系统的任何其他部分。这是对Pascal Binggeli提出的问题以及许多其他问题的干净解决方案。这是您的工具箱中非常有用的工具。

参考

  • 附加行为模式——约翰·戈斯曼
  • 使用ViewModel模式简化WPF TreeView——Josh Smith
  • 在WPF TreeView中使用复选框——Josh Smith

https://www.codeproject.com/Articles/28959/Introduction-to-Attached-Behaviors-in-WPF

WPF中的附加行为简介相关推荐

  1. 【翻译】WPF 中附加行为的介绍 Introduction to Attached Behaviors in WPF

    [翻译]WPF 中附加行为的介绍 Introduction to Attached Behaviors in WPF 目录  隐藏 引言 [Introduction] 背景 [Background] ...

  2. WPF中的图像处理简介

    和Winform中的GDI+相比,WPF提供了一组新的API用于显示和编辑图像.新API特点如下: 适用于新的或专用图像格式的扩展性模型. 对包括位图 (BMP).联合图像专家组 (JPEG).可移植 ...

  3. WPF中的Data Binding调试指南

    点击蓝字"大白技术控"关注我哟 加个"星标★",每日良时,好文必达! WPF中的Data Binding如何Debug? 大家平时做WPF开发,相信用Visua ...

  4. 了解 WPF 中的路由事件和命令

    目录 路由事件概述 WPF 元素树 事件路由 路由事件和组合 附加事件 路由命令概述 操作中的路由命令 命令路由 定义命令 命令插入 路由命令的局限 避免命令出错 超越路由命令 路由处理程序示例 要想 ...

  5. WPF中的路由事件(转)

    出处:https://www.cnblogs.com/JerryWang1991/archive/2013/03/29/2981103.html 最近因为工作需要学习WPF方面的知识,因为以前只关注的 ...

  6. 【小沐学C#】WPF中嵌入web网页控件(WebBrowser、WebView2、CefSharp)

    文章目录 1.简介 1.1 WPF简介 1.2 WPF 体系结构 1.3 WPF入门开发 2.WebBrowser 2.1 WebBrowser特点 2.2 WebBrowser常用的属性.方法和事件 ...

  7. WPF中通过控件Margin属性设置控件位置

    WPF中通过控件Margin属性设置控件位置 一.Margin属性简介 二.Margin在cs文件中定义 三.Margin设置控件位置 四.参考文档 一.Margin属性简介 在使用WPF进行页面设计 ...

  8. WPF中的MatrixTransform

    WPF中的MatrixTransform                                                                              周银 ...

  9. WPF中ControlTemplate和DataTemplate的区别

    原文:WPF中ControlTemplate和DataTemplate的区别 下面代码很好的解释了它们之间的区别: <Window x:Class="WPFTestMe.Window1 ...

最新文章

  1. iOS 根据中文字符串排序出字母索引
  2. get_headers()请求https报错解决思路
  3. 小小的改进,逻辑运算
  4. 在某游戏公司面试游戏运营的感受
  5. java-多线程-一道阿里面试题分析
  6. ubuntu Vim的退出命令
  7. 【机器学习】梯度下降中矩阵的迹的求导证明
  8. AndroidStudio_android中实现图片动态设置图片的位置以及图片动态缩放---Android原生开发工作笔记234
  9. 北京的小伙伴,本周五阿里聚安全约你来玩
  10. albian开发笔记四
  11. maven项目中操作mysql数据库案例
  12. python实现bt下载器_使用Python实现BT种子和磁力链接的相互转换
  13. cachecloud部署和创建机器
  14. Siamese 目标跟踪:Learning to Fuse Asymmetric Feature Maps in Siamese Trackers(CVPR2021)
  15. 162天,从华为外包5k转岗正式员工15k,心酸只有自己知道
  16. 记:疯狂的程序员 (连续n天写n个代码)
  17. 企业级负载均衡集群——lvs的DR模式(直接路由模式)详细说明
  18. 洛谷 P5740 【深基7.例9】最厉害的学生 题解
  19. LINUX下三款QQ聊天软件全接触(最新实践和对比)
  20. “10•24”专供:Spark全套知识体系,免费领!

热门文章

  1. 计算机网络数据链路层封装,计算机网络(3.3)数据链路层- 封装成帧
  2. python类的命名空间_Python之关于类变量的两种赋值区别详解
  3. 网络存储空间_网络存储服务器的三大分类,你都清楚吗?
  4. UI设计干货素材|教你正确使用浮动按钮
  5. linux下amd超频工具,AMD锐龙超频民间工具Work Tool:可单独超CCX模块
  6. linux parted命令,Linux分区之parted命令详解
  7. matlab由直方图分度,MATLAB复习资料
  8. [译转] eBPF 概念和基本原理
  9. Linux系统调用权威指南
  10. QNX Software Development Platform