欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
WPF快速入門(mén)系列(3)

WPF快速入門(mén)系列(3)——深入解析WPF事件機制

一、引言

WPF除了創(chuàng )建了一個(gè)新的依賴(lài)屬性系統之外,還用更高級的路由事件功能替換了普通的.NET事件。

路由事件是具有更強傳播能力的事件——它可以在元素樹(shù)上向上冒泡和向下隧道傳播,并且沿著(zhù)傳播路徑被事件處理程序處理。與依賴(lài)屬性一樣,可以使用傳統的事件方式使用路由事件。盡管路由事件的使用方式與傳統的事件一樣,但是理解其工作原理還是相當重要的。

二、路由事件的詳細介紹

對于.NET中的事件,大家應該在熟悉不過(guò)了。事件指的在某個(gè)事情發(fā)生時(shí),由對象發(fā)送用于通知代碼的消息。WPF中的路由事件允許事件可以被傳遞。例如,路由事件允許一個(gè)來(lái)自工具欄按鈕的單擊事件,在被處理之前可以傳遞到工具欄,然后再傳遞到包含工具欄的窗口。那么現在問(wèn)題來(lái)了,我怎樣在WPF中去定義一個(gè)路由事件呢?

2.1 如何定義路由事件

既然有了問(wèn)題,自然就要去解決了。在自己定義一個(gè)依賴(lài)屬性之前,首先,我們得學(xué)習下WPF框架中是怎么去定義的,然后按照WPF框架中定義的方式去試著(zhù)自己定義一個(gè)依賴(lài)屬性。下面通過(guò)Reflector工具來(lái)查看下WPF中 Button 按鈕的Click事件的定義方式。

由于Button按鈕的Click事件是繼承于 ButtonBase 基類(lèi)的,所以我們直接來(lái)查看ButtonBase中Click事件的定義。具體的定義代碼如下所示:

[Localizability(LocalizationCategory.Button), DefaultEvent("Click")]public abstract class ButtonBase : ContentControl, ICommandSource{  // 事件定義  public static readonly RoutedEvent ClickEvent;  // 事件注冊  static ButtonBase()  {      ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ButtonBase));    CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(ButtonBase), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(ButtonBase.OnCommandChanged)));    .......  }  // 傳統事件包裝    public event RoutedEventHandler Click  {    add    {      base.AddHandler(ClickEvent, value);    }    remove    {      base.RemoveHandler(ClickEvent, value);    }  }  .......} 

從上面代碼可知,路由事件的定義與依賴(lài)屬性的定義類(lèi)似,路由事件由只讀的靜態(tài)字段表示,在一個(gè)靜態(tài)構造函數通過(guò) EventManager.RegisterRoutedEvent 函數注冊,并且通過(guò)一個(gè).NET事件定義進(jìn)行包裝。

現在已經(jīng)知道了路由事件是如何在WPF框架中定義和實(shí)現的了,那要想自己定義一個(gè)路由事件也自然不在話(huà)下了。

2.2 共享路由事件

與依賴(lài)屬性一樣,可以在類(lèi)之間共享路由事件的定義。即實(shí)現路由事件的繼承。例如 UIElement 類(lèi)和ContentElement類(lèi)都使用了MouseUp事件,但MouseUp事件是由 System.Windows.Input.Mouse 類(lèi)定義的。UIElement類(lèi)和ContentElement類(lèi)只是通過(guò) RouteEvent.AddOwner 方法重用了MouseUp事件。你可以在UIElement類(lèi)的靜態(tài)構造函數找到下面的代碼:

static UIElement(){    _typeofThis = typeof(UIElement);         PreviewMouseUpEvent =  Mouse.PreviewMouseUpEvent.AddOwner(_typeofThis);    MouseUpEvent = Mouse.MouseUpEvent.AddOwner(_typeofThis);}

2.3 引發(fā)和處理路由事件

盡管路由事件通過(guò)傳統的.NET事件進(jìn)行包裝,但路由事件并不是通過(guò).NET事件觸發(fā)的,而是使用RaiseEvent方法觸發(fā)事件,所有元素都從UIElement類(lèi)繼承了該方法。下面代碼是具體ButtonBase類(lèi)中觸發(fā)路由事件的代碼:

1  protected virtual void OnClick()2 {3        RoutedEventArgs e = new RoutedEventArgs(ClickEvent, this);4        base.RaiseEvent(e);// 通過(guò)RaiseEvent方法觸發(fā)路由事件5        CommandHelpers.ExecuteCommandSource(this);6 }

而在WinForm中, Button 的Click事件是通過(guò)調用委托進(jìn)行觸發(fā)的,具體的實(shí)現代碼如下所示:

1  protected virtual void OnClick(EventArgs e)2         {3             EventHandler handler = (EventHandler)base.Events[EventClick];4             if (handler != null)5             {6                 handler(this, e); // 直接調用委托進(jìn)行觸發(fā)事件7             }8         }

對于路由事件的處理,與原來(lái)WinForm方式一樣,你可以在XAML中直接連接一個(gè)事件處理程序,具體實(shí)現代碼如下所示:

<TextBlock Margin="3" MouseUp="SomethingClick" Name="tbxTest">  text label</TextBlock>// 后臺cs代碼private void SomethingClick(object sender, MouseButtonEventArgs e){}

同時(shí)還可以通過(guò)后臺代碼的方式連接事件處理程序,具體的實(shí)現代碼如下所示:

tbxTest.MouseUp += new MouseButtonEventHandler(SomethingClick);    // 或者省略委托類(lèi)型    tbxTest.MouseUp += SomethingClick;

三、路由事件其特殊性

路由事件的特殊性在于其傳遞性,WPF中的路由事件分為三種。

  • 與普通的.NET事件類(lèi)似的直接路由事件(Direct event)。它源自一個(gè)元素,并且不傳遞給其他元素。例如,MouseEnter事件(當鼠標移動(dòng)到一個(gè)元素上面時(shí)觸發(fā))就是一個(gè)直接路由事件。
  • 在包含層次中 向上傳遞的冒泡路由事件(Bubbling event) 。例如,MouseDown事件就是一個(gè)冒泡路由事件。它首先被單擊的元素觸發(fā),接下來(lái)就是該元素的父元素觸發(fā),依此類(lèi)推,直到WPF到達元素樹(shù)的頂部為止。
  • 在包含層次中 向下傳遞的隧道路由事件(Tunneling event) 。例如PreviewKeyDown就是一個(gè)隧道路由事件。在一個(gè)窗口上按下某個(gè)鍵,首先是窗口,然后是更具體的容器,直到到達按下鍵時(shí)具有焦點(diǎn)的元素。

既然,路由事件有三種表現形式,那我們怎么去區別具體的路由事件是屬于哪種呢?辨別的方法在于路由事件的注冊方法上,當使用 EventManager.RegisterEvent 方法注冊一個(gè)路由事件時(shí),需要傳遞一個(gè) RoutingStrategy 枚舉值來(lái)標識希望應用于事件的事件行為。

3.1 冒泡路由事件

下面代碼演示了事件冒泡過(guò)程:

<Window x:Class="BubbleLabelClick.MainWindow"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    Title="MainWindow" Height="350" Width="525" MouseUp="SomethingClick">  <Grid Margin="3" MouseUp="SomethingClick">    <Grid.RowDefinitions>      <RowDefinition Height="Auto"/>      <RowDefinition Height="*"/>      <RowDefinition Height="Auto"/>      <RowDefinition Height="Auto"/>    </Grid.RowDefinitions>    <Label Margin="5" Grid.Row="0" HorizontalAlignment="Left" Background="AliceBlue"         BorderBrush="Black" BorderThickness="2" MouseUp="SomethingClick">      <StackPanel MouseUp="SomethingClick">        <TextBlock Margin="3" MouseUp="SomethingClick" Name="tbxTest">          Image and text label        </TextBlock>        <Image Source="pack://application:,,,/BubbleLabelClick;component/face.png" Stretch="None"  MouseUp="SomethingClick"/>        <TextBlock Margin="3" MouseUp="SomethingClick">          Courtest for the StackPanel        </TextBlock>			      </StackPanel>    </Label>    <ListBox Grid.Row="1" Margin="3" Name="lstMessage">	     </ListBox>    <CheckBox Margin="5" Grid.Row="2" Name="chkHandle">Handle first event</CheckBox>    <Button Click="cmdClear_Click"  Grid.Row="3" HorizontalAlignment="Right" Margin="5" Padding="3">Clear List</Button>  </Grid></Window>

其后臺代碼為:

 1     public partial class MainWindow : Window 2     { 3         public MainWindow() 4         { 5             InitializeComponent(); 6              7         } 8  9         private int eventCounter = 0;10 11         private void SomethingClick(object sender, RoutedEventArgs e)12         {13             eventCounter++;14             string message = "#" + eventCounter.ToString() + ":\r\n" + "Sender: " + sender.ToString() + "\r\n" +15                 "Source: " + e.Source + "\r\n" +16                 "Original Source: " + e.OriginalSource;17             lstMessage.Items.Add(message);18             e.Handled = (bool)chkHandle.IsChecked;19         }20 21         private void cmdClear_Click(object sender, RoutedEventArgs e)22         {23             eventCounter = 0;24             lstMessage.Items.Clear();25         }26     }

運行之后的效果圖如下所示:

單擊窗口中的笑臉圖像之后,程序的運行結果如下圖所示。

從上圖結果可以發(fā)現,MouseUp事件由下向上傳遞了5級,直到窗口級別結束。另外,如果選擇了Handle first event復選框的話(huà),SomethingClicked方法會(huì )將RoutedEventArgs.Handled屬性設置為true,表示事件已被處理,且該事件將終止向上冒泡。因此,此時(shí)列表中只能看到Image的事件,具體運行結果如下圖所示:

并且在列表框或窗口空白處進(jìn)行單擊,此時(shí)也一樣只會(huì )出現一次MouseUp事件。但單擊一個(gè)地方例外。當單擊Clear List按鈕,此時(shí)不會(huì )引發(fā)MouseUp事件。這是因為按鈕包含一些特殊的處理代碼,這些代碼會(huì )掛起MouseUp事件(即不會(huì )觸發(fā)MouseUp事件,則相應的事件處理程序也不會(huì )被調用),并引發(fā)一個(gè)更高級的Click事件,同時(shí),Handled標記被設置為true(這里指的在觸發(fā)Click事件時(shí)會(huì )把Handled設置為true),從而阻止MouseUp事件繼續向上傳遞。

3.2 隧道路由事件

隧道路由事件與冒泡路由事件的工作方式一樣,只是方向相反。 即如果上面的例子中,觸發(fā)的是一個(gè)隧道路由事件的話(huà),如果在圖像上單擊,則首先窗口觸發(fā)該隧道路由事件,然后才是Grid控件,接下來(lái)是StackPanel面板,以此類(lèi)推,直到到達實(shí)際源頭,即標簽中的圖像為止。

看了上面的介紹。隧道路由事件想必是相當好理解吧。它與冒泡路由事件的傳遞方式相反。但是我們怎樣去區別隧道路由事件呢?隧道路由事件的識別相當容易,因為 隧道路由事件都是以單詞Preview開(kāi)頭。 并且,WPF一般都成對地定義冒泡路由事件和隧道路由事件。這意味著(zhù)如果發(fā)現一個(gè)冒泡的MouseUp事件,則對應的PreviewMouseUp就是一個(gè)隧道路由事件。另外, 隧道路由事件總是在冒泡路由事件之前被觸發(fā) 。

另外需要注意的一點(diǎn)是:如果將隧道路由事件標記為已處理的,那么冒泡路由事件就不會(huì )發(fā)生。這是因為這兩個(gè)事件共享同一個(gè)RoutedEventArgs類(lèi)的實(shí)例。隧道路由事件對于來(lái)執行一些預處理操作非常有用,例如,根據鍵盤(pán)上特定的鍵執行特定操作,或過(guò)濾掉特定的鼠標操作等這樣的場(chǎng)景都可以在隧道路由事件處理程序中進(jìn)行處理。下面的示例演示了PreviewKeyDown事件的隧道過(guò)程。XAML代碼如下所示。

<Window x:Class="TunneleEvent.MainWindow"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    Title="MainWindow" Height="350" Width="525" PreviewKeyDown="SomeKeyPressed">  <Grid Margin="3" PreviewKeyDown="SomeKeyPressed">    <Grid.RowDefinitions>      <RowDefinition Height="Auto"/>      <RowDefinition Height="*"/>      <RowDefinition Height="Auto"/>      <RowDefinition Height="Auto"/>    </Grid.RowDefinitions>    <Label Margin="5" Grid.Row="0" HorizontalAlignment="Left" Background="AliceBlue"         BorderBrush="Black" BorderThickness="2" PreviewKeyDown="SomeKeyPressed">      <StackPanel>        <TextBlock Margin="3" PreviewKeyDown="SomeKeyPressed">          Image and text label        </TextBlock>        <Image Source="face.png" Stretch="None" PreviewMouseUp="SomeKeyPressed"/>        <DockPanel Margin="0,5,0,0"  PreviewKeyDown="SomeKeyPressed">          <TextBlock Margin="3"            PreviewKeyDown="SomeKeyPressed">            Type here:          </TextBlock>          <TextBox PreviewKeyDown="SomeKeyPressed" KeyDown="SomeKeyPressed"></TextBox>        </DockPanel>      </StackPanel>    </Label>    <ListBox Grid.Row="1" Margin="3" Name="lstMessage">    </ListBox>    <CheckBox Margin="5" Grid.Row="2" Name="chkHandle">Handle first event</CheckBox>    <Button Click="cmdClear_Click"  Grid.Row="3" HorizontalAlignment="Right" Margin="5" Padding="3">Clear List</Button>  </Grid></Window>

其對應的后臺cs代碼實(shí)現如下所示:

 1 public partial class MainWindow : Window 2     { 3         public MainWindow() 4         { 5             InitializeComponent(); 6         } 7  8         private int eventCounter = 0; 9 10         private void SomeKeyPressed(object sender, RoutedEventArgs e)11         {12             eventCounter++;13             string message = "#" + eventCounter.ToString() + ":\r\n" +14                 " Sender: " + sender.ToString() + "\r\n" +15                 " Source: " + e.Source + "\r\n" +16                 " Original Source: " + e.OriginalSource + "\r\n" +17                 " Event: " + e.RoutedEvent;18             lstMessage.Items.Add(message);19             e.Handled = (bool)chkHandle.IsChecked;20         }21 22         private void cmdClear_Click(object sender, RoutedEventArgs e)23         {24             eventCounter = 0;25             lstMessage.Items.Clear();26         }27     }

程序運行后的效果圖如下所示:

在文本框中按下一個(gè)鍵時(shí),事件首先在窗口觸發(fā),然后在整個(gè)層次結構中向下傳遞。具體的運行結果如下圖所示:

如果在任何位置將PreviewKeyDown事件標記為已處理,則冒泡的KeyDown事件也就不會(huì )觸發(fā)。 當勾選了Handle first event 復選框時(shí),當在輸入框中按下一個(gè)鍵時(shí),listbox中顯示的記錄只有1條記錄,因為窗口觸發(fā)的PrevieKeyDown事件處理已經(jīng)把隧道路由事件標識為已處理,所以PreviewKeyDown事件將不會(huì )向下傳遞,所以此時(shí)只會(huì )顯示一條MainWindow觸發(fā)的記錄。并且,此時(shí),你可以注意到, 我們按下的鍵上對應的字符并沒(méi)有在輸入框中顯示,因為此時(shí)并沒(méi)有觸發(fā)Textbox中的KeyDown事件,因為改變文本框內容的處理是在KeyDown事件中處理的。 具體的運行結果如下圖所示:

3.3 附加事件

在上面例子中,因為所有元素都支持MouseUp和PreviewKeyDown事件。然而,許多控件都有它們自己特殊的事件。例如按鈕的的Click事件,其他任何類(lèi)都有定義該事件。假設有這樣一個(gè)場(chǎng)景,StackPanel面板中包含了一堆按鈕,并且希望在一個(gè)事件處理程序中處理所有這些按鈕的單擊事件。首先想到的辦法就是將每個(gè)按鈕的Click事件關(guān)聯(lián)到同一個(gè)事件處理程序。但是Click事件支持事件冒泡,從而有一種更好的解決辦法??梢栽诟邔哟卧貋?lái)關(guān)聯(lián)Click事件來(lái)處理所有按鈕的單擊事件,具體的XAML代碼實(shí)現如下所示:

<Window x:Class="AttachClickEvent.MainWindow"  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  Title="MainWindow" Height="350" Width="525">    <StackPanel Margin="3" Button.Click="DoSomething">  <Button Name="btn1">Button 1</Button>  <Button Name="btn2">Button 2</Button>  <Button Name="btn3">Button 3</Button>    </StackPanel></Window>

也可以在代碼中關(guān)聯(lián)附加事件,但是需要使用UIElement.AddHandle方法,而不能使用+=運算符的方式。具體實(shí)現代碼如下所示:

// StackPanel面板命名為ButtonsPanel ButtonsPanel.AddHandler(Button.ClickEvent, new RoutedEventHandler(DoSomething));

四、WPF事件生命周期

WPF事件生命周期起始和WinForm中類(lèi)似。下面詳細解釋下WPF中事件的生命周期。

FrameworkElement類(lèi)實(shí)現了 ISupportInitialize 接口,該接口提供了兩個(gè)用于控制初始化過(guò)程的方法。第一個(gè)是 BeginInit 方法,在實(shí)例化元素后立即調用該方法。BeginInit方法被調用之后,XAML解析器設置所有元素的屬性并添加內容。第二個(gè)是EndInit方法,當初始化完成后,該方法被調用。此時(shí)引發(fā) Initialized 事件。更準確地說(shuō),XAML解析器負責調用BeginInit方法和EndInit方法。

當創(chuàng )建窗口時(shí),每個(gè)元素分支都以自下而上的方式被初始化。這意味著(zhù)位于深層的嵌套元素在它們容器之前先被初始化。當引發(fā)初始化事件時(shí),可以確保元素樹(shù)中當前元素以下的元素已經(jīng)全部完成了初始化。但是,包含當前元素的容器還沒(méi)有初始化,而且也不能假設窗口的其他部分也已經(jīng)完成初始化了。在每個(gè)元素都完成初始化之后,還需要在它們的容器中進(jìn)行布局、應用樣式,如果需要的話(huà)還會(huì )進(jìn)行數據綁定。

一旦初始化過(guò)程完成后,就會(huì )引發(fā)Loaded事件。Loaded事件和Initialized事件的發(fā)生過(guò)程相反。意思就是說(shuō),包含所有元素的窗口首先引發(fā)Loaded事件,然后才是更深層次的嵌套元素。當所有元素都引發(fā)了Loaded事件之后,窗口就變得可見(jiàn)了,并且元素都已被呈現。下圖列出了部分生命周期事件。

五、小結

到這里,WPF路由事件的內容就介紹結束了,本文首先介紹了路由事件的定義,接著(zhù)介紹了三種路由事件,WPF包括直接路由事件、冒泡路由事件和隧道路由事件,最后介紹了WPF事件的生命周期。在后面一篇文章將介紹WPF中的元素綁定。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
【W(wǎng)PF學(xué)習】第十六章 鍵盤(pán)輸入
WPF之深入淺出話(huà)事件
wpf傳遞事件和傳遞命令系統
WPF繼續響應被標記為已處理事件的方法
WPF開(kāi)發(fā)環(huán)境的基本使用
ComponentOne DataGrid for WPF 基礎教程:5.自動(dòng)添加行
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久