【WPF學習】第十七章 鼠標輸入

  鼠標事件執行幾個關聯的任務。當鼠標移到某個元素上時,可經過最基本的鼠標事件進行響應。這些事件是MouseEnter(當鼠標指針移到元素上時引起該事件)和MouseLeave(當鼠標指針離開元素時引起該事件)。這兩個事件都是直接事件,這意味着他們不使用冒泡和隧道過程,而是源自一個元素而且只被該元素引起。考慮到控件嵌入到WPF窗口的方式,這是合理的。編程

  例如,若是有一個包含按鈕的StackPanel面板,並將鼠標指針移到按鈕上,那麼首先會爲這個StackPanel引起MouseEnter事件(當鼠標指針進入StackPanel面板的邊界時),而後爲StackPanel面板引起MouseLeave事件。ide

  還可響應PreviewMouseMove事件(隧道路由事件)和MouseMove事件(冒泡路由事件),只要移動鼠標就會引起這兩個事件。全部這些事件都爲代碼提供了相同的信息:MouseEventArgs對象。MouseEventArgs對象包含當事件發生時標識鼠標鍵狀態的屬性,以及GetPosition()方法,該方法返回相對於所選元素的鼠標座標。下面列舉一個示例,該例以設備無關的像素顯示鼠標指針在窗口中的位置:this

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);
        }

  在該例中,從客戶區(標題欄的下面)的左上角開始測量座標。下圖顯示了上述代碼的運行狀況。spa

 

  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>

1、鼠標單擊code

  鼠標單擊事件的引起方式和按鍵事件的引起方式有相似之處。區別是對於鼠標左鍵和鼠標右鍵引起不一樣的事件。下表根據他們的發生順序列出了這些事件。除這些事件外,還有兩個響應鼠標滾動動做的事件:PreviewMouseWheel和MouseWheel。orm

表 全部元素的鼠標單擊事件(按順序排列)xml

 

   全部鼠標事件提供MouseButtonEventArgs對象。MouseButtonEventArgs類繼承自MouseEventArgs類(這意味着該類包含相同的座標和按鈕狀態信息),並添加了幾個成員。這些成員中相對不重要的是MouseButton(該成員用於通知是哪一個鼠標鍵引起的事件)和ButtonState(該成員用於通知當事件發生時鼠標鍵時處於按下狀態仍是釋放狀態)。ClickCount屬性更有趣,該屬性用於通知鼠標鍵被單擊了多少次,從而能夠區分是單擊(ClickCount的值是1)仍是雙擊(ClickCount的值爲2)。對象

  某些元素添加了更高級的鼠標事件。例如,Control類添加了PreviewMouseDoubleClick事件和MouseDoubleClick事件,這兩個事件代替了MouseLeftButtonUp事件。以此類推,對於Button類,經過鼠標或鍵盤可觸發Click事件。blog

2、捕獲鼠標

  一般,元素每次接收到鼠標鍵「按下」事件後,不久後就會接受到對應的鼠標鍵「釋放」事件。但狀況不見的老是如此。例如,若是單擊一個元素,保持按下鼠標鍵,而後移動鼠標指針離開該元素,這時該元素就不會接收到鼠標鍵釋放事件。

  某些狀況下,可能但願經過鼠標鍵釋放事件,即便鼠標鍵釋放事件是在鼠標已經離開了原來的元素以後發生的。爲此,須要調用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

3、鼠標拖放

  拖放操做(是一種拖動信息使其離開窗口中的某個位置,而後將其放到其餘位置的技術)和前幾年相比如今不是很是廣泛。編程人員已經逐漸地使用方法複製信息,從人不在須要按住鼠標鍵(許多用戶發現這一技術比較困難)。支持鼠標拖放功能的程序一般將它用做爲高級用戶提供的一種快捷方式,而不是一種標準的工做方式。

  本質上,拖放操做經過如下三個步驟進行:

  (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
相關文章
相關標籤/搜索