【WPF学习】第十七章 鼠标输入

  鼠标事件执行几个关联的任务。当鼠标移到某个元素上时,可通过最基本的鼠标事件进行响应。这些事件是MouseEnter(当鼠标指针移到元素上时引发该事件)和MouseLeave(当鼠标指针离开元素时引发该事件)。这两个事件都是直接事件,这意味着他们不使用冒泡和隧道过程,而是源自一个元素并且只被该元素引发。考虑到控件嵌入到WPF窗口的方式,这是合理的。

  例如,如果有一个包含按钮的StackPanel面板,并将鼠标指针移到按钮上,那么首先会为这个StackPanel引发MouseEnter事件(当鼠标指针进入StackPanel面板的边界时),然后为StackPanel面板引发MouseLeave事件。

  还可响应PreviewMouseMove事件(隧道路由事件)和MouseMove事件(冒泡路由事件),只要移动鼠标就会引发这两个事件。所有这些事件都为代码提供了相同的信息:MouseEventArgs对象。MouseEventArgs对象包含当事件发生时标识鼠标键状态的属性,以及GetPosition()方法,该方法返回相对于所选元素的鼠标坐标。下面列举一个示例,该例以设备无关的像素显示鼠标指针在窗口中的位置:

private void MouseMoved(object sender, MouseEventArgs e)
        {
            Point pt = e.GetPosition(this);
            lblInfo.Text = string.Format("You are at ({0},{1}) in window coordinates", pt.X, pt.Y);
        }

  在该例中,从客户区(标题栏的下面)的左上角开始测量坐标。下图显示了上述代码的运行情况。

  XAML代码如下所示:

<Window x:Class="MouseEvents.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="300">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Rectangle Grid.Row="0" Name="rect" MouseMove="MouseMoved" Fill="LightBlue"></Rectangle>
        <TextBlock Name="lblInfo" Grid.Row="1"></TextBlock>
    </Grid>
</Window>

一、鼠标单击

  鼠标单击事件的引发方式和按键事件的引发方式有类似之处。区别是对于鼠标左键和鼠标右键引发不同的事件。下表根据他们的发生顺序列出了这些事件。除这些事件外,还有两个响应鼠标滚动动作的事件:PreviewMouseWheel和MouseWheel。

表 所有元素的鼠标单击事件(按顺序排列)

  所有鼠标事件提供MouseButtonEventArgs对象。MouseButtonEventArgs类继承自MouseEventArgs类(这意味着该类包含相同的坐标和按钮状态信息),并添加了几个成员。这些成员中相对不重要的是MouseButton(该成员用于通知是哪个鼠标键引发的事件)和ButtonState(该成员用于通知当事件发生时鼠标键时处于按下状态还是释放状态)。ClickCount属性更有趣,该属性用于通知鼠标键被单击了多少次,从而可以区分是单击(ClickCount的值是1)还是双击(ClickCount的值为2)。

  某些元素添加了更高级的鼠标事件。例如,Control类添加了PreviewMouseDoubleClick事件和MouseDoubleClick事件,这两个事件代替了MouseLeftButtonUp事件。以此类推,对于Button类,通过鼠标或键盘可触发Click事件。

二、捕获鼠标

  通常,元素每次接收到鼠标键“按下”事件后,不久后就会接受到对应的鼠标键“释放”事件。但情况不见的总是如此。例如,如果单击一个元素,保持按下鼠标键,然后移动鼠标指针离开该元素,这时该元素就不会接收到鼠标键释放事件。

  某些情况下,可能希望通过鼠标键释放事件,即使鼠标键释放事件是在鼠标已经离开了原来的元素之后发生的。为此,需要调用Mouse.Capture()方法并传递恰当的元素以捕获鼠标。此后,就会接受到鼠标键按下事件和释放事件,直到再次调用Mouse.Capture()方法传递空引用为止。当鼠标被一个元素捕获后,其他元素就不会接收到鼠标事件。这意味着用户不能单击窗口中其他位置的按钮,不能单击文本框的内部。鼠标捕获有时用于可以被拖放并可以改变尺寸的元素。

  有些情况下,可能由于其他原因(不是你的错)丢失鼠标捕获。例如,如果需要显示系统对话框,Windows可能会释放鼠标捕获。如果当鼠标键释放事件发生后没有释放鼠标,并且用户单击了另一个应用程序的窗口,也可能丢失鼠标捕获。无论哪种情况,都可以通过处理元素的LostMouseCapture事件来响应鼠标捕获的丢失。

  当鼠标被一个元素捕获时,就不能与其他元素进行交互(例如,不能单击窗口中的其他元素)。鼠标捕获通常用于短事件的操作,如拖放。

  对前面一个示例进行修改,如下图所示:

完整代码如下所示:

<Window x:Class="MouseEvents.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="300">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Rectangle Grid.Row="0" Name="rect" MouseMove="MouseMoved" Fill="LightBlue"></Rectangle>
        <Button Grid.Row="1" Name="cmdCapture" Click="cmdCapture_Click">Capture the Mouse</Button>
        <TextBlock Name="lblInfo" Grid.Row="2"></TextBlock>
    </Grid>
</Window>

XAML

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MouseEvents
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void MouseMoved(object sender, MouseEventArgs e)
        {
            Point pt = e.GetPosition(this);
            lblInfo.Text = string.Format("You are at ({0},{1}) in window coordinates", pt.X, pt.Y);
        }

        private void cmdCapture_Click(object sender, RoutedEventArgs e)
        {
            this.AddHandler(Mouse.LostMouseCaptureEvent, new RoutedEventHandler(LostMouseCapture));
            Mouse.Capture(rect);
            cmdCapture.Content = "[ Mouse is now captured ... ]";
        }
        protected void LostMouseCapture(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Lost capture");
            cmdCapture.Content = "Capture the Mouse";
        }
    }
}

CS

三、鼠标拖放

  拖放操作(是一种拖动信息使其离开窗口中的某个位置,然后将其放到其他位置的技术)和前几年相比现在不是非常普遍。编程人员已经逐渐地使用方法复制信息,从人不在需要按住鼠标键(许多用户发现这一技术比较困难)。支持鼠标拖放功能的程序通常将它用作为高级用户提供的一种快捷方式,而不是一种标准的工作方式。

  本质上,拖放操作通过以下三个步骤进行:

  (1)用户单击元素(或选择元素中的一块特定区域),并保持鼠标键为按下状态。这是,某些信息被搁置起来,并且拖放操作开始。

  (2)用户将鼠标移到其他元素上。如果该元素可接受正在拖动的内容的类型(例如一幅位图或一块文本),鼠标指针会变成拖放图标,否则鼠标指针会变成内部有一条信息的图像。

  (3)当用户释放鼠标键时,元素接收信息并决定如何处理接收信息。在没有释放鼠标键时,可按下Esc键取消该操作。

  可在窗口中添加两个文本框来尝试拖放操作支持的工作方式。因为TextBox控件提供了支持拖放的内置逻辑。如果选中文本框中的一些文本,就可以将这些文本拖动到另一个文本框中。当释放鼠标键时,这些文本将移动位置。同一技术在两个应用程序之间也可以工作——例如,可从Word文本中拖动一些文本,并放入到WPF应用程序的TextBox对象中,也可将文本从WPF应用程序的TextBox对象拖动到Word文档中。

  有时,可能希望在两个未提供内置拖放功能的元素之间进行拖放。例如,可能希望允许用户将内容从文本框拖放到标签中;或者可能希望创建如下图所示的示例,该例允许用户从Label对象或TextBox对象拖动文本,并放到另一个标签中。对于这种情况,需要处理拖放事件。

  拖放操作有两个方面:源和目标。为了创建拖放源,需要在某个位置调用DragDrop.DoDragDrop()方法来初始化拖放操作。此时确定拖放操作的源,搁置希望拖动的内容,并指明允许什么样的拖放效果(复制、移动等)。

  通常,在响应MouseDown或PreivewMouseDown事件时调用DoDragDrop()方法。下面是一个示例,当单击标签时该例初始化拖放操作。标签中的文本内容用于拖放操作:

private void lblSource_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Label lbl = (Label)sender;
            DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Copy);
        }

  接收数据的元素需要将它的AllowDrop属性设置为true。此外,它还需要通过处理Drop事件来处理数据:

<Label Grid.Row="1" Grid.ColumnSpan="2" Background="LightGoldenrodYellow"
           VerticalAlignment="Center" HorizontalAlignment="Center" Padding="20"
      AllowDrop="True" Drop="lblTarget_Drop" DragEnter="lblTarget_DragEnter">To this Label</Label>

  将AllowDrop属性设置为true时,就将元素配置为允许任何类型的信息。如果希望有选择地接收内容,可处理DragEnter事件。这时,可以检查正在拖动的内容的数据类型,然后确定所允许的操作类型。下面的示例只允许文本内容——如果拖动的内容不能转换成文本,就不能允许执行拖动操作,鼠标指针会变成内部具有一条线的图像光标,表示禁止操作:

private void lblTarget_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.Text))
                e.Effects = DragDropEffects.Copy;
            else
                e.Effects = DragDropEffects.None;
        }

  最后,当完成操作后就可以检索并处理数据了。下面的代码将拖放的文本插入标签中:

private void lblTarget_Drop(object sender, DragEventArgs e)
        {
            ((Label)sender).Content = e.Data.GetData(DataFormats.Text);
        }

  可通过拖放操作交换任意类型的对象。然而,如果需要和其他应用程序通信,这种自由的方法尽管很完美,却是不明智的。如果希望将内容拖放到其他应用程序中,应当使用基本数据类型(如字符串、整型等),或者使用实现了ISerializable或IDataObject接口的对象(这两个接口允许.NET将对象转换成字节流,并在另一个应用程序域中重新构造对象)。一个有趣的技巧就是将WPF对象转换成XAML,并在其他地方重新构成该WPF对象。所需要的所有对象就是XamlWriter和XamlReader对象。

  拖放功能的完整代码如下所示:

<Window x:Class="MouseEvents.DragAndDrop"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DragAndDrop" Height="300" Width="300">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBox Padding="10" VerticalAlignment="Center" HorizontalAlignment="Center">Drag from this TextBox</TextBox>
        <Label Grid.Column="1" Padding="20" Background="LightGoldenrodYellow"
           VerticalAlignment="Center"  HorizontalAlignment="Center"
           MouseDown="lblSource_MouseDown">Or this Label</Label>
        <Label Grid.Row="1" Grid.ColumnSpan="2" Background="LightGoldenrodYellow"
           VerticalAlignment="Center" HorizontalAlignment="Center" Padding="20"
      AllowDrop="True" Drop="lblTarget_Drop" DragEnter="lblTarget_DragEnter">To this Label</Label>
    </Grid>
</Window>

DragAndDrop.xaml

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace MouseEvents
{
    /// <summary>
    /// DragAndDrop.xaml 的交互逻辑
    /// </summary>
    public partial class DragAndDrop : Window
    {
        public DragAndDrop()
        {
            InitializeComponent();
        }
        private void lblSource_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Label lbl = (Label)sender;
            DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Copy);
        }

        private void lblTarget_Drop(object sender, DragEventArgs e)
        {
            ((Label)sender).Content = e.Data.GetData(DataFormats.Text);
        }

        private void lblTarget_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.Text))
                e.Effects = DragDropEffects.Copy;
            else
                e.Effects = DragDropEffects.None;
        }
    }
}

DragAndDrop.xaml.cs

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

时间: 2024-10-07 23:11:28

【WPF学习】第十七章 鼠标输入的相关文章

【WPF学习】第九章 使用Canvas面板进行基于坐标的布局

Canvas面板允许使用精确的坐标放置元素,如果设置数据驱动的富窗体和标准对话框,这并非好的选择:但如果需要构建其他一些不同的内容(例如,为图形工具创建创建绘图表面),Canvas面板可能是个有用的工具.Canvas面板还是最轻量级的布局容器.这是因为Canvas面板没有包含任何复杂的布局逻辑,用以改变其子元素的首选尺寸.Canvas面板只是在指定的位置放置其子元素,并且子元素具有所希望的精确尺寸. 为在Canvas面板中定位元素,需要设置Canvas.Left和Canvas.Top附加属性.C

【WPF学习】第一章 XAML介绍

XAML(Extensible Application Markup Language的简写,发音为“zammel”)是用于实例化.NET对象的标记语言.尽管XAML是一种应用于诸多不同问题领域的技术,但其主要作用是构造WPF用户界面.换言之,XAML文档定义了在WPF应用程序中组成窗口的面板.按钮以及各种空间的布局. 一.XAML变体 实际上术语“XAML”有多种含义.到目前为止,我们使用XAML标识整个XAML语言,它是一种基于通用XML语法.专门用于表示一颗.NET对象树的语言(这些对象可

WP8.1学习系列(第十七章)——交互UX之输入和反馈模式

如果你将 Windows 应用商店应用设计为触摸交互,则可免费获取对触摸板.鼠标.笔和键盘交互的支持.你的用户可以从一种输入法切换到另一种,而不会丧失应用体验的感觉.将键盘插入平板电脑?没问题.你的应用将对用户的选择做出一致且可预见的响应. 设计你的 Windows 应用商店应用的 UI 以支持触摸,同时考虑各种设备的设计含义: 结合了触摸和鼠标体验的触控板 鼠标 笔,专为数字墨迹而设计 键盘设备 触摸 Windows 提供一组在整个系统中使用的简单触摸交互功能.一致地应用此触摸语言可让用户对你

【WPF学习】第十五章 WPF事件

前两章学习了WPF事件的工作原理,现在分析一下在代码中可以处理的各类事件.尽管每个元素都提供了许多事件,但最重要的事件通常包括以下5类: 生命周期事件:在元素被初始化.加载或卸载时发生这些事件. 鼠标事件:这些事件是鼠标动作的结果. 键盘事件:这些事件是键盘动作(如按下键盘上的键)的结果. 手写笔事件:这些事件是使用类似钢笔的手写笔的结果.在平板电脑上用手写笔代替鼠标. 多点触控事件:这些事件是一根或多根手指在多点触控屏上触摸的结果.尽在Windows7中支持这些事件. 一.生命周期事件 当首次

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

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

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

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

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

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

【WPF学习】第三十一章 WPF命令模型

原文:[WPF学习]第三十一章 WPF命令模型 WPF命令模型由许多可变的部分组成.总之,它们都具有如下4个重要元素: 命令:命令表示应用程序任务,并且跟踪任务是否能够被执行.然而,命令实际上不包含执行应用程序任务的代码. 命令绑定:每个命令绑定针对用户界面的具体区域,将命令连接到相关的应用程序逻辑.这种分解的设计是非常重要的,因为单个命令可用于应用程序中的多个地方,并且在每个地方具有不同的意义.为处理这一问题,需要将同一命令与不同的命令绑定. 命令源:命令源触发命令.例如,MenuItem和B

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

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