【WPF学习】第三十八章 行为
样式提供了重用一组属性设置的实用方法。它们为帮助构建一致的、组织良好的界面迈出了重要的第一步——但是它们也是有许多限制。
问题是在典型的应用程序中,属性设置仅是用户界面基础结构的一小部分。甚至最基本的程序通常也需要大量的用户界面代码,这些代码与应用程序的功能无关。在许多程序中,用于用户界面任务的代码(如驱动动画、实现平滑效果、维护用户界面状态,以及支持诸如拖放、缩放以及停靠等用户界面特性)无论是在数量山还是复杂性上都超出了业务代码。许多这类代码是通用的,这意味着在创建的每个WPF对象中需要编写相同的内容。所有这些工作几乎都单调乏味的。
为回应这一挑战,Expression Blend创作者开发了称为行为(behavior)的特征。其思想很简单:创建封装了一些通用用户界面功能的行为。这一功能可以是基本功能(如启动故事板或导航到超链接),也可以是复杂功能(如处理多点触摸交互,或构建使用实时物理引擎的碰撞模型)。一旦构建功能,就可将它们添加到任意应用程序的另一个控件中,具体方法是将该控件链接到适当的行为并设置行为的属性。在Expression Blend中,只通过拖放操作就可以使用行为。
一、获取行为支持
重用用户界面的代码通用块得基础结构不是WPF的一部分。反而,它被捆绑到Expression Blend。这是因为行为开始时作为Expression Blend的设计时特性引入的。但这并不意味着行为只能用于Expression Blend。只需要付出很少的努就可以在Visual Studio应用程序中的创建和使用行为。只需要手动缩写标记,而不是使用工具箱。
为了获得支持行为的程序集,有两种选择:
- 可按照Expression Blend 3、Expression Blend 4 或Visual Studio 2012以上版本。所有这些版本都包含Visual Studio中的行为功能所需的程序集。
- 可安装Expression Blend SDK。
无论是使用Expression Blend的哪个版本,都可以在文件夹中看到所需的两个相同的重要程序集:
- System.Windows.Interactivity.dll。这个程序集定义了支持行为的基本类。它是行为特征的基础。
- Microsoft.Expression.Interactions.dll。这个程序集通过添加可选的以核心行为类为基础的动作和触发器类,增加了一些有用的扩展。
二、理解行为模型
行为特性具有两个版本:一个版本是旨在Silverlight添加行为支持,Silverlight是Microsoft的针对浏览器的富客户端插件;而另一个版本是针对WPF设计的。尽管这两个版本提供了相同的特性,但行为特性和Silverlight领域更吻合,因为它弥补了更大的鸿沟。与WPF不同,Silverlight不支持触发器,所以实现行为的程序集也实现触发器更合理。然而,WPF支持触发器,行为特性包含自己的触发器系统,而触发器系统与WPF模型不匹配,这确实令人感到有些困惑。
问题在于具有类似名称的这两个特性有部分重合但不完全相同。在WPF中,触发器最重要的角色是构建灵活的样式和控件模板。在触发器的帮助下,样式和模板变得更加智能;例如,当一些熟悉发生变化时可应用可视化效果。然而,Expression Blend中的触发器系统具有不同的目的。通过使用可视化设计工具,允许为应用程序添加简单功能。换句话说,WPF触发器支持更加强大的样式和控件模板。而Expression Blend触发器支持快速的不需要代码的应用程序设计。
那么,对于使用WPF的普遍开发人员来说所有这些意味着什么呢?下面是几条指导原则:
- 行为模型不是WPF的核心部分,所以行为不像样式和模板那样确定。换句话说,可编写不使用行为的WPF应用程序,但如果不使用样式和模板,就不能创建比“Hello World”演示更复杂的WPF应用程序。
- 如果在Expression Blend上耗费大量时间,或希望为其他Expression Blend用户开发组件,可能会对Expression Blend中的触发器特性感兴趣。尽管和WPF中的触发器系统使用相同的名称,但它们不能相互重叠,可以同时使用这两者。
- 如果不使用Expression Blend,可完全略过触发器特性——但仍应分析Expression Blend提供的功能完整的行为类。这是因为行为比Expression Blend的触发器更强大也更常用。最终,将准备查找那些提供了可在自己的应用程序中使用的整洁美观行为的第三方组件。
三、创建行为
行为旨在封装一些UI功能,从而可以不比编写代码就能够将其应用到元素上。从另一个角度看,每个行为都为元素提供了一个服务。该服务通常涉及监听几个不同的事件并执行几个相关的操作。
为更好地理解行为,最好自己创建一个行为。设想希望为任意元素提供使用鼠标在Canvas面板上拖动元素的功能。对于单个元素实现该功能的基本步骤是非常简单的——代码监听鼠标事件并修改设置相应Canvas坐标的附加属性。但通过付出更多一点的努力,可将该代码转换为可重用的行为,该行为可为Canvas面板上的所有元素提供拖动支持。
在继续之前,创建一个WPF类库程序集。在该程序集中,添加System.Windows.Interactivity.dll程序集的引用。然后,创建一个继承自Behavior基类的类。Behavior是通用类,该类使用一个类型参数。可使用该类型参数将行为限制到特定的元素,或使用UIElement或FrameworkElement将它们都包含进来。如下所示:
public class DragInCanvasBehavior : Behavior<UIElement>
{ }
在任何行为中,第一步要覆盖OnAttached()和OnDetaching()方法。当调用OnAttached()方法时,可通过AssociatedObject属性访问放置行为的元素,并可关联事件处理程序。当调用OnDetaching()方法时,移除事件处理程序。
下面是DragInCanvasBehavior类用于监视MouseLeftButtonDown、MouseMove以及MouseLeftButtonUp事件代码:
protected override void OnAttached()
{
base.OnAttached(); // Hook up event handlers.
this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
} protected override void OnDetaching()
{
base.OnDetaching(); // Detach event handlers.
this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
}
最后一步是在事件处理程序中运行适当的代码。例如,当用户单击鼠标左键时,DragInCanvasBehavior开始拖动操作、记录元素左上角与鼠标指针之间的偏移并捕获鼠标:
private Canvas canvas;
// Keep track of when the element is being dragged.
private bool isDragging = false; // When the element is clicked, record the exact position
// where the click is made.
private Point mouseOffset; private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Find the canvas.
if (canvas == null) canvas = VisualTreeHelper.GetParent(this.AssociatedObject) as Canvas; // Dragging mode begins.
isDragging = true; // Get the position of the click relative to the element
// (so the top-left corner of the element is (0,0).
mouseOffset = e.GetPosition(AssociatedObject); // Capture the mouse. This way you'll keep receiveing
// the MouseMove event even if the user jerks the mouse
// off the element.
AssociatedObject.CaptureMouse();
}
当元素处于拖动模式并移动鼠标时,重新定位元素:
private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
// Get the position of the element relative to the Canvas.
Point point = e.GetPosition(canvas); // Move the element.
AssociatedObject.SetValue(Canvas.TopProperty, point.Y - mouseOffset.Y);
AssociatedObject.SetValue(Canvas.LeftProperty, point.X - mouseOffset.X);
}
}
当释放鼠标键时,结束拖动:
private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (isDragging)
{
AssociatedObject.ReleaseMouseCapture();
isDragging = false;
}
}
DragInCanvasBehavior类的完整代码如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media; namespace CustomBehaviorsLibrary
{
public class DragInCanvasBehavior : Behavior<UIElement>
{
private Canvas canvas; protected override void OnAttached()
{
base.OnAttached(); // Hook up event handlers.
this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
} protected override void OnDetaching()
{
base.OnDetaching(); // Detach event handlers.
this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
} // Keep track of when the element is being dragged.
private bool isDragging = false; // When the element is clicked, record the exact position
// where the click is made.
private Point mouseOffset; private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Find the canvas.
if (canvas == null) canvas = VisualTreeHelper.GetParent(this.AssociatedObject) as Canvas; // Dragging mode begins.
isDragging = true; // Get the position of the click relative to the element
// (so the top-left corner of the element is (0,0).
mouseOffset = e.GetPosition(AssociatedObject); // Capture the mouse. This way you'll keep receiveing
// the MouseMove event even if the user jerks the mouse
// off the element.
AssociatedObject.CaptureMouse();
} private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
// Get the position of the element relative to the Canvas.
Point point = e.GetPosition(canvas); // Move the element.
AssociatedObject.SetValue(Canvas.TopProperty, point.Y - mouseOffset.Y);
AssociatedObject.SetValue(Canvas.LeftProperty, point.X - mouseOffset.X);
}
} private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (isDragging)
{
AssociatedObject.ReleaseMouseCapture();
isDragging = false;
}
}
}
}
DragInCanvasBehavior
四、使用行为
为测试行为,创建一个新的WPF应用程序项目。然后添加对定义DragInCanvasBehavior类的类库以及System.Windows.Interactivity.dll程序集得应用。接下来在XML中映射这两个标记名称。假定存储DragInCanvasBehavior类的类库名为CustomBehaviorsLibrary,所需的标记如下所示:
<Window x:Class="BehaviorTest.DragInCanvasTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:custom="clr-namespace:CustomBehaviorsLibrary;assembly=CustomBehaviorsLibrary"
Title="DragInCanvasTest" Height="300" Width="300">
为使用该行为,只需要使用Interaction.Behaviors附加属性在Canvas面板中添加任意元素。下面的标记创建一个具有三个图形的Canvas面板。两个Ellipse元素使用了DragInCanvasBehavior,并能在Canvas面板中拖动。Rectangle元素没有使用DraginCanvasBehavior,因此无法移动。
<Canvas>
<Rectangle Canvas.Left="10" Canvas.Top="10" Fill="Yellow" Width="40" Height="60"></Rectangle>
<Ellipse Canvas.Left="10" Canvas.Top="70" Fill="Blue" Width="80" Height="60">
<i:Interaction.Behaviors>
<custom:DragInCanvasBehavior></custom:DragInCanvasBehavior>
</i:Interaction.Behaviors>
</Ellipse>
<Ellipse Canvas.Left="80" Canvas.Top="70" Fill="OrangeRed" Width="40" Height="70">
<i:Interaction.Behaviors>
<custom:DragInCanvasBehavior></custom:DragInCanvasBehavior>
</i:Interaction.Behaviors>
</Ellipse>
</Canvas>
下图显示了该例的运行效果。
DragInCanvasTest完整代码:
<Window x:Class="BehaviorTest.DragInCanvasTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:custom="clr-namespace:CustomBehaviorsLibrary;assembly=CustomBehaviorsLibrary"
Title="DragInCanvasTest" Height="300" Width="300">
<Canvas>
<Rectangle Canvas.Left="10" Canvas.Top="10" Fill="Yellow" Width="40" Height="60"></Rectangle>
<Ellipse Canvas.Left="10" Canvas.Top="70" Fill="Blue" Width="80" Height="60">
<i:Interaction.Behaviors>
<custom:DragInCanvasBehavior></custom:DragInCanvasBehavior>
</i:Interaction.Behaviors>
</Ellipse>
<Ellipse Canvas.Left="80" Canvas.Top="70" Fill="OrangeRed" Width="40" Height="70">
<i:Interaction.Behaviors>
<custom:DragInCanvasBehavior></custom:DragInCanvasBehavior>
</i:Interaction.Behaviors>
</Ellipse>
</Canvas>
</Window>
DragInCanvasTest
但这并非是全部内容。如果正在使用Expression Blend进行开发,行为甚至提供了更好的设计体验——可以根本不用编写任何标记。
【WPF学习】第三十八章 行为的更多相关文章
- 【WPF学习】第十八章 多点触控输入
多点触控(multi-touch)是通过触摸屏幕与应用程序进行交互的一种方式.多点触控输入和更传统的基于笔(pen-based)的输入的区别是多点触控识别手势(gesture)——用户可移动多根手指以 ...
- 【WPF学习】第二十八章 程序集资源
WPF应用程序中的程序集资源与其他.NET应用程序中的程序集资源在本质上是相同的.基本概念是为项目添加文件,从而Visual studio可将其嵌入到编译过的应用程序的EXE或DLL文件中.WPF程序 ...
- “全栈2019”Java第三十八章:类与方法
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- Gradle 1.12用户指南翻译——第三十八章. Eclipse 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- angular学习笔记(三十)-指令(10)-require和controller
本篇介绍指令的最后两个属性,require和controller 当一个指令需要和父元素指令进行通信的时候,它们就会用到这两个属性,什么意思还是要看栗子: html: <outer‐direct ...
- angular学习笔记(三十)-指令(7)-compile和link(2)
继续上一篇:angular学习笔记(三十)-指令(7)-compile和link(1) 上一篇讲了compile函数的基本概念,接下来详细讲解compile和link的执行顺序. 看一段三个指令嵌套的 ...
- angular学习笔记(三十)-指令(7)-compile和link(1)
这篇主要讲解指令中的compile,以及它和link的微妙的关系. link函数在之前已经讲过了,而compile函数,它和link函数是不能共存的,如果定义了compile属性又定义link属性,那 ...
- angular学习笔记(三十)-指令(6)-transclude()方法(又称linker()方法)-模拟ng-repeat指令
在angular学习笔记(三十)-指令(4)-transclude文章的末尾提到了,如果在指令中需要反复使用被嵌套的那一坨,需要使用transclude()方法. 在angular学习笔记(三十)-指 ...
- angular学习笔记(三十)-指令(5)-link
这篇主要介绍angular指令中的link属性: link:function(scope,iEle,iAttrs,ctrl,linker){ .... } link属性值为一个函数,这个函数有五个参数 ...
- angular学习笔记(三十)-指令(2)-restrice,replace,template
本篇主要讲解指令中的 restrict属性, replace属性, template属性 这三个属性 一. restrict: 字符串.定义指令在视图中的使用方式,一共有四种使用方式: 1. 元素: ...
随机推荐
- $Poj1037\ A\ Decorative\ Fence$ 计数类$DP$
Poj AcWing Description Sol 这题很数位$DP$啊, 预处理$+$试填法 $F[i][j][k]$表示用$i$块长度不同的木板,当前木板(第$i$块)在这$i$块木板中从小到 ...
- 一文MyBatis-Plus快速入门
目录 一.依赖及配置 1.在idea中创建一个SpringBoot项目,在pom.xml中添需要的依赖 2.配置数据库连接 3.在启动类中添加注解 @MapperScan 扫描Mapper接口包 4. ...
- 使用PE启动盘清空电脑登入密码
1.PE启动盘制作过程 要制作一个启动盘可以使用很多工具来制作,比如老毛桃.U深度.大白菜等软件都可以制作PE启动盘.此处就用老毛桃制作PE启动盘为例(http://www.laomaotao.tv/ ...
- 2019 年百度之星 初赛一 1002 Game
传送门 Problem Description 度度熊在玩一个好玩的游戏.游戏的主人公站在一根数轴上,他可以在数轴上任意移动,对于每次移动,他可以选择往左或往右走一格或两格.现在他要依次完成 n 个任 ...
- 【Java并发基础】并发编程领域的三个问题:分工、同步和互斥
前言 可以将Java并发编程抽象为三个核心问题:分工.同步和互斥. 这三个问题的产生源自对性能的需求.最初时,为提高计算机的效率,当IO在等待时不让CPU空闲,于是就出现了分时操作系统也就出现了并发. ...
- windows创建git并连结github
1.下载跟自己系统相对应的git版本 2.默认安装 3.绑定用户 git config --global user.name ""git config --global user. ...
- .NET Core 3 WPF MVVM框架 Prism系列之事件聚合器
本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的使用事件聚合器实现模块间的通信 一.事件聚合器 在上一篇 .NET Core 3 WPF MVVM框架 Prism系列之模块化 ...
- Java 循环队列
传统数组实现的队列有缺陷,当多次入队出队后,队头指针会后移,当队尾指针达到数组末尾时,会提示队列已满,导致数组前部分空间被浪费.如果当队尾和队头指针到达数组末尾时能从数组[0]继续添加数据,可以提升数 ...
- java学生管理系统(增、删、改、查功能)
package mm; import java.util.Scanner;import java.util.ArrayList; class Student1 { private String stu ...
- 关于neo4j初入门(2)
DELETE删除 删除节点及相关节点和关系. DELETE <node-name-list> DELETE <node1-name>,<node2-name>,&l ...