【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进行开发,行为甚至提供了更好的设计体验——可以根本不用编写任何标记。

原文地址:https://www.cnblogs.com/Peter-Luo/p/12299855.html

时间: 2024-10-13 04:25:15

【WPF学习】第三十八章 行为的相关文章

【WPF学习】第二十八章 程序集资源

原文:[WPF学习]第二十八章 程序集资源 WPF应用程序中的程序集资源与其他.NET应用程序中的程序集资源在本质上是相同的.基本概念是为项目添加文件,从而Visual studio可将其嵌入到编译过的应用程序的EXE或DLL文件中.WPF程序集资源与其他应用程序中的程序集资源之间的重要区别是引用他们的寻址系统不同. 在前面章节已讨论过程序集资源的工作原理.因为每次编译应用程序时,项目中的每个XAML文件都转换为解析效率更高的BAML文件.这些BAML文件作为独立资源嵌入到程序集中.添加自己的资

【WPF学习】第十八章 多点触控输入

多点触控(multi-touch)是通过触摸屏幕与应用程序进行交互的一种方式.多点触控输入和更传统的基于笔(pen-based)的输入的区别是多点触控识别手势(gesture)——用户可移动多根手指以执行常见操作的特殊方式.例如,在触摸屏上放置两根手指并同时移动他们,这通常意味着“放大",而以一根手指为支点转动另一根手指意味着"旋转".并且因为用户直接在应用程序窗口中进行这些手势,所以每个手势自然会被连接到某个特定的对象.例如,简单的具有多点触控功能的应用程序,可能会在虚拟桌

第三十八章

上德不德,是以有德:下德不失德,是以无德.上德无为而无以为也.上仁为之而无以为也:上义为之而有以为也.上礼为之而莫之应也,则攘臂而扔之.故失道而后德,失德而后仁,失仁而后义,失义而后礼.夫礼者,忠信之薄也,而乱之首也.前识者,道之华也,而愚之首也.是以大丈夫居其厚,不居其薄,居其实,不居其华.故去彼取此. 第三十八章1 想索取回报的行善,是“无德” 上德不德,是以有德:下德不失德,是以无德. (第三十七章 第3讲) 真正修行高的人,不会以“德”的名义去做事,不会标榜自己的德行,这是真正的“有德”

【WPF学习】第二十四章 基于范围的控件

原文:[WPF学习]第二十四章 基于范围的控件 WPF提供了三个使用范围概念的控件.这些控件使用在特定最小值和最大值之间的数值.这些控件--ScrollBar.ProgressBar以及Slider--都继承自RangeBase类(该类又继承自Control类).尽管它们使用相同的抽象概念(范围),但工作方式却又很大的区别. 下表显示了RangeBase类定义的属性: 表 RangeBase类的属性 通常不比直接使用ScrollBar控件.更高级的ScrollViewer控件(封装了两个Scro

【WPF学习】第二十九章 元素绑定——将元素绑定到一起

原文:[WPF学习]第二十九章 元素绑定--将元素绑定到一起 数据banding的最简单情形是,源对象时WPF元素而且源属性是依赖性属性.前面章节解释过,依赖项属性具有内置的更改通知支持.因此,当在源对象中改变依赖项属性的值时,会立即更新目标对象中的绑定属性.这正是我们所需要的行为--而且不必为此构建任何额外的基础结构. 为理解如何将一个元素绑定到另一个元素,下面创建一个简单的示例.该示例窗口包含了两个控件:一个Slider控件和一个具有单行文本的TextBlock控件.如果向右拖动滑动条上的滑

【WPF学习】第十九章 控件类

原文:[WPF学习]第十九章 控件类 WPF窗口充满了各种元素,但这些元素中只有一部分是控件.在WPF领域,控件通常被描述为与用户交互的元素--能接收焦点并接受键盘或鼠标输入的元素.明显的例子包括文本框和按钮.然而,这个区别有时有些模糊.将工具提示视为控件,因为它根据用户鼠标的移动显示或消失.将标签视为控件,因为它支持记忆码(mnemonics,将焦点转移到相关控件快捷键). 所有控件都继承自System.Windows.Control类,该类添加了一小部分基本的基础结构: 设置控件内容对齐方式

【WPF学习】第二十六章 Application类——应用程序的生命周期

原文:[WPF学习]第二十六章 Application类--应用程序的生命周期 在WPF中,应用程序会经历简单的生命周期.在应用程序启动后,将立即创建应用程序对象,在应用程序运行时触发各种应用程序事件,你可以选择监视其中的某些事件.最后,当释放应用程序对象时,应用程序将结束. 一.创建Application对象 使用Application类的最简单方式是手动创建它.下面的示例演示了最小的程序:在应用程序入口(Main()方法)处创建名为MainWindow的窗口,并启动一个新的应用程序: 在本质

【WPF学习】第十四章 事件路由

原文:[WPF学习]第十四章 事件路由 由上一章可知,WPF中的许多控件都是内容控件,而内容控件可包含任何类型以及大量的嵌套内容.例如,可构建包含图形的按钮,创建混合了文本和图片内容的标签,或者为了实现滚动或折叠的显示效果而在特定容器中放置内容.设置可以多次重复嵌套,直至达到你所希望的层次深度.如下所示: <Window x:Class="RouteEvent.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2

【Unity 3D】学习笔记三十四:游戏元素——常用编辑器组件

常用编辑器组件 unity的特色之一就是编辑器可视化,很多常用的功能都可以在编辑器中完成.常用的编辑器可分为两种:原有组件和拓展组件.原有组件是编辑器原生的一些功能,拓展组件是编辑器智商通过脚本拓展的新功能. 摄像机 摄像机是unity最为核心组件之一,游戏界面中显示的一切内容都得需要摄像机来照射才能显示.摄像机组件的参数如下: clear flags:背景显示内容,默认的是skybox.前提是必须在render settings 中设置天空盒子材质. background:背景显示颜色,如果没

马哥学习笔记三十二——计算机及操作系统原理

缓存方式: 直接映射 N路关联 缓存策略: write through:通写 write back:回写 进程类别: 交互式进程(IO密集型) 批处理进程(CPU密集型) 实时进程(Real-time) CPU: 时间片长,优先级低IO:时间片短,优先级高 Linux优先级:priority 实时优先级: 1-99,数字越小,优先级越低 静态优先级:100-139,数据越小,优先级越高 实时优先级比静态优先级高 nice值:调整静态优先级   -20,19:100,139   0:120 ps