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

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

目录  隐藏 

引言 [Introduction]

背景 [Background]

附加行为 [Attached Behaviors]

论证 [Demonstration]

结论 [Conclusion]

参考 [References]

版本历史 [Revision History]

许可证 [License]

作者:Josh Smith 2008.08.30

翻译:独立观察员 2021.03.17

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

 

Explains the concept of attached behaviors and shows how to use them in the context of the MVVM pattern.

 

解释附加行为的概念并展示如何在 MVVM 模式上下文中使用它们。

 

[Demo 下载] Download demo project (requires Visual Studio 2008) - 21.3 KB

 

引言 [Introduction]

This article explains what an attached behavior is, and how you can implement them in a WPF application. Readers of this article should be somewhat familiar with WPF, XAML, attached properties, and the Model-View-ViewModel (MVVM) pattern. I highly recommend that you also read my ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’ article, because the material here is an extension of the material presented in it.

 

本文解释了什么是附加行为,以及您如何在 WPF 应用程序中实现它们。本文的读者需要稍微熟悉 WPF、XAML、附加属性、以及 MVVM 模式。我强烈建议您也阅读下我的文章《Simplifying the WPF TreeView by Using the ViewModel Pattern(通过使用 MVVM 模式来简化 WPF 的 TreeView)》,因为这里的素材就是对其中提及的素材的一个拓展。

 

背景 [Background]

Back in May of 2008, I published an article called ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’. That article focused on the MVVM pattern. This morning, I woke up to find that a fellow by the name of Pascal Binggeli had asked an excellent question on that article’s message board.

 

回想  2008  年  5  月,我发布了一篇文章叫作《Simplifying the WPF TreeView by Using the ViewModel Pattern(通过使用  MVVM  模式来简化  WPF  的  TreeView)》的。那篇文章关注的是  MVVM  模式。今天早上,我醒来时发现一个叫  Pascal Binggeli  的家伙在那篇文章的留言板问了 一个极好的问题 。

 

Pascal wanted to know how to scroll a TreeViewItem into the viewable area of the TreeView control when its associated ViewModel object selects it. That seems simple enough, but upon further examination, it is not quite as straightforward as one might initially expect. The objective, and problem, is to find the proper place to put code that calls BringIntoView() on the selected TreeViewItem, such that the principles of the MVVM pattern are not violated.

 

Pascal 想知道怎样让一个 TreeViewItem 在与它关联的 ViewModel 对象中选中它时将它滚动到 TreeView 的可视区。这似乎足够简单,但在进一步检查之后,它似乎不像最初的期望那么简单。目标和问题是找到合适的地方放置代码来对选中的 TreeViewItem  调用 BringIntoView() ,同时不违反 MVVM 模式的理念。

 

For example, suppose that the user searches through a TreeView for an item whose display text matches a user-defined search string. When the search logic finds a matching item, the matching ViewModel object will have its IsSelected property set to true. Then, via the magic of data binding, the TreeViewItem associated with that ViewModel object enters into the selected state (i.e., its IsSelected property is set to true, too). However, that TreeViewItem will not necessarily be in view, which means the user will not see the item that matches their search string. Pascal wanted a TreeViewItem brought into view when the ViewModel determines that it is in the selected state.

 

例如,假设用户从一个 TreeView 中搜索显示文本匹配用户自定义搜索字符的一项。当搜索逻辑找到一个匹配项,ViewModel 中的匹配对象会将其 IsSelected 属性设置为 true 。然后,通过神奇的数据绑定,和这个 ViewModel 中的对象关联的 TreeViewItem 进入被选中的状态(比如,它的 IsSelected 属性也被设为 true )。然而,这个 TreeViewItem 不一定在视野中,这意味着用户将看不到匹配他搜索字符串的项。Pascal 想要 TreeViewItem 在 ViewModel 设定它为被选中状态时被带到视野中。

 

The ViewModel objects have no idea that a TreeViewItem exists, and is bound to them, so it does not make sense to expect the ViewModel objects to bring TreeViewItems into view. The question becomes, now, who is responsible for bringing a TreeViewItem into view when the ViewModel forces it to be selected?

 

ViewModel 对象不知道 TreeViewItem 的存在,也不能约束他们,所以期望 ViewModel 对象把 TreeViewItem 带到视野中是没有意义的。现在问题就变成了,当 ViewModel 设置一个 TreeViewItem 为被选中时谁负责将其带到视野中。

 

We certainly do not want to put that code into the ViewModel because it introduces an artificial, and unnecessary, coupling between a ViewModel object and a visual element. We do not want to put that code in the code-behind of every place a TreeView is bound to a ViewModel, because it reintroduces some of the problems that we avoid by using a ViewModel in the first place. We could create a TreeViewItem subclass that has built-in support for bringing itself into view when selected, but, in the WPF world, that is definitely a heavy-handed solution to a lightweight problem.

 

我们当然不想将代码放到 ViewModel 里,因为这将在 ViewModel 对象和视觉元素之间引入人工的不必要的耦合。我们也不想把代码放在每个放置了受 ViewModel 约束的 TreeView 的后置代码中,因为这将重新引入一些我们起初通过使用 ViewModel 避免了的问题。我们可以创建一个 TreeViewItem 子类,该类拥有当被选中时将自己带到视野中的内建支持,但是,在 WPF 的世界中,这肯定就是杀鸡用牛刀了。

 

How can we elegantly solve this problem in a lightweight and reusable way?

 

我们怎样通过一个轻量的、可复用的方式优雅地解决这个问题呢?

 

附加行为 [Attached Behaviors]

The solution to the problem explained above is to use an attached behavior. Attaching a behavior to an object simply means making the object do something that it would not do on its own. Here is the explanation of attached behaviors that I wrote in my ‘Working with CheckBoxes in the WPF TreeView’ article:

The idea is that you set an attached property on an element so that you can gain access to the element from the class that exposes the attached property. Once that class has access to the element, it can hook events on it and, in response to those events firing, make the element do things that it normally would not do. It is a very convenient alternative to creating and using subclasses, and is very XAML-friendly.

 

上面阐述的问题的解决方案就是使用 附加行为。附加行为到一个对象简单来说就是让一个对象做一些它之前自己不会做的事情。我把对附加行为的解释写在了我的文章《Working with CheckBoxes in the WPF TreeView(在 WPF 的 TreeView 中使用 CheckBoxes)》中:

这个点子就是,你在一个元素上设置一个附加属性,那么你就可以从暴露这个附加属性的类中获得该元素的访问。一旦那个类有权限访问那个元素,它就能在其上挂钩事件,响应这些事件的触发,使该元素做出它通常不会做的事情。创建和使用子类是个非常方便的选择,并且对 XAML 是非常友好的。

 

In that article, the demo application uses attached behaviors in complicated ways, but in this article, we will keep it simple. Enough with the background and theory, let’s see how to create an attached behavior that solves the problem posed by our friend Pascal.

 

在那篇文章中,Demo  程序以一种复杂的方式使用附加行为,但在这篇文章中,我们会让其简单。背景和理论足够了,让我们看看怎样创建一个附加行为来解决我们的朋友  Pascal  发布的问题吧。

 

论证 [Demonstration]

 

This article’s demo app, which is available for download at the top of this page, uses the Text Search demo provided by the ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’ article. I made a few changes, such as adding more items to the TreeView, increasing the font size, and adding an attached behavior. The attached behavior is in a new static class called TreeViewItemBehavior. That class exposes a Boolean attached property that can be set on a TreeViewItem, called IsBroughtIntoViewWhenSelected. Here is the TreeViewItemBehavior class:

 

这篇文章的 Demo 程序(在本页顶部可供下载)使用了文章《Simplifying the WPF TreeView by Using the ViewModel Pattern》中提供的 “Text Search” 示例程序。我做了些修改,例如,往 TreeView 中添加了更多的项,增大了字体大小,添加了附加行为。附加行为在一个叫做 TreeViewItemBehavior 的新的静态类中。这个类暴露了一个可以被设置到 TreeViewItem 的 Boolean 类型的附加属性,叫作 IsBroughtIntoViewWhenSelected  。这就是 TreeViewItemBehavior 类:

/// <summary>
/// Exposes attached behaviors that can be applied to TreeViewItem objects.
/// 暴露可被应用到 TreeViewItem 对象的附加行为。
/// </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.// 只对 IsSelected 属性被修改的 TreeViewItem 触发的 Selected 事件作出反应。// 忽略所有只是报告子孙的 Selected 被触发的祖先。if (!Object.ReferenceEquals(sender, e.OriginalSource))return;TreeViewItem item = e.OriginalSource as TreeViewItem;if (item != null)item.BringIntoView();}#endregion // IsBroughtIntoViewWhenSelected
}

 

The attached behavior seen above is basically just a fancy way of hooking the Selected property of a TreeViewItem and, when the event is raised, calling BringIntoView() on the item. The final piece of this puzzle is seeing how the TreeViewItemBehavior class gets a reference to every TreeViewItem in the TreeView. We accomplish that by adding a Setter to the Style applied to every item in the TreeView, as seen below:

 

上述附加行为从根本上来说只是一种挂钩 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 给所有 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>

 

When the demo application loads up, the search text will be set to the letter Y automatically. Click the Find button a few times, and you will see that each time an item is selected, it will contain the letter Y and will scroll into view. The fact that it scrolls into view upon being selected means that the attached behavior is working properly.

 

当示例程序载入后,搜索文本会被自动设置为字母 Y。点击 Find 按钮几次,你会看到每次选中了一项,它包含了字母 Y 并且会滚动到视野中。一旦被选中就会滚动到视野中这个事实意味着附加行为工作正常。

 

结论 [Conclusion]

Hooking an event on an object and doing something when it fires is certainly not a breakthrough innovation, by any stretch of the imagination. In that sense, attached behaviors are just another way to do the same old thing. However, the importance of this technique is that it has a name, which is probably the most important aspect of any design pattern. In addition, you can create attached behaviors and apply them to any element without having to modify any other part of the system. It is a clean solution to the problem raised by Pascal Binggeli, and many, many other problems. It\'s a very useful tool to have in your toolbox.

 

无论你怎么想,在对象上挂钩事件并且在触发时做些事情当然不是一个突破性的创新。从这个层面来说,附加行为只是做相同旧事的另一种方式。然而,这个技术的重要之处就是它有个名称,这可能是任何设计模式的重要层面。而且,你可以创建附加行为并将它们应用到任何元素而无需修改系统的任何部分。它是对 Pascal Binggeli 提出的问题以及其它很多很多问题的一个清爽的解决方案,是您工具箱中非常有用的工具。

 

参考 [References]

  • The Attached Behavior Pattern – John Gossman

  • Simplifying the WPF TreeView by Using the ViewModel Pattern – Josh Smith

  • Working with CheckBoxes in the WPF TreeView - Josh Smith

 

版本历史 [Revision History]

  • August 30, 2008 – Created the article.

 

许可证 [License]

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

 

这篇文章,包括任何附带的源码和文件,在 The Code Project Open License (CPOL) 下被许可。

 

[dlgcy] 源码下载:https://download.csdn.net/download/w19921004/15873706

[dlgcy] 效果演示(动图):

 

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

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

相关文章

地图是怎么知道实时路况的?因为我们每个人都是“百万年薪级”数据贡献者

【导读】地图公司给地图“上色”主要靠数据&#xff0c;数据够则地图生。数据是地图的生命&#xff0c;车速是路况的根本&#xff0c;地图获取数据主要有两种方式&#xff1a;自给自足、仰给于人上班出门&#xff0c;点开地图&#xff0c;前面红色&#xff0c;后面红色&#xf…

MySQL优化从执行计划开始(explain超详细)

前言小伙伴一定遇到过这样反馈&#xff1a;这页面加载数据太慢啦&#xff0c;甚至有的超时了&#xff0c;用户体验极差&#xff0c;需要赶紧优化&#xff1b;反馈等同于投诉啊&#xff0c;多有几次&#xff0c;估计领导要找你谈话啦。于是不得不停下手里头的活&#xff0c;赶紧…

一个老程序员的忠告:不要一辈子靠技术生存

图丨《Ready Player One》文丨源&#xff1a;世界经理人我现在是自己做&#xff0c;但我此前有多年在从事软件开发工作&#xff0c;当回过头来想一想自己&#xff0c;觉得特别想对那些初学JAVA/DOT。NET技术的朋友说点心里话&#xff0c;希望你们能从我们的体会中&#xff0c;多…

怎样安装php52-71,CentOS如何安装PHP5和PHP7

推荐(免费)&#xff1a;PHP7安装PHP5下载解压二进制包[roottest-a src]# cd /usr/local/src/[roottest-a src]# wget http://cn2.php.net/distributions/php-5.6.32.tar.bz2[roottest-a src]# tar jxvf php-5.6.32.tar.bz2编译安装PHP./configure --prefix/usr/local/php --wit…

应用程序自动更新组件GeneralUpdate3.2.1发布

GeneralUpdate开源组件更新公告一、组件简介GeneralUpdate是基于.net standard开发的一款&#xff08;c/s应用&#xff09;自动升级程序。该组件将更新的核心部分抽离出来方便应用于多种项目当中目前适用于wpf&#xff0c;控制台应用&#xff0c;winfrom。本组件&#xff08;除…

你不知道C#只带有 get 访问器的属性是只读属性?

问题窥探之前有个哥们在使用Newtonsoft.Json反序列化的时候&#xff0c;发现明明有数据&#xff0c;但是就是序列化不出来数据到实体里面。咋一看&#xff0c;确实没什么问题啊&#xff0c;字符串的数据是存在的。那么问题出在哪里呢。解决思路1、我让他把字符串复制出来&#…

葬身李刚儿子车轮下的漂亮女孩

这么一个青春、靓丽的女孩~~~~~~~~就这样丧生在官二代的铁轮下~~~~~~~~~~~~可惜了~~~~~~~~~~~~哀痛啊&#xff0c;什么世道&#xff01;转载于:https://blog.51cto.com/jsfido/418494

RTC 媒体流数据包丢包问题解决

最近在一个测试中&#xff0c;遇到了OCS和PSTN通话之间的声音出现严重的断续问题&#xff0c;用户的使用体验就是听对方的声音不是完整的&#xff0c;有部分的语音丢失&#xff0c;在服务器上判断就是RTC数据包丢失了。专业术语就是Voice clipping。先看看丢包的恐怖情况&#…

如何在 ASP.Net Core 中使用 Autofac

依赖注入可以有效的实现对象之间的 松耦合 并能够实现代码的可测试和可维护性&#xff0c;ASP.Net Core 提供了一个极简版的容器实现对 依赖注入 的原生支持&#xff0c;然而内置的依赖注入容器相比成熟的 依赖注入容器 太弱了。为了解决这个问题&#xff0c;可以使用第三方的依…

2010年11月编程语言排行榜:手机里的代码

【51CTO独家特稿】Tiobe今天发布了最新一期的编程语言排行榜。本期榜单的前五名与10月份没有明显变化&#xff0c;依然是Java、C、C、PHP和Python&#xff1b;Objective-C的增长势头强劲。本期另外一个值得注意的语言是重回前20名NXT-G。NXT-G是一种集成在乐高&#xff08;LEGO…

NET问答: 如何迭代 Enum ?

咨询区 Peter Mortensen&#xff1a;如何使用 C# 迭代 enum ?下面的代码会编译失败&#xff0c;错误信息: Suit is a type but is used like a variablepublic enum Suit {Spades,Hearts,Clubs,Diamonds }public void EnumerateAllSuitsDemoMethod() {foreach (Suit suit in S…

承接数字油画图稿/线条图定制(出图)业务

集异璧实验室对外提供数字油画图纸&#xff08;图稿/线条图&#xff09;定制和自选业务&#xff0c;欢迎洽谈。 定制流程&#xff1a; &#xff08;1&#xff09;贵方将图片传给我方&#xff1b; &#xff08;2&#xff09;我方出图&#xff0c;提供效果图&#xff1b; &#…

爬取异步请求(XHR/JS)数据方法

概述之前在做爬虫的时候&#xff0c;比如在爬取到https://www.1688.com/?spma261p.8650866.0.0.2dfa36c3tjLrCQ网页的时候&#xff0c;发现很多内容明明在浏览器看得见&#xff0c;但是请求下来的内容却没有&#xff0c;于是打开F12查看Network发现&#xff0c;如下&#xff1…

php的old函数,laravel单元测试之phpUnit中old()函数报错解决

php 的 laravel单元测试之phpUnit中old()函数报错解决前言最近在做laravel单元测试.遇到了一个问题&#xff1a;当添加的view里面使用old()函数时就会报错,正常url访问没问题,但是在phpUnit中就报错错误原因: exception RuntimeException with message Session store not set o…

ASP.NET Core 集成 React SPA 应用

AgileConfig的UI使用react重写快完成了。上次搞定了基于jwt的登录模式&#xff08;AntDesign Pro .NET Core 实现基于JWT的登录认证&#xff09;&#xff0c;但是还有点问题。现在使用react重写后&#xff0c;agileconfig成了个确确实实的前后端分离项目。那么其实部署的话要分…

Ingress-nginx工作原理和实践

本文记录/分享 目前项目的 K8s 部署结构和请求追踪改造方案这个图算是一个通用的前后端分离的 k8s 部署结构:Nginx Ingress 负责暴露服务(nginx前端静态资源服务)&#xff0c; 根据十二要素应用的原 则&#xff0c;将后端 api 作为 nginx 服务的附加动态资源。Ingress vs Ingre…

Dotnet洋葱架构实践

一个很清晰的架构实践&#xff0c;同时刨刨MySQL的坑。一、洋葱架构简介洋葱架构出来的其实有一点年头了。大约在2017年下半年&#xff0c;就有相关的说法了。不过&#xff0c;大量的文章在于理论性的讨论&#xff0c;而我们今天会用一个项目来完成这个架构。洋葱架构&#xff…

EF Core3.0+ 通过拦截器实现读写分离与SQL日志记录

前言本文主要是讲解EF Core3.0 通过拦截器实现读写分离与SQL日志记录注意拦截器只有EF Core3.0 支持,2.1请考虑上下文工厂的形式实现.说点题外话..一晃又大半年没更新技术博客..唉,去年一年发生了太多事情..博主真的 一言难尽..有兴趣的可以去看看:记录一下,也许是转折,也许是结…

对于scanf的使用一点体会心得

今天非常的突发气象的在acm上面做了一下题目&#xff0c;悲剧的是多年不用c的人忘记了怎么样的使用scanf了&#xff0c;今天还学到了一点东西。 题目里面提示了输入两个数&#xff0c;规定第1&#xff5e;6列是第一个数的范围&#xff0c;第8&#xff5e;9列是第二个数的范围。…

毕业二十年,为什么人和人之间的差距那么大?

这是头哥侃码的第237篇原创最近天气逐渐转暖&#xff0c;身边的各种聚会也开始多了起来。找个周末&#xff0c;朋友之间喝点小酒&#xff0c;或者跟高中同学来一场久违的重逢&#xff0c;重温着曾经的回忆&#xff0c;加深着彼此之间的感情&#xff0c;想必都是不错的选择。什么…