程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 《Programming WPF》翻譯 第3章 2.處理輸入

《Programming WPF》翻譯 第3章 2.處理輸入

編輯:關於.NET

在Windows應用程序中,又3種基本的用戶輸入形式:鼠標、鍵盤和手寫板。 同時,還有一種更高級輸入方式,其可能來自快捷鍵、工具欄的按鈕、菜單項。

盡管控件擔當著主要的輸入對象,用戶界面的所有元素都可以接受輸入。不 必吃驚,這是因為,為了提供外觀,控件完全依賴於底層元素的服務,如 Rectangle和TextBlock。因此,在用戶界面內的元素類型中,所有的輸入機制都 是有用的,我們將要在接下來的章節介紹這些機制。

3.2.1 Routed事件

.Net框架定義了一個標准的機制來暴露事件。一個類可能暴露了一些事件, 每個事件可能有任意數量的訂閱者。雖然WPF也使用了這一標准機制,聲稱其克 服了一個局限:如果一個正常.NET事件沒有注冊句柄,該事件將被視為無效並忽 略。

考慮一下這對於一個典型的WPF控件意味著什麼。大多數控件是由多個可視化 組件組成的。例如,即使你為一個按鈕添加了一個非常簡單的可視化樹,這棵樹 包括一個單獨的矩形框,以及一條簡單的文本,目前有兩個元素:文本和矩形框 。不管光標是否在文本或矩形框上,這個按鈕都要響應鼠標點擊事件。在標 准.NET事件處理模型中,這意味著要為所有元素注冊MouseLeftButtonUp事件。

更嚴重的是使用WPF內容模型。一個按鈕並不局限於只有簡單文本作為標題, 它可以包含任意標簽。示例3-2是一個相當普通的情況,但即使如此,其中仍然 有6個元素:黃色的邊框,代表眼睛的兩個點,代表嘴的曲線,文本,以及作為 背景的按鈕本身。為每一個單獨元素關聯事件句柄關聯,是煩冗而且效率低下的 。幸運的是,這些並不是必需的。

圖3-2

WPF使用routed事件,該事件比其他普通事件更為直接。原先的機制是,將委 托句柄關聯到激發該事件的元素,調用該句柄。如今,一個rounted事件會調用 所有的關聯到已知代碼的句柄,從初始元素向上直到用戶界面書的根元素。

示例3-1顯示了圖3-2中按鈕的標記。如果Canvas中的一個Elliipse元素接收 到輸入,事件路由可以支持Button、Grid、Canvas和Ellispse接收事件,如圖3 -3所示。

示例3-1

圖3-3

<Button MouseLeftButtonDown="MouseButtonDownButton"
        PreviewMouseLeftButtonDown="PreviewMouseButtonDownButton">
    <Grid MouseLeftButtonDown="MouseButtonDownGrid"
            PreviewMouseLeftButtonDown="PreviewMouseButtonDownGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Canvas MouseLeftButtonDown="MouseButtonDownCanvas"
                PreviewMouseLeftButtonDown="PreviewMouseButtonDownCanvas"
                Width="20" Height="18" VerticalAlignment="Center">

            <Ellipse MouseLeftButtonDown="MouseButtonDownEllipse"
                        PreviewMouseLeftButtonDown="PreviewMouseButtonDownEllipse"
                        Canvas.Left="1" Canvas.Top="1" Width="16" Height="16"
                        Fill="Yellow" Stroke="Black" />
            <Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" Height="3"
                        Fill="Black" />
            <Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" Height="3"
                        Fill="Black" />
            <Path Data="M 5,10 A 3,3 0 0 0 13,10" Stroke="Black" />
        </Canvas>

        <TextBlock Grid.Column="1">Foo</TextBlock>
    </Grid>
</Button>

一個路由事件可以是bubbling,tunneling或Direct的。Bubbling事件以尋找 附屬到激發事件的事件句柄開始,接著尋找它的父級別,再接著是它的父級別的 父級別,依次類推,直到達到這棵樹的根,這個順序是由圖3-3的數字表明的。 Tunneling事件以相反的方式工作。它先在樹根尋找句柄,接著向下開始工作, 以原始的元素作為結束。

Direct事件的路由方式與傳統的.NET事件處理相同,只有直接附屬到原始元 素的句柄會被通知到。這典型地用於僅在它們的原始元素的上下文中有意義的那 些事件。例如,如果鼠標的進入和移開是bubbled或tunneled的,這將是無用的 。父級元素未必會關心何時鼠標從一個元素移動到另一個元素。在父一級元素, 你可能希望“鼠標移開”意味著“鼠標已經離開了父一級元素”,因為使用了 Direct事件路由,這才是它正確地意味著什麼。一旦使用bubbiling,事件將有 效的意味著“鼠標已經離開了這個元素,可能在或不在其父一級內的另一個元素 中”。

除direct事件之外,WPF還定義了很多成對(bubbling和tunneling)的路由 事件。Tunneling事件的名稱通常以Preview開始,而且會首先被激發。這將給原 始元素的父級一個看一下事件的機會,在到達其子級別之前(因此以“Preview ”為前綴。)tunneling的Preview事件直接遵循bubbling事件。在大多數情形中 ,你將要處理bubbling事件,preview事件只用於你想要阻塞一個事件時,或者 你想要父一級在正常處理事件時預先做一些事情。

在示例3-1中,大多數元素擁有事件句柄,由MouseLeftButtonDown和 PreviewMouseLeftButtonDown事件指定相應的bubbling和tunneling事件。示例 3-2顯示了相應的後台代碼文件。

示例3-2

using System;
using System.Windows;
using System.Diagnostics;


namespace EventRouting …{
    public partial class Window1 : Window …{
        public Window1(  ) …{
            InitializeComponent(  );
        }

        private void MouseButtonDownButton(object sender, RoutedEventArgs e)
        …{ Debug.WriteLine("MouseButtonDownButton"); }

        private void PreviewMouseButtonDownButton(object sender, RoutedEventArgs e)
        …{ Debug.WriteLine("PreviewMouseButtonDownButton"); }


        private void MouseButtonDownGrid(object sender, RoutedEventArgs e)
        …{ Debug.WriteLine("MouseButtonDownGrid"); }

         private void PreviewMouseButtonDownGrid(object sender, RoutedEventArgs e)
        …{ Debug.WriteLine("PreviewMouseButtonDownGrid"); }


        private void MouseButtonDownCanvas(object sender, RoutedEventArgs e)
        …{ Debug.WriteLine("MouseButtonDownCanvas"); }

        private void PreviewMouseButtonDownCanvas(object sender, RoutedEventArgs e)
        …{ Debug.WriteLine("PreviewMouseButtonDownCanvas"); }


        private void MouseButtonDownEllipse(object sender, RoutedEventArgs e)
        …{ Debug.WriteLine("MouseButtonDownEllipse"); }

        private void PreviewMouseButtonDownTextBlock (object sender,
                                                      RoutedEventArgs e)
        …{ Debug.WriteLine("PreviewMouseButtonDownEllipse"); }

    }
}

每一個句柄輸出了一條debug信息。這裡時我們獲得的debug輸出,當點擊 Canvas中的TextBlock時。

PreviewButtonDownButton
PreviewButtonDownGrid
PreviewButtonDownCanvas
PreviewButtonDownEllipse
ButtonDownEllipse
ButtonDownCanvas
ButtonDownGrid
ButtonDownButton

輸出結果證實了Preview事件是最先被激發的。還顯示了它是從Button元素開 始向下工作,正如我們對tunneling事件希望的那樣。bubbling事件則從 Ellispse開始向上工作。

Bubbling路由事件提供了很多事件,意味著你可以注冊一個單獨的事件處理 在一個控件上,而且它將為內嵌在控件中的任何元素接收事件。你不需要任何特 殊的處理以解決內嵌內容或自定義可視化內容,事件簡單的向上冒泡,並且在那 裡可以全部被處理。

3.2.1.1中止事件處理

有很多情形你可能不想讓事件冒泡。例如,你可能希望轉換事件為別的什麼 東西,Button元素有效的轉換了MouseLeftButtonDown和MouseLeftButtonUp事件 為Click事件。它抑止了底層事件,從而只有Click事件冒泡到控件之外。

任何句柄都能防止進一步的處理路由事件——通過設置RoutedEvebtArgs的 Handled屬性,如示例3-4所示。

示例3-3

private void ButtonDownCanvas(object sender, RoutedEventArgs e) …{
    Debug.WriteLine("ButtonDownCanvas");
    e.Handled = true;
}

另一個設置Handled標志的原因是,如果你想要防止正常的事件處理。一旦你 在Preview句柄中這麼做,不僅tunneling的Preview事件會停止,本應正常執行 的bubbling事件也不會被激活,因此看起來似乎事件沒有發生。

3.2.1.2確定目標

雖然在一個單獨的地方,能夠處理來自一組元素的事件,這是非常便利的, 你的句柄可能需要知道是哪個元素引起激活一個事件,你可能想這正是句柄中 sender參數的意圖。事實上,sender一直將對象歸諸於你附加到的事件句柄上。 在使用bubbling和tunneling事件的情形中,這並不總是引起事件被激活的元素 。在示例3-1中,ButtonDownWindow句柄的sneder是Window本身。

幸運的是,找到潛在的導致事件發生的元素,這是容易的。RouteEventArgs 對象作為第二個參數傳遞,提供了一個OriginalSource屬性。

3.2.1.3路由事件和正常的事件

正常的.NET事件(或者說,他們曾經稱為CLR事件),提供了一個優勢——相 對於路由事件語法:很多.NET語言對處理CLR事件提供內嵌的支持。這就提供了 最好的兩種世界:你可以使用你喜歡的語言的事件處理語法,而不是利用額外的 由路由事件提供的功能。

多虧了CLR事件機制的彈性設計。雖然這裡有一種標准的聯合了CLR事件的簡 單行為,CLR的設計者有遠見的意識到,一些應用程序需要更多的高級行為。這 些類因此可以自由的實現它們喜歡的事件。WPF獲益於這種有CLR事件定義的設計 ——這些事件內在的作為路由事件來實現。

示例3-1和示例3-2安排了事件句柄的連接,通過使用標記中的屬性。但是我 們可能已經替代地使用了正常的C#事件句柄語法來關聯構造函數中的句柄。例如 ,我們要在示例3-1中移除MouseLeftButtonDown和PreviewMouseLeftButtonDown 屬性,接著修改示例3-2的構造函數,如下面的示例3-4。

示例3-4


public Window1(  ) …{
    InitializeComponent(  );

    this.MouseLeftButtonDown += MouseButtonDownWindow;
    this.PreviewMouseLeftButtonDown += PreviewMouseButtonDownWindow;
}

我們還能對來自內嵌元素的事件進行同樣的處理。我們不得不應用x:Name屬 性為了能夠訪問C#的元素。

後台代碼經常是最好的地方來附屬事件句柄。一旦你的用戶界面有不尋常和 有創意的可視化外觀,這是一個好的時機讓xaml文件有效地被圖形設計器擁有。 一個設計者不應該知道開發者需要處理哪些事件,或者調用那些句柄函數。因此 ,你將通常要設計者在xaml中給元素命名,同時開發者將要在後台代碼附屬句柄 。

3.2.2鼠標輸入

鼠標輸入關注於哪個元素直接位於鼠標下。所有的用戶界面元素派生於 UIElement基類,這個基類定義了大量的鼠標輸入事件。這些事件列於表3-1中。

表3-1

Event Routing Meaning GotMouseCapture Bubble Element captured the mouse. LostMouseCapture Bubble Element lost mouse capture. MouseEnter Direct Mouse pointer moved into element. MouseLeave Direct Mouse pointer moved out of element. PreviewMouseLeftButtonDown, MouseLeftButtonDown

 

Tunnel, Bubble

UIElement還定義了一對屬性,表示鼠標當前是否在元素上:ISMouseOver和 ISDirectMouseOver。這兩個屬性的區別在於,當鼠標在正被討論的元素上或任 何它的子元素上時,前者為true;而後者僅當鼠標在正被討論的元素上的時候才 為true,不包括它的子元素這種情況。

注意到,上表中基本的鼠標事件設置不包括Click事件。這是因為Click一個 高級別的概念——相對於基本的輸入。一個按鈕可以被點擊——通過鼠標或鍵盤 。此外,Click並不是必要的直接符合一個單獨的鼠標事件。通常的,用戶不得 不點擊或按下或釋放鼠標,當鼠標在鼠標之上以注冊一個Click事件時。相應地 ,這些高級別的事件由更明確的元素類型提供。Control類添加了一對事件: MouseDoubleClick和PreviewMouseDoubleClick。ButtonBase——Button的基類 ,CheckBox,RadioButton,都有添加這個Click事件。

如果你使用了一個Fill屬性為透明的Shape,這個Shape將擔當輸入的目標, 一旦鼠標在Shape之上。這回有一點令人驚訝,如果你使用了一個完全透明的筆 刷。這個Shape將是不可見的,但是仍然作為輸入的目標,不管鼠標在其上看來 可能是什麼樣的。如果你想要一個填充為透明的Shape,而且不捕獲鼠標輸入, 簡單的根本不提供Fill屬性,如果Fill屬性為null值(而不是一個完全的透明筆 刷),,這個Shape將不會擔當輸入的模板。

記住,如果你考慮處理一個鼠標事件的原因是,簡單的為用戶提供某些可見 的反饋,寫一個事件句柄可能過度了。這通常是可能的,通過聲明性的屬性觸發 器和事件觸發器,可以在樣式的標簽中,完全達到你需要的可視化效果。

3.2.3鍵盤輸入

鍵盤輸入引入了focus的概念。不同於鼠標,沒法為用戶移動鍵盤在一個元素 上,從而指出輸入的目標。在Windows中,一個特定的元素被指定為擁有focus, 意味著它會擔當鍵盤輸入的目標。用戶通過點擊鼠標或Alt+Tap 在正在討論的控 件上設置focus,或者通過使用導航鍵如Tab和指針。

原則上,任何用戶元素可以獲得焦點。IsFocused屬性定義在UIElement—— FrameworkElement的基類。盡管如此,Focusable屬性決定了是否支持這個特征 在任意特定的元素上。默認的,這個值對於控件是true;對其他元素是false。

表3-2顯示了有用戶界面元素提供的盤輸入事件。所有的這些項使用tunnel和 bubble路由,分別為Preview和主要事件。

表3-2

Event Routing Meaning PreviewGotFocus, GotFocus Tunnel, Bubble Element received the focus. PreviewLostFocus, LostFocus Tunnel, Bubble Element lost the focus. PreviewKeyDown, KeyDown Tunnel, Bubble Key pressed.

 

PreviewKeyUp, KeyUp

注意到,TextInput並不是必要的鍵盤的輸入。它代表了文本的輸入在一個獨 立於設備的方式,因此這個事件也能被手動輸入的結果所激活。

3.2.4手動輸入

手寫板上的鐵筆以及其他支持手動輸入的系統,有一套自己的事件。表3-3顯 示了手動輸入事件——由用戶界面元素提供。

表3-3

Event Routing Meaning GotStylusCapture Bubble Element captured stylus. LostStylusCapture Bubble Element lost stylus capture. PreviewStylusDown, StylusDown Tunnel, Bubble Stylus touched screen over element. PreviewStylusUp, StylusUp Tunnel, Bubble Stylus left screen while over element. PreviewStylusEnter, StylusEnter Tunnel, Bubble Stylus moved into element. PreviewStylusLeave, StylusLeave Tunnel, Bubble Stylus left element. PreviewStylusInRange, StylusInRange Tunnel, Bubble Stylus moved close enough to screen to be detected. PreviewStylusOutOfRange, StylusOutOfRange Tunnel, Bubble Stylus moved out of detection range. PreviewStylusMove, StylusMove Tunnel, Bubble Stylus moved while over element. PreviewStylusInAirMove, StylusInAirMove Tunnel, Bubble Stylus moved while over element but not in contact with screen. PreviewStylusSystemGesture, StylusSystemGesture Tunnel, Bubble Stylus performed a gesture. PreviewTextInput, TextInput Tunnel, Bubble Element received text input.

3.2.5命令

很多應用程序提供了多於一種的方式來執行確定動作。例如,考慮創建一個 新文件的動作。你可以選擇Fiel——New menu item,或者你可以點擊相應的工 具欄按鈕。可選擇的,你可以使用快捷鍵如Ctrl+N。如果應用程序提供了一個腳 本系統,這個腳本還可以提供另一種執行這個動作的方式。結果是,無論你使用 什麼機制,都是一樣的,因為這裡有不同的方式調用同樣的底層命令。

WPF對這個想法提供了內嵌的支持。RoutedCommand類代表了一個可以在多種 方式調用的邏輯動作。在典型的WPF應用程序中,每個菜單項和工具欄按鈕都聯 合到一個底層的RoutedCommand對象。

RoutedCommand以一種與底層輸入表單非常相似的方式工作。當調用一個命令 的時候,它激活了兩個事件:PreviewExecuteEvent和ExecuteEvent。這些事件 在這棵元素樹中使用tunnel和bubble機制,和輸入事件的方式相同。命令的目標 是由命令的調用方式來決定。典型地,這個目標將會是當前有焦點的任何一個元 素,但是RoutedCommand還提供了一個Execute的重載方法,這會傳遞一個明確的 目標元素。

你可以從很多地方獲取一個RoutedCommand。一些控件提供了命令。例如, ScrollBar控件為它的每個動作定義了命令,使之在靜態字段有效,如 LineUpCommand和PageDownCommand。然而,大多數命令並不是唯一對應到特定的 控件。一些符合應用程序級別的動作如”新文件”或“打開”。其他動作會在控 件上被調用,但是可以被一些不同的控件實現。例如,TextBox和RichTextBox都 能處理剪切操作。

這裡有一組提供了標准命令的類。這些類顯示在表3-4中。這意味著你不需要 創建自己的RoutedCommand對象來代表最普遍的操作。此外,很多命令被內嵌控 件了解。例如TextBox和RichTextBox都支持很多標准的操作,包括clipboard, undo和redo命令。

表3-4

Class Command types ApplicationCommands Commands common to almost all applications. Includes clipboard commands, undo and redo, and document-level operations (open, close, print, etc.). ComponentCommands Operations for moving through information such as scroll up and down, move to end, and text selection. EditCommands Text-editing commands such as bold, italic, and alignment. MediaCommands Media-playing operations such as transport (play, pause, etc.), volume control, and track selection.

3.2.5.1命令句柄

作為一個有用的命令,必須有事物對其進行響應。這個工作些微不同於處理 正常的輸入事件,因為大多數不是由控件定義的命令將會處理它們。表3-4中的 類定義了95個命令,因此如果Control為每個截然不同的命令定義了CLR事件,那 將需要190個事件——一旦還要包括preview的話。這不僅會極度不廣泛,甚至還 不是一個完全的解決方案。大多數應用程序在使用標准命令的同時,還定義了他 們自身的自定義命令。明顯的可選擇性是為了RoutedCommand自身激活事件。然 而,每個命令都是一個單件。例如,只有一個ApplicationCommand.New對象。如 果你能直接添加一個句柄到命令對象,這個句柄會在任何時間運行。這個命令在 你的應用程序任何地方被調用。如果你正想處理一個命令,當此命令在一個特定 的窗口中執行的時候,會怎麼樣呢?

CommandBinding類解決了這些問題。一個CommandBinding對象映射了一個明 確的RoutedCommand到一個句柄函數上——在一個特定的用戶界面元素級別。正 是這個CommandBinding會激活PreviewExecute和Execute事件,而不是UI元素。 這些綁定保存在UI元素定義的CommandBinding屬性。示例3-5顯示了如何為一個 窗體在後台代碼文件中,處理ApplicationCommand.New命令。

示例3-5

public partial class Window1 : Window {
public Window1( ) {
InitializeComponent( );
CommandBinding cmdBindingNew = new CommandBinding (ApplicationCommands.New);
cmdBindingNew.Execute += NewCommandHandler;
CommandBindings.Add(cmdBindingNew);
}
private void NewCommandHandler(object sender, ExecuteEventArgs e) {
if (unsavedChanges) {
MessageBoxResult result = MessageBox.Show(this,
"Save changes to existing document?", "New",
MessageBoxButton.YesNoCancel);
if (result == MessageBoxResult.Cancel) {
return;
}
if (result == MessageBoxResult.Yes) {
SaveChanges( );
}
}
// Reset text box contents
inputBox.Clear( );
}
}

這段代碼依賴於命令路由的冒泡本質。頂級Window元素不同於成為命令目標 的元素,當焦點通常屬於某個窗體中的子元素時。然而,命令會向上冒泡到頂級 。這個路由對命令的處理只放在一個地方,從而變得容易。

示例3-5處理的命令是ApplicationCommand.New。如果這組標准命令並沒有滿 足你的應用程序的需要,你可以為明確的操作定義自定義命令。

3.2.5.2定義命令

示例3-6顯示了如何定義一個命令。WPF使用對象實例來確定命令的唯一性。 如果你要創建同名的第二個命令,這不會被當作同樣的命令。由於這個原因,命 令通常放置在靜態字段或屬性。

示例3-6

Example 3-6. Creating a custom command
public partial class Window1 : Window {
public static RoutedCommand FooCommand;
static Window1( ) {
InputGestureCollection fooInputs = new InputGestureCollection( );
fooInputs.Add(new KeyGesture
(Key.F,
ModifierKeys.Control|ModifierKeys.Shift));
FooCommand = new RoutedCommand("Foo", typeof(Window1), fooInputs);
}
...
}

在示例3-6中創建的Foo命令,通過一個CommandBinding被處理,正如任何其 它命令一樣。當然,用戶某種調用這個命令的方式。

3.2.5.3調用命令

不僅定義了一個自定義命令,示例3-6還顯示了一個將命令聯合到用戶輸入的 方法。配置這個特別的命令用來被一個特殊的輸入表示所調用。當前支持兩種輸 入表示類型:MouseGesture,是一個特別的由鼠標和觸筆選中的形狀; KeyGesture,正如在示例3-6中使用的,是一個特別的鍵盤快捷鍵。很多內嵌控 件聯合了標准的表示。例如,ApplicationCommand.Copy聯合了標准的鍵盤快捷 鍵,用來復制(大多數地方為Ctrl+C)。

雖然一個命令在創建的時候可以聯合一組表示, 在一個特別的窗體的上下文 中,你可能希望為這個命令分配另外的快捷鍵。為了允許這樣做,用戶界面元素 有一個InputBindings屬性。這個集合包含了InputBinding對象——聯合了輸入 表示和命令。這些增加了聯合了命令的默認表示。

輸入表示如快捷鍵,不是唯一調用命令的方式。你可以在命令上調用Execute 方法從而在代碼上調用它。正如示例3-7所示,Execute被重載了。如果你沒有傳 遞參數,這個命令目標將會是任何得到焦點的元素,正如通過一個輸入表示調用 一個命令。但是你可以傳遞任何你想要的目標元素。

示例3-7

ApplicationCommands.New.Execute( );
...or...
ApplicationCommands.New.Execute(targetElement);

你可能想,要在菜單項和工具欄按鈕的Click句柄中,編寫這樣的代碼。盡管 如此,由於命令經常聯合於菜單項和工具欄按鈕,Button和MenuItem都支持 Command屬性。這就唯一標志了要調用的命令,當元素被點擊的時候。這裡,為 命令本身,提供了一種聲明式的方式,而不是為每一個綁定到命令的UI元素提供 一個句柄。示例3-8顯示了一個聯合了標准Copy命令的Button。

示例3-8

<Button Command="Copy">Copy</Button>因為這個 示例使用了來自ApplicationCommands類的標准命令,我們可以使用這個語法的 簡寫形式,只需要指出命令名稱。因為命令不是定義表3-4中的類定義的,這就 需要一些更詳細的信息。完整的命令屬性xaml語法是:

[[xmlNamePrefix:]ClassName.]EventName

如果當前只有事件名,這個事 件假定為標准命令中的一個。例如,Undo是ApplicationCommands. Undo的簡寫 。否則,你必須提供一個類的名稱,以及可能一個命名空間前綴。如果你正在使 用自定義命令或者某個第三方組件定義的命令,這個命名空間前綴就是需要的。 與Mapping這個XML處理指令(使外部類型在xaml文件中有效)協力工作。(參見 附錄A獲取更多Mapping處理指令的信息。)

示例3-9顯示了命令名稱語法的使用——所有部分都在。M:MyCommand.Foo的 值意味著當前正在討論的命令是在mylib組件的MyLib.Commands.MyCommands類中 定義的,並且存儲在名為Foo的字段或屬性中。

示例3-9

<?Mapping ClrNamespace="MyLib.Commands" Assembly="mylib"
XmlNamespace="urn:mylib" ?>
<Window xmlns:m="urn:mylib" >
<Button Command="m:MyCommands.Foo">Custom Command</Button>
...

3.2.5.4支持命令

不僅可以被執行,命令還提供了一個QueryEnabled方法,返回了一個Boolean 值表明命令是否能被立刻調用;某些命令僅在特定的上下文中有效。這個特征可 以用來決定菜單或工具欄中的項是否應該變為灰色。調用QueryEnabled方法,會 被以Execute同樣的方式處理;CommandBinding對象用於處理這次查詢。這個綁 定激活一對PreviewQueryEnabled和QueryEnabled事件,這將以與 PreviewExecute和Execute同樣的方式進行tunnel和bubble。示例3-10顯示了如 何處理這個事件,為了系統定義的Redo命令。

示例3-10

public Window1( ) {
InitializeComponent( );
CommandBinding redoCommandBinding =
new CommandBinding(ApplicationCommands.Redo);
redoCommandBinding.QueryEnabled += RedoCommandQueryEnabled;
CommandBindings.Add(redoCommandBinding);
}
void RedoCommandQueryEnabled(object sender, QueryEnabledEventArgs e) {
if (!CanRedo( )) {
e.IsEnabled = false;
}
}

不幸的是,截止到寫作時間,當前WPF的版本並不會使菜單或工具欄中的項變 灰。它會激活QueryEnabled事件當一個菜單項被調用時,以及防止命令的執行, 如果被disabled了,但是當前不提供任何可視化的指示,來表明一個項被 disabled。我們希望這個問題會被解決在將來的版本中。

我們已經看到在WPF中控件是如何處理輸入的所有可能方式。現在讓我們開一 下一組內嵌在WPF中的控件。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved