鼠标事件执行几个关联的任务。当鼠标移到某个元素上时,可通过最基本的鼠标事件进行响应。这些事件是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

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

  1. 【Visual C++】游戏编程学习笔记之八:鼠标输入消息(小demo)

     本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder  微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.c ...

  2. 【WPF学习】第十七章 键盘输入

    当用户按下键盘上的一个键时,就会发生一系列事件.下表根据他们的发生顺序列出了这些事件: 表 所有元素的键盘事件(按顺序) 键盘处理永远不会像上面看到的这么简单.一些控件可能会挂起这些事件中的某些事件, ...

  3. 【WPF学习】第四十一章 变换

    通过使用变换(transform),许多绘图任务将更趋简单:变换是通过不加通告地切换形状或元素使用的坐标系统来改变形状或元素绘制方式的对象.在WPF中,变换由继承自System.Windows.Med ...

  4. 【WPF学习】第一章 XAML介绍

    XAML(Extensible Application Markup Language的简写,发音为“zammel”)是用于实例化.NET对象的标记语言.尽管XAML是一种应用于诸多不同问题领域的技术 ...

  5. [汇编学习笔记][第十七章使用BIOS进行键盘输入和磁盘读写

    第十七章 使用BIOS进行键盘输入和磁盘读写 17.1 int 9 中断例程对键盘输入的处理 17.2 int 16 读取键盘缓存区 mov ah,0 int 16h 结果:(ah)=扫描码,(al) ...

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

    如果你将 Windows 应用商店应用设计为触摸交互,则可免费获取对触摸板.鼠标.笔和键盘交互的支持.你的用户可以从一种输入法切换到另一种,而不会丧失应用体验的感觉.将键盘插入平板电脑?没问题.你的应 ...

  7. 【WPF学习】第五十七章 使用代码创建故事板

    在“[WPF学习]第五十章 故事板”中讨论了如何使用代码创建简单动画,以及如何使用XAML标记构建更复杂的故事板——具有多个动画以及播放控制功能.但有时采用更复杂的故事板例程,并在代码中实现全部复杂功 ...

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

    前两章学习了WPF事件的工作原理,现在分析一下在代码中可以处理的各类事件.尽管每个元素都提供了许多事件,但最重要的事件通常包括以下5类: 生命周期事件:在元素被初始化.加载或卸载时发生这些事件. 鼠标 ...

  9. 【WPF学习】第五十三章 动画类型回顾

    创建动画面临的第一个挑战是为动画选择正确的属性.期望的结果(例如,在窗口中移动元素)与需要使用的属性(在这种情况下是Canvas.Left和Canvas.Top属性)之间的关系并不总是很直观.下面是一 ...

随机推荐

  1. c#数字图像处理(四)线性点运算

    灰度图像的点运算可分为线性点运算和非线性点运算两种.4.1线性点运算定义线性点运算就是输出灰度级与输入灰度级呈线性关系的点运算.在这种情况下,灰度变换函数的形式为: g(x, y)=pf(x,y)+L ...

  2. Mybatis框架配置讲解以及使用

    1.什么是Mybatis MyBatis 是一款优秀的持久层框架, 它支持定制化 SQL.存储过程以及高级映射. MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.·MyB ...

  3. 深入理解Java虚拟机内存模型

    前言 本文中部分内容引用至<深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)>第12章,如果有兴趣可自行深入阅读,文末放有书籍PDF版本连接. 一.物理机中的并发 物理机遇到的并 ...

  4. python sqlalchemy mysql 自动映射

    SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作 简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果 ...

  5. 尝试在阿里云的Linux服务器器上安装拥有图形界面的Pycharm

    在Linux服务器上跑Python项目发现每次从本地上传文件太过麻烦,于是打算在服务器上安装Pycharm直接写Pycharm代码.   去Pycharm的官网下载Linux版本(支持正版于是我下载了 ...

  6. MyBatis基础_连接池与事务、动态SQL、注解开发

    一.MyBatis连接池及事务控制 1.连接池 在实际开发中,都会使用连接池,因为它可以减少获取连接缩消耗的时间.所谓连接池,就是存储数据库连接的容器.连接池中存储一定数量的数据库连接,当线程需要使用 ...

  7. python学习记录(八)

    0910--https://www.cnblogs.com/fnng/archive/2013/04/28/3048356.html Python异常 Python用异常对象(exception ob ...

  8. python pip使用报错: Fatal error in launcher: Unable to create process using '"c:\python27\python.exe" "C:\Python27\Scripts\pip.exe" '

    在一个系统中,如果同时存在python2和python3,在cmd.exe程序下执行pip.pip2或者pip3均会报错. 如何解决: 如果是在python3环境下,使用pip安装扩展库,可以使用以下 ...

  9. Java.work6 stasic、this、包总结作业20194651

    题目一: 编写一个类Computer,类中含有一个求n的阶乘的方法.将该类打包,并在另一包中德Java文件App.java中引入包,在主类中定义Computer类的对象,调用求n的阶乘的方法(n值由参 ...

  10. 最大连续和(dp复习)

    最大连续和:给出一段序列,选出其中连续且非空的一段使得这段和最大. stdin: 7 2 -4 3 -1 2 -4 3 stdout: 4 状态转移方程:dp[i]=max(dp[i-1]+a[i], ...