【翻译】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] 效果演示(動圖):
?
總結
以上是生活随笔為你收集整理的【翻译】WPF 中附加行为的介绍 Introduction to Attached Behaviors in WPF的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET 差点不叫“.NET”?微软大牛
- 下一篇: MySQL优化从执行计划开始(expla