事件與路由事件概觀Events and routed events overview

重要 APIImportant APIs

當您使用C#、Visual Basic 或 Visual C++ component extensions (C++/cx)做為程式設計語言,以及 UI 定義的 XAML 時,我們會說明 Windows 執行階段應用程式中事件的程式設計概念。We describe the programming concept of events in a Windows Runtime app, when using C#, Visual Basic or Visual C++ component extensions (C++/CX) as your programming language, and XAML for your UI definition. 您可以在 XAML 中指派事件的處理常式,以做為 UI 元素宣告的一部分,或是在程式碼中新增處理常式。You can assign handlers for events as part of the declarations for UI elements in XAML, or you can add the handlers in code. Windows 執行階段支援「路由事件」:某些輸入事件與資料事件,可以由引發事件之物件以外的物件來處理。Windows Runtime supports routed events: certain input events and data events can be handled by objects beyond the object that fired the event. 當您定義控制項範本或是使用頁面或配置容器時,路由事件非常實用。Routed events are useful when you define control templates, or use pages or layout containers.

程式設計概念的事件Events as a programming concept

一般而言,進行 Windows 執行階段應用程式的程式設計時,所使用的事件概念與最受歡迎的程式設計語言中的事件模型類似。Generally speaking, event concepts when programming a Windows Runtime app are similar to the event model in most popular programming languages. 如果您已經知道如何使用 Microsoft .NET 或 C++ 事件,就能輕易理解。If you know how to work with Microsoft .NET or C++ events already, you have a head start. 然而,您只需對事件模型概念有基本的認識,就能夠執行一些基本的工作,像是附加處理常式。But you don't need to know that much about event model concepts to perform some basic tasks, such as attaching handlers.

當您使用 C#、Visual Basic 或 C++/CX 做為程式設計語言時,UI 會定義在標記 (XAML) 中。When you use C#, Visual Basic or C++/CX as your programming language, the UI is defined in markup (XAML). 就 XAML 標記語法而言,在標記元素與執行階段程式碼實體間之連接事件的一些原理,與其他 Web 技術 (像是 ASP.NET 或 HTML5) 類似。In XAML markup syntax, some of the principles of connecting events between markup elements and runtime code entities are similar to other Web technologies, such as ASP.NET, or HTML5.

請注意,為 XAML 定義的 UI 提供執行時間邏輯的 @no__t 1The 程式碼,通常稱為程式碼後置或程式碼後置檔案。Note  The code that provides the runtime logic for a XAML-defined UI is often referred to as code-behind or the code-behind file. 在 Microsoft Visual Studio 方案檢視中,會以圖形顯示這個關係,而程式碼後置檔案對它所參考的 XAML 頁面而言,是相依與巢狀的檔案。In the Microsoft Visual Studio solution views, this relationship is shown graphically, with the code-behind file being a dependent and nested file versus the XAML page it refers to.

Button.Click:事件與 XAML 的簡介Button.Click: an introduction to events and XAML

Windows 執行階段應用程式最常見的一項程式設計工作,是將使用者輸入擷取到 UI。One of the most common programming tasks for a Windows Runtime app is to capture user input to the UI. 例如,您的 UI 可能包含按鈕,使用者必須按下按鈕才能送出資訊或變更狀態。For example, your UI might have a button that the user must click to submit info or to change state.

您是透過產生 XAML 的方式定義 Windows 執行階段應用程式的 UI。You define the UI for your Windows Runtime app by generating XAML. 此 XAML 通常是 Visual Studio 設計介面的輸出。This XAML is usually the output from a design surface in Visual Studio. 您也可以在純文字編輯器或其他廠商的 XAML 編輯器中撰寫 XAML。You can also write the XAML in a plain-text editor or a third-party XAML editor. 在產生該 XAML 的過程中,您可以連接個別 UI 元素的事件處理常式,同時定義建立該 UI 元素的屬性值的其他所有 XAML 屬性。While generating that XAML, you can wire event handlers for individual UI elements at the same time that you define all the other XAML attributes that establish property values of that UI element.

若要在 XAML 中連接事件,請指定您已定義或稍後將在程式碼後置中定義之處理常式方法的字串格式名稱。To wire the events in XAML, you specify the string-form name of the handler method that you've already defined or will define later in your code-behind. 例如,下列 XAML 定義一個將其他屬性 (x:Name 屬性Content) 指派為屬性的 Button 物件,並參考名為 ShowUpdatesButton_Click 的方法來連接按鈕的 Click 事件的處理常式:For example, this XAML defines a Button object with other properties (x:Name attribute, Content) assigned as attributes, and wires a handler for the button's Click event by referencing a method named ShowUpdatesButton_Click:

<Button x:Name="showUpdatesButton"
  Content="{Binding ShowUpdatesText}"
  Click="ShowUpdatesButton_Click"/>

  提示事件連接」是一個程式設計術語。Tip  Event wiring is a programming term. 它指的是您在表示發生某個事件時應叫用具名處理常式方法的處理程序或程式碼。It refers to the process or code whereby you indicate that occurrences of an event should invoke a named handler method. 在大部分的程序性程式碼模型中,事件連接是隱含或明確的 "AddHandler" 程式碼,可以為事件和方法命名,通常包含目標物件執行個體。In most procedural code models, event wiring is implicit or explicit "AddHandler" code that names both the event and method, and usually involves a target object instance. 在 XAML 中,"AddHandler" 是隱含的,而事件連接完全是由下列兩個動作所組成:將事件命名為物件元素的屬性名稱,以及將處理常式命名為該屬性的值。In XAML, the "AddHandler" is implicit, and event wiring consists entirely of naming the event as the attribute name of an object element, and naming the handler as that attribute's value.

您是以用來撰寫所有 app 之程式碼和程式碼後置的程式設計語言,來撰寫實際的處理常式。You write the actual handler in the programming language that you're using for all your app's code and code-behind. 使用屬性 Click="ShowUpdatesButton_Click" 會建立一個協定,也就是當 XAML 是以標記編譯和剖析時,您的 IDE 建置動作的 XAML 標記編譯步驟和應用程式載入時的最終 XAML 剖析,都可以在應用程式的程式碼中找到名為 ShowUpdatesButton_Click 的方法。With the attribute Click="ShowUpdatesButton_Click", you have created a contract that when the XAML is markup-compiled and parsed, both the XAML markup compile step in your IDE's build action and the eventual XAML parse when the app loads can find a method named ShowUpdatesButton_Click as part of the app's code. ShowUpdatesButton_Click 必須是一個方法,它會針對Click事件的任何處理程式,執行相容的方法簽章(根據委派)。ShowUpdatesButton_Click must be a method that implements a compatible method signature (based on a delegate) for any handler of the Click event. 例如,下列程式碼會定義 ShowUpdatesButton_Click 處理常式。For example, this code defines the ShowUpdatesButton_Click handler.

private void ShowUpdatesButton_Click (object sender, RoutedEventArgs e) 
{
    Button b = sender as Button;
    //more logic to do here...
}
Private Sub ShowUpdatesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim b As Button = CType(sender, Button)
    '  more logic to do here...
End Sub
void winrt::MyNamespace::implementation::BlankPage::ShowUpdatesButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
    auto b{ sender.as<Windows::UI::Xaml::Controls::Button>() };
    // More logic to do here.
}
void MyNamespace::BlankPage::ShowUpdatesButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) 
{
    Button^ b = (Button^) sender;
    //more logic to do here...
}

在此範例中,ShowUpdatesButton_Click 方法是以 RoutedEventHandler 委派為基礎。In this example, the ShowUpdatesButton_Click method is based on the RoutedEventHandler delegate. 您會知道這就是要使用的委派,因為 MSDN 參考頁面上 Click 方法的語法中將會提及該委派。You'd know that this is the delegate to use because you'll see that delegate named in the syntax for the Click method on the MSDN reference page.

秘訣  Visual Studio 提供便利的方式來命名事件處理常式,並在編輯 XAML 時定義處理常式方法。Tip  Visual Studio provides a convenient way to name the event handler and define the handler method while you're editing XAML. 當您在 XAML 文字編輯器中提供事件的屬性名稱時,請稍候片刻,讓 Microsoft IntelliSense 清單顯示。When you provide the attribute name of the event in the XAML text editor, wait a moment until a Microsoft IntelliSense list displays. 如果您按一下清單中的 [<新事件處理常式>],Microsoft Visual Studio 會根據元素的 x:Name (或類型名稱)、事件名稱以及數值尾碼來建議方法名稱。If you click <New Event Handler> from the list, Microsoft Visual Studio will suggest a method name based on the element's x:Name (or type name), the event name, and a numeric suffix. 然後,您可以在選取的事件處理常式名稱上按一下滑鼠右鍵,再按一下 [巡覽至事件處理常式]。You can then right-click the selected event handler name and click Navigate to Event Handler. 這會直接瀏覽到新插入的事件處理常式定義,如您在 XAML 頁面程式碼後置檔案的程式碼編輯器檢視中所見。This will navigate directly to the newly inserted event handler definition, as seen in the code editor view of your code-behind file for the XAML page. 事件處理常式已經有正確的簽章,其中包含事件使用的 sender 參數及事件資料類別。The event handler already has the correct signature, including the sender parameter and the event data class that the event uses. 此外,如果您的程式碼後置中已經有正確簽章的處理常式方法,這個方法的名稱會顯示在自動完成下拉式清單中,連同顯示 [<新事件處理常式>] 選項。Also, if a handler method with the correct signature already exists in your code-behind, that method's name appears in the auto-complete drop-down along with the <New Event Handler> option. 您也可以按下 Tab 鍵做為快速鍵,以取代按一下 IntelliSense 清單項目。You can also press the Tab key as a shortcut instead of clicking the IntelliSense list items.

定義事件處理常式Defining an event handler

對於在 XAML 中宣告的 UI 元素物件,事件處理常式程式碼是定義在做為 XAML 頁面程式碼後置的部分類別中。For objects that are UI elements and declared in XAML, event handler code is defined in the partial class that serves as the code-behind for a XAML page. 事件處理常式是您撰寫為部分類別中一部分的方法,這個部分類別與您的 XAML 相關聯。Event handlers are methods that you write as part of the partial class that is associated with your XAML. 這些事件處理常式是根據特定事件使用的委派。These event handlers are based on the delegates that a particular event uses. 您的事件處理常式方法可以是公用或私用的。Your event handler methods can be public or private. 使用私用存取的原因,是因為程式碼產生最終會聯結 XAML 建立的處理常式與執行個體。Private access works because the handler and instance created by the XAML are ultimately joined by code generation. 一般而言,建議您在類別中將事件處理常式方法建立為私用的。In general, we recommend that you make your event handler methods private in the class.

請注意@no__t- C++不會在部分類別中定義1Event 處理常式,而是在標頭中宣告為私用類別成員。Note  Event handlers for C++ don't get defined in partial classes, they are declared in the header as a private class member. C++ 專案的建置動作會負責處理程式碼的產生,而這些程式碼可支援 C++ 的 XAML 類型系統和程式碼後置模型。The build actions for a C++ project take care of generating code that supports the XAML type system and code-behind model for C++.

sender 參數與事件資料The sender parameter and event data

您為事件撰寫的處理常式可以存取兩個值,這兩個值可做為您的處理常式被叫用時的輸入使用。The handler you write for the event can access two values that are available as input for each case where your handler is invoked. 第一個值是 sender,為針對附加處理常式之物件的參考。The first such value is sender, which is a reference to the object where the handler is attached. sender 參數的類型是基底 Object 類型。The sender parameter is typed as the base Object type. 常見的技巧是將 sender 轉換成更精確的類型。A common technique is to cast sender to a more precise type. 如果您想在 sender 物件本身上進行檢查或變更狀態,這個技巧就很有用。This technique is useful if you expect to check or change state on the sender object itself. 依據您 app 的設計,您通常可以依據處理程式的附加位置或是其他設計細節,來了解是否可以安全地將 sender 轉換成某個類型。Based on your own app design, you usually know a type that is safe to cast sender to, based on where the handler is attached or other design specifics.

第二個值是事件資料,通常以 e 參數的形式顯示在語法定義中。The second value is event data, which generally appears in syntax definitions as the e parameter. 您可以查看指派給您正在處理的特定事件之委派的 e 參數,然後在 Visual Studio 中使用 IntelliSense 或物件瀏覽器,以探索可用的事件資料屬性。You can discover which properties for event data are available by looking at the e parameter of the delegate that is assigned for the specific event you are handling, and then using IntelliSense or Object Browser in Visual Studio. 或者,您可以使用 Windows 執行階段參考文件。Or you can use the Windows Runtime reference documentation.

對某些事件來說,事件資料的特定屬性值與知道事件發生一樣重要。For some events, the event data's specific property values are as important as knowing that the event occurred. 輸入事件更是如此。This is especially true of the input events. 對指標事件來說,事件發生時的指標位置可能很重要。For pointer events, the position of the pointer when the event occurred might be important. 對鍵盤事件來說,所有可能的按鍵動作都會引發 KeyDownKeyUp 事件。For keyboard events, all possible key presses fire a KeyDown and KeyUp event. 為了判斷使用者按下哪個按鍵,您必須存取可供事件處理常式存取的 KeyRoutedEventArgsTo determine which key a user pressed, you must access the KeyRoutedEventArgs that is available to the event handler. 如需處理輸入事件的詳細資訊,請參閱鍵盤互動處理指標輸入For more info about handling input events, see Keyboard interactions and Handle pointer input. 輸入事件與輸入案例通常有本主題未涵蓋的其他考量,像是指標事件的指標擷取,以及鍵盤事件的輔助按鍵與平台按鍵程式碼。Input events and input scenarios often have additional considerations that are not covered in this topic, such as pointer capture for pointer events, and modifier keys and platform key codes for keyboard events.

使用 async 模式的事件處理常式Event handlers that use the async pattern

在某些情況下,您會想要在事件處理常式內使用運用 async 模式的 API。In some cases you'll want to use APIs that use an async pattern within an event handler. 例如,您可能會在 AppBar 中使用 Button 來顯示檔案選擇器並與其互動。For example, you might use a Button in an AppBar to display a file picker and interact with it. 不過,許多檔案選擇器 API 都是非同步的。However, many of the file picker APIs are asynchronous. 它們必須在 async/awaitable 範圍內呼叫,而且編譯器會強制這項條件。They have to be called within an async/awaitable scope, and the compiler will enforce this. 因此,您可以在事件處理常式中加入async關鍵字,讓處理常式現在是非同步 voidSo what you can do is add the async keyword to your event handler such that the handler is now async void. 現在,您的事件處理常式已可做出 async/awaitable 呼叫。Now your event handler is permitted to make async/awaitable calls.

如需使用 async 模式的使用者互動事件處理範例,請參閱檔案存取和選擇器 (使用 C# 或 Visual Basic 建立您的第一個 Windows 執行階段應用程式系列中的一部分)。For an example of user-interaction event handling using the async pattern, see File access and pickers (part of theCreate your first Windows Runtime app using C# or Visual Basic series). 另請參閱 [在 C 中呼叫非同步 API]。See also [Call asynchronous APIs in C).

在程式碼中新增事件處理常式Adding event handlers in code

XAML 不是將事件處理常式指派給物件的唯一方法。XAML is not the only way to assign an event handler to an object. 如果要將事件處理常式新增到程式碼中的任何指定物件 (包含無法在 XAML 中使用的物件),您可以使用語言特定的語法來新增事件處理常式。To add event handlers to any given object in code, including to objects that are not usable in XAML, you can use the language-specific syntax for adding event handlers.

在 C# 中的語法是使用 += 運算子。In C#, the syntax is to use the += operator. 您要透過參考運算子右邊的事件處理常式方法名稱來登錄處理常式。You register the handler by referencing the event handler method name on the right side of the operator.

如果您使用程式碼新增事件處理常式到顯示在執行階段 UI 中的物件,常見的做法是新增這類處理常式以回應物件存留期事件或回呼,例如 LoadedOnApplyTemplate,這樣的話,相關物件的事件處理常式就會為使用者在執行階段起始的事件做好準備。If you use code to add event handlers to objects that appear in the run-time UI, a common practice is to add such handlers in response to an object lifetime event or callback, such as Loaded or OnApplyTemplate, so that the event handlers on the relevant object are ready for user-initiated events at run time. 此範例說明頁面結構的 XAML 大綱,然後提供可將事件處理常式新增到物件的 C# 語言語法。This example shows a XAML outline of the page structure and then provides the C# language syntax for adding an event handler to an object.

<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded">
  <StackPanel>
    <TextBlock Name="textBlock1">Put the pointer over this text</TextBlock>
...
  </StackPanel>
</Grid>
void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
    textBlock1.PointerEntered += textBlock1_PointerEntered;
    textBlock1.PointerExited += textBlock1_PointerExited;
}

注意  a 更詳細的語法存在。Note  A more verbose syntax exists. 在 2005 年時,C# 新增一項稱為委派推斷的功能,可以讓編譯器推斷新的委派執行個體,並啟用更簡單的舊語法。In 2005, C# added a feature called delegate inference, which enables a compiler to infer the new delegate instance and enables the previous, simpler syntax. 詳細語法在功能上與上一個範例相同,但會在登錄之前先明確地建立新的委派執行個體,因此不會利用委派推斷。The verbose syntax is functionally identical to the previous example, but explicitly creates a new delegate instance before registering it, thus not taking advantage of delegate inference. 這個明確的語法較不常見,但是在有些程式碼範例中還是可能會看到它。This explicit syntax is less common, but you might still see it in some code examples.

void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
    textBlock1.PointerEntered += new PointerEventHandler(textBlock1_PointerEntered);
    textBlock1.PointerExited += new MouseEventHandler(textBlock1_PointerExited);
}

Visual Basic 語法有兩種可能性。There are two possibilities for Visual Basic syntax. 一種會呼應 C# 語法,並直接將處理常式附加到執行個體。One is to parallel the C# syntax and attach handlers directly to instances. 這種方法需要 AddHandler 關鍵字,也需要解除參照處理常式方法名稱的 AddressOf 運算子。This requires the AddHandler keyword and also the AddressOf operator that dereferences the handler method name.

Visual Basic 語法的另一種選項,是在事件處理常式使用 Handles 關鍵字。The other option for Visual Basic syntax is to use the Handles keyword on event handlers. 若預期處理常式於載入期間會存在於物件上,並於整個物件存留期中持續存在,便適合使用這個技巧。This technique is appropriate for cases where handlers are expected to exist on objects at load time and persist throughout the object lifetime. 若要在 XAML 中定義的物件上使用 Handles,您必須提供 Name / x:NameUsing Handles on an object that is defined in XAML requires that you provide a Name / x:Name. 這個名稱會成為 Handles 語法之一部分的 Instance.Event 所需要的執行個體限定詞。This name becomes the instance qualifier that is needed for the Instance.Event part of the Handles syntax. 在這個案例中,您不需要物件存留期型的事件處理常式,就能夠開始附加其他事件處理常式;當您編譯 XAML 頁面時,就會建立 Handles 連線。In this case you don't need an object lifetime-based event handler to initiate attaching the other event handlers; the Handles connections are created when you compile your XAML page.

Private Sub textBlock1_PointerEntered(ByVal sender As Object, ByVal e As PointerRoutedEventArgs) Handles textBlock1.PointerEntered
' ...
End Sub

注意  Visual Studio 及其 XAML 設計介面通常會升級實例處理技術, 而不是控制碼關鍵字。Note  Visual Studio and its XAML design surface generally promote the instance-handling technique instead of the Handles keyword. 這是因為在 XAML 中建立事件處理常式連接,是設計人員與開發人員之間一般工作流程的一部分,而 Handles 關鍵字技巧與連接 XAML 中的事件處理常式不相容。This is because establishing the event handler wiring in XAML is part of typical designer-developer workflow, and the Handles keyword technique is incompatible with wiring the event handlers in XAML.

在C++/cx 中,您也會使用 += 語法,但與基本C#形式不同:In C++/CX, you also use the += syntax, but there are differences from the basic C# form:

  • 不具備委派推斷功能,因此必須針對委派執行個體使用 ref newNo delegate inference exists, so you must use ref new for the delegate instance.
  • 委派建構函式有兩個參數,並且需要以目標物件做為第一個參數。The delegate constructor has two parameters, and requires the target object as the first parameter. 通常您是指定這個Typically you specify this.
  • 委派建構函式需以方法位址做為第二個參數,因此 & 參考運算子必須位於方法名稱之前。The delegate constructor requires the method address as the second parameter, so the & reference operator precedes the method name.
textBlock1().PointerEntered({this, &MainPage::TextBlock1_PointerEntered });
textBlock1->PointerEntered += 
ref new PointerEventHandler(this, &BlankPage::textBlock1_PointerEntered);

移除程式碼中的事件處理常式Removing event handlers in code

通常,即使您在程式碼中加入事件處理常式,也沒有必要將該事件處理常式從程式碼移除。It's not usually necessary to remove event handlers in code, even if you added them in code. 在物件與主要 Window 及其視覺化樹狀結構中斷連線之後,大多數 Windows 執行階段物件 (例如頁面和控制項) 的物件存留期行為會將物件摧毀,也會一併摧毀所有委派參考。The object lifetime behavior for most Windows Runtime objects such as pages and controls will destroy the objects when they are disconnected from the main Window and its visual tree, and any delegate references are destroyed too. .NET 會透過記憶體回收執行這項工作,而搭配 C++/CX 的 Windows 執行階段則是預設使用弱式參照。.NET does this through garbage collection and Windows Runtime with C++/CX uses weak references by default.

在某些罕見的情況下,您確實會想要明確移除事件處理常式。There are some rare cases where you do want to remove event handlers explicitly. 它們包括:These include:

  • 您為靜態事件新增的處理常式,這些是無法以常規方式進行記憶體回收的處理常式。Handlers you added for static events, which can't get garbage-collected in a conventional way. Windows 執行階段 API 中的靜態事件範例就是 CompositionTargetClipboard 類別的事件。Examples of static events in the Windows Runtime API are the events of the CompositionTarget and Clipboard classes.
  • 將處理常式移除時間設為立即的測試程式碼,或是在執行階段交換事件的新/舊事件處理常式的程式碼。Test code where you want the timing of handler removal to be immediate, or code where you what to swap old/new event handlers for an event at run time.
  • 自訂 remove 存取子的實作。The implementation of a custom remove accessor.
  • 自訂靜態事件。Custom static events.
  • 適用於頁面瀏覽的處理常式。Handlers for page navigations.

FrameworkElementNavigatedFrom是具有適當位置的狀態管理和物件存留期的可能事件觸發程式,因此您可以使用它們來移除其他事件的處理常式。FrameworkElement.Unloaded or Page.NavigatedFrom are possible event triggers that have appropriate positions in state management and object lifetime such that you can use them for removing handlers for other events.

例如,您可以使用這段程式碼,從目標物件textBlock1中移除名為textBlock1 @ no__t-1PointerEntered的事件處理常式。For example, you can remove an event handler named textBlock1_PointerEntered from the target object textBlock1 using this code.

textBlock1.PointerEntered -= textBlock1_PointerEntered;
RemoveHandler textBlock1.PointerEntered, AddressOf textBlock1_PointerEntered

當事件是透過 XAML 屬性新增的情況下,也就是說當處理常式是在產生的程式碼中新增的情況下,您也可以移除處理常式。You can also remove handlers for cases where the event was added through a XAML attribute, which means that the handler was added in generated code. 如果您為處理常式所附加的元素提供了 Name 值,在執行上就會比較容易,因為那會在稍後為程式碼提供一個物件參照;不過,在物件沒有 Name 的情況下,您也可以瀏覽物件樹狀結構來找出所需的物件參照。This is easier to do if you provided a Name value for the element where the handler was attached, because that provides an object reference for code later; however, you could also walk the object tree in order to find the necessary object reference in cases where the object has no Name.

如果您需要移除 C++/CX 中的事件處理常式,您將需要一個註冊 Token,而您應該已經從 += 事件處理常式註冊的傳回值收到這個權杖 Token。If you need to remove an event handler in C++/CX, you'll need a registration token, which you should've received from the return value of the += event handler registration. 那是因為您在 C++/CX 語法中用於 -= 取消註冊右邊的值就是該 Token,不是方法名稱。That's because the value you use for the right side of the -= deregistration in the C++/CX syntax is the token, not the method name. 以 C++/CX 來說,您不能移除新增為 XAML 屬性的處理常式,因為 C++/CX 產生的程式碼不會儲存 Token。For C++/CX, you can't remove handlers that were added as a XAML attribute because the C++/CX generated code doesn't save a token.

路由事件Routed events

搭配 C#、Microsoft Visual Basic 或 C++/CX 的 Windows 執行階段可以針對大多數 UI 元素上的一組事件,支援路由事件的概念。The Windows Runtime with C#, Microsoft Visual Basic or C++/CX supports the concept of a routed event for a set of events that are present on most UI elements. 這些事件用於輸入與使用者互動案例,並且會在 UIElement 基底類型中實作。These events are for input and user interaction scenarios, and they are implemented on the UIElement base class. 下列是路由事件的輸入事件清單:Here's a list of input events that are routed events:

路由事件是可能由子物件傳遞 (路由) 到它在物件樹狀結構中之每個後續父物件的事件。A routed event is an event that is potentially passed on (routed) from a child object to each of its successive parent objects in an object tree. UI 的 XAML 結構接近這個物件樹,它的根是 XAML 中的根元素。The XAML structure of your UI approximates this tree, with the root of that tree being the root element in XAML. 實際的物件樹狀結構可能與 XAML 元素巢狀結構有些不同,因為物件樹狀結構未包含 XAML 語言功能,像是屬性元素標記。The true object tree might vary somewhat from the XAML element nesting, because the object tree doesn't include XAML language features such as property element tags. 您可以將路由事件想像成引發事件的任一 XAML 物件元素子元素 (針對包含它的父物件元素所引發) 中的「事件反昇」(Bubbling)。You can conceive of the routed event as bubbling from any XAML object element child element that fires the event, toward the parent object element that contains it. 事件與其事件資料可沿著事件路由在多個物件中處理。The event and its event data can be handled on multiple objects along the event route. 如果元素未具備處理常式,路由可能會持續進行,直到到達根元素為止。If no element has handlers, the route potentially keeps going until the root element is reached.

如果您了解動態 HTML (DHTML) 或 HTML5 之類的 Web 技術,可能已經熟知「事件反昇」(Bubbling) 的事件概念。If you know Web technologies such as Dynamic HTML (DHTML) or HTML5, you might already be familiar with the bubbling event concept.

當路由事件經由它的事件路由反昇時,任何附加的事件處理常式都會存取事件資料的共用執行個體。When a routed event bubbles through its event route, any attached event handlers all access a shared instance of event data. 因此,如果某個處理常式可以寫入任何事件資料,對事件資料所做的任何變更將會傳遞給下一個處理常式,因此可能不再是該事件中的原始事件資料。Therefore, if any of the event data is writeable by a handler, any changes made to event data will be passed on to the next handler, and may no longer represent the original event data from the event. 當某個事件具有路由事件行為時,參考文件將會包含關於該路由行為的備註或其他附註。When an event has a routed event behavior, the reference documentation will include remarks or other notations about the routed behavior.

RoutedEventArgsOriginalSource 屬性The OriginalSource property of RoutedEventArgs

當某個事件沿著事件路徑向上反昇時,sender 和事件引發物件已不再是相同的物件。When an event bubbles up an event route, sender is no longer the same object as the event-raising object. sender 將變成是附加叫用之處理常式的物件。Instead, sender is the object where the handler that is being invoked is attached.

在某些情況下,sender 不是您感興趣的目標,您反而是想了解其他資訊,像是指標事件觸發時,指標在哪個可能的子物件上方,或當使用者按下鍵盤按鍵時,較大 UI 中的哪個物件是焦點。In some cases, sender is not interesting, and you are instead interested in info such as which of the possible child objects the pointer is over when a pointer event fired, or which object in a larger UI held focus when a user pressed a keyboard key. 在這些情況下,您可以使用 OriginalSource 屬性的值。For these cases, you can use the value of the OriginalSource property. 在路由的所有點中,OriginalSource 會報告引發事件的原始物件,而非附加處理常式的物件。At all points on the route, OriginalSource reports the original object that fired the event, instead of the object where the handler is attached. 不過,對於 UIElement 輸入事件來說,該原始物件通常是無法立即在頁面層級的 UI 定義 XAML 中所能看到的物件。However, for UIElement input events, that original object is often an object that is not immediately visible in the page-level UI definition XAML. 該原始來源物件可能是控制項的範本組件。Instead, that original source object might be a templated part of a control. 例如,如果使用者將滑鼠指標停留在 Button 的邊緣,對於大多數的指標事件來說,OriginalSourceTemplate 中的 Border 範本組件,而非 Button 本身。For example, if the user hovers the pointer over the very edge of a Button, for most pointer events the OriginalSource is a Border template part in the Template, not the Button itself.

提示@no__t-如果您要建立樣板化控制項,1Input 事件反升會特別有用。Tip  Input event bubbling is especially useful if you are creating a templated control. 控制項的取用者可以為含有範本的任一控制項套用新範本。Any control that has a template can have a new template applied by its consumer. 正嘗試重建工作範本的取用者,可能會不慎刪除在預設範本中宣告的某些事件處理。The consumer that's trying to recreate a working template might unintentionally eliminate some event handling declared in the default template. 您仍然可以提供控制項層級的事件處理,方法是將處理常式附加為類別定義中的 OnApplyTemplate 覆寫的一部分。You can still provide control-level event handling by attaching handlers as part of the OnApplyTemplate override in the class definition. 接著,您可以攔截具現化時向上反昇至控制項根目錄的輸入事件。Then you can catch the input events that bubble up to the control's root on instantiation.

Handled 屬性The Handled property

特定路由事件的數種事件資料類別包含一個名為 Handled 的屬性。Several event data classes for specific routed events contain a property named Handled. 例如,請參閱 PointerRoutedEventArgs.HandledKeyRoutedEventArgs.Handled 以及 DragEventArgs.HandledFor examples, see PointerRoutedEventArgs.Handled, KeyRoutedEventArgs.Handled, DragEventArgs.Handled. 在所有情況下,Handled 都是可設定的布林值屬性。In all cases Handled is a settable Boolean property.

Handled 屬性設為 true 會影響事件系統行為。Setting the Handled property to true influences the event system behavior. Handledtrue 時,大多數事件處理常式的路由會停止;事件不會繼續沿著路由向其他附加的處理常式通知該特定事件案例。When Handled is true, the routing stops for most event handlers; the event doesn't continue along the route to notify other attached handlers of that particular event case. 「handled」在事件內容中的意義,以及您的 app 會如何回應它,將由您決定。What "handled" means in the context of the event and how your app responds to it is up to you. Handled 基本上是簡單的通訊協定,可讓 app 程式碼表明某個事件的發生不需反昇至任何容器,您的 app 邏輯已處理需完成的工作。Basically, Handled is a simple protocol that enables app code to state that an occurrence of an event doesn't need to bubble to any containers, your app logic has taken care of what needs done. 但相反地,您必須小心自己是否沒有處理到應該反昇的事件,導致內建的系統或控制項行為無法作用。例如,處理選取控制項的組件或項目內的低層級事件可能會有不良影響。Conversely though, you do have to be careful that you aren't handling events that probably should bubble so that built-in system or control behaviors can act. For example, handling low-level events within the parts or items of a selection control can be detrimental. 選取控制項可能會尋找輸入事件,以確認選取項目應變更。The selection control might be looking for input events to know that the selection should change.

並非所有路由事件都能以這種方式取消路由,而您也可以依據它們沒有 Handled 屬性來分辨。Not all of the routed events can cancel a route in this way, and you can tell that because they won't have a Handled property. 例如,GotFocusLostFocus 會反昇,但它們永遠會一路反昇至根目錄,而它們的事件資料類別並沒有能夠影響該行為的 Handled 屬性。For example, GotFocus and LostFocus do bubble, but they always bubble all the way to the root, and their event data classes don't have a Handled property that can influence that behavior.

控制項中的輸入事件處理常式Input event handlers in controls

特定的 Windows 執行階段控制項有時會在內部對輸入事件使用 Handled 概念。Specific Windows Runtime controls sometimes use the Handled concept for input events internally. 這樣可以使輸入事件看似從未發生,因為您的使用者程式碼無法處理它。This can make it seem like an input event never occurs, because your user code can't handle it. 例如,Button 類別包含會刻意處理一般輸入事件 PointerPressed 的邏輯。For example, the Button class includes logic that deliberately handles the general input event PointerPressed. 它會這麼做的原因,是因為按鈕會引發由 pointer-pressed 輸入與其他輸入模式 (例如可以在身為焦點時叫用按鈕的處理按鍵,如 ENTER 鍵) 起始的 Click 事件。It does so because buttons fire a Click event that is initiated by pointer-pressed input, as well as by other input modes such as handling keys like the Enter key that can invoke the button when it's focused. 針對 Button 類別設計的目的,原始輸入事件將會以概念方式處理,而類別取用者 (例如您的使用者程式碼) 可改為與控制項相關的 Click 事件互動。For purposes of the class design of Button, the raw input event is conceptually handled, and class consumers such as your user code can instead interact with the control-relevant Click event. Windows 執行階段 API 參考資料中特定控制項類別的主題,通常會提到類別會實作的事件處理行為。Topics for specific control classes in the Windows Runtime API reference often note the event handling behavior that the class implements. 在某些情況下,您可以透過覆寫 OnEvent 方法來變更行為。In some cases, you can change the behavior by overriding OnEvent methods. 例如,您可以覆寫 Control.OnKeyDown,以變更 TextBox 衍生類別回應按鍵輸入的方式。For example, you can change how your TextBox derived class reacts to key input by overriding Control.OnKeyDown.

登錄已處理之路由事件的處理常式Registering handlers for already-handled routed events

前文曾經提到,將 Handled 設為 true 可以避免呼叫大多數的處理常式。Earlier we said that setting Handled to true prevents most handlers from being called. 不過,AddHandler 方法能提供一個技巧,即使路由中的部分其他處理常式已經在共用事件資料中將 Handled 設為 true,您還是可以為路由附加永遠會被叫用的處理常式。But the AddHandler method provides a technique where you can attach a handler that is always invoked for the route, even if some other handler earlier in the route has set Handled to true in the shared event data. 如果您使用的控制項已透過在其內部結合來處理該事件,或是針對控制項特定的邏輯,這個技巧就很有用。This technique is useful if a control you are using has handled the event in its internal compositing or for control-specific logic. 但您仍然想從控制項執行個體或您的 app UI 對它做出回應。but you still want to respond to it from a control instance, or your app UI. 不過使用這個技巧時請小心,因為它可能會與 Handled 的目的衝突,並且可能會中斷控制項的目標互動。But use this technique with caution, because it can contradict the purpose of Handled and possibly break a control's intended interactions.

只有包含對應的路由事件識別碼的路由事件可以使用 AddHandler 事件處理方法技術,因為識別碼是 AddHandler 方法的必要輸入。Only the routed events that have a corresponding routed event identifier can use the AddHandler event handling technique, because the identifier is a required input of the AddHandler method. 請參閱 AddHandler 的參考文件,以取得含有可用路由事件識別碼的事件清單。See the reference documentation for AddHandler for a list of events that have routed event identifiers available. 這與我們之前說明的路由事件清單大致相同。For the most part this is the same list of routed events we showed you earlier. 例外狀況是清單中的最後兩個:GotFocusLostFocus沒有路由事件識別碼,因此您無法對這些專案使用AddHandlerThe exception is that the last two in the list: GotFocus and LostFocus don't have a routed event identifier, so you can't use AddHandler for those.

視覺化樹狀結構以外的路由事件Routed events outside the visual tree

某些物件會參與和主要視覺化樹狀結構的關係,在概念上像是重疊了主要視覺化樹狀結構。Certain objects participate in a relationship with the primary visual tree that is conceptually like having an overlay over the main visuals. 這些物件不是將所有樹狀結構元素與視覺化根目錄連接的一般父系-子系關係的一部分。These objects are not part of the usual parent-child relationships that connect all tree elements to the visual root. 任何顯示的 PopupToolTip 皆為這種案例。This is the case for any displayed Popup or ToolTip. 如果您想處理 PopupToolTip 中的路由事件,請在 PopupToolTip 內的特定 UI 元素中放置處理常式,而非在 PopupToolTip 元素本身。If you want to handle routed events from a Popup or ToolTip, place the handlers on specific UI elements that are within the Popup or ToolTip and not the Popup or ToolTip elements themselves. 不要仰賴針對 PopupToolTip 內容執行之任何結合內的路由。Don't rely on routing inside any compositing that is performed for Popup or ToolTip content. 這是因為路由事件的事件路由只會沿著主要的視覺化樹狀結構進行。This is because event routing for routed events works only along the main visual tree. PopupToolTip 不會被視為附屬 UI 元素的父系,並且永遠不會接收路由事件,即使它嘗試使用像是 Popup 之類的預設背景做為輸入事件的擷取區域也一樣。A Popup or ToolTip is not considered a parent of subsidiary UI elements and never receives the routed event, even if it is trying to use something like the Popup default background as the capture area for input events.

點擊測試和輸入事件Hit testing and input events

判斷滑鼠、觸控以及手寫筆輸入是否能夠在 UI 中看見元素以及在何處看見的動作,稱為「點擊測試」。Determining whether and where in UI an element is visible to mouse, touch, and stylus input is called hit testing. 對於觸控動作以及因為觸控動作而引發的互動特定或操作事件,元素必須具有點擊測試可見性,才能成為事件來源並引發與動作相關聯的事件。For touch actions and also for interaction-specific or manipulation events that are consequences of a touch action, an element must be hit-test visible in order to be the event source and fire the event that is associated with the action. 否則,動作會透過這個元素傳送至視覺化樹狀結構中可與該輸入進行互動的任何基礎元素或父項元素。Otherwise, the action passes through the element to any underlying elements or parent elements in the visual tree that could interact with that input. 影響點擊測試的因素有很多,不過您可以檢查元素的 IsHitTestVisible 屬性,判斷指定的元素是否會引發輸入事件。There are several factors that affect hit testing, but you can determine whether a given element can fire input events by checking its IsHitTestVisible property. 這個屬性只在元素符合以下條件時才會傳回 trueThis property returns true only if the element meets these criteria:

  • 元素的 Visibility 屬性值是 VisibleThe element's Visibility property value is Visible.
  • 元素的 BackgroundFill 屬性值不是 nullThe element's Background or Fill property value is not null. Null 筆刷值會產生透明度和點擊測試隱藏。A null Brush value results in transparency and hit test invisibility. (若要讓元素變成透明但仍可以進行點擊測試,請使用 Transparent 筆刷而不要使用 null)。(To make an element transparent but also hit testable, use a Transparent brush instead of null.)

注意  BackgroundFill 不是由 UIElement 定義的,而是由不同的衍生類別 (如 ControlShape) 定義的。Note  Background and Fill aren't defined by UIElement, and are instead defined by different derived classes such as Control and Shape. 不過,您為前景和背景屬性使用的筆刷含意,與點擊測試及輸入事件是相同的,無論該屬性是由哪個子類別實作。But the implications of brushes you use for foreground and background properties are the same for hit testing and input events, no matter which subclass implements the properties.

  • 如果元素是控制項,它的 IsEnabled 屬性值必須是 trueIf the element is a control, its IsEnabled property value must be true.
  • 元素在配置中必須具有實際的尺寸。The element must have actual dimensions in layout. ActualHeightActualWidth 為 0 的元素不會引發輸入事件。An element where either ActualHeight and ActualWidth are 0 won't fire input events.

部分控制項擁有特殊的點擊測試規則。Some controls have special rules for hit testing. 例如,TextBlock 沒有 Background 屬性,但是在其尺寸的整個區域內仍然可以進行點擊測試。For example, TextBlock has no Background property, but is still hit testable within the entire region of its dimensions. 影像MediaElement控制項會在其定義的矩形維度上進行點擊測試,而不論顯示的媒體來源檔案中的透明內容(例如 Alpha 色板)。Image and MediaElement controls are hit testable over their defined rectangle dimensions, regardless of transparent content such as alpha channel in the media source file being displayed. Web工作控制項有特殊的點擊測試行為,因為輸入可以由裝載的 HTML 和引發腳本事件來處理。WebView controls have special hit testing behavior because the input can be handled by the hosted HTML and fire script events.

大部分的 Panel 類別和 Border 在自己的背景中無法進行點擊測試,但是仍然可以處理從它們包含之元素所路由的使用者輸入事件。Most Panel classes and Border are not hit-testable in their own background, but can still handle the user input events that are routed from the elements that they contain.

無論元素是否可以進行點擊測試,您都可以判斷哪些元素與使用者輸入事件位於相同的位置。You can determine which elements are located at the same position as a user input event, regardless of whether the elements are hit-testable. 若要這樣做,請呼叫 FindElementsInHostCoordinates 方法。To do this, call the FindElementsInHostCoordinates method. 如名稱所示,這個方法會在與指定主機元素相關的位置尋找元素。As the name implies, this method finds the elements at a location relative to a specified host element. 不過,套用的轉換和配置變更會調整元素的相對座標系統,進而影響在指定位置找到的元素。However, applied transforms and layout changes can adjust the relative coordinate system of an element, and therefore affect which elements are found at a given location.

命令Commanding

少數 UI 元素支援命令功能。A small number of UI elements support commanding. 命令功能會在它的基礎實作中使用輸入相關的路由事件,並透過叫用單一命令處理常式來處理相關 UI 輸入 (某種指標動作,特定的快速鍵)。Commanding uses input-related routed events in its underlying implementation and enables processing of related UI input (a certain pointer action, a specific accelerator key) by invoking a single command handler. 如果某個 UI 元素擁有命令功能,請考慮使用它的命令 API,而非任何特定輸入事件。If commanding is available for a UI element, consider using its commanding APIs instead of any discrete input events. 您通常使用 Binding 參照到某個定義資料檢視模型的類別屬性。You typically use a Binding reference into properties of a class that defines the view model for data. 這些屬性擁有具名命令,會實作語言特定的 ICommand 命令模式。The properties hold named commands that implement the language-specific ICommand commanding pattern. 如需詳細資訊,請參閱 ButtonBase.CommandFor more info, see ButtonBase.Command.

Windows 執行階段中的自訂事件Custom events in the Windows Runtime

在定義自訂事件的目的上,您新增事件的方式及對您類別設計的意義,與您所使用的程式設計語言息息相關。For purposes of defining custom events, how you add the event and what that means for your class design is highly dependent on which programming language you are using.

  • 對於 C# 和 Visual Basic,您定義的是 CLR 事件。For C# and Visual Basic, you are defining a CLR event. 只要您不是使用自訂存取子 (add/remove),您就可以使用標準的 .NET 事件模式。You can use the standard .NET event pattern, so long as you aren't using custom accessors (add/remove). 其他提示:Additional tips:
  • 如需 C++/CX 的相關資訊,請參閱事件 (C++/CX)For C++/CX, see Events (C++/CX).
    • 使用具名參照,即使是在您自己的自訂事件使用方式中也一樣。Use named references even for your own usages of custom events. 請勿將 Lambda 用於自訂事件,它會建立循環參照。Don't use lambda for custom events, it can create a circular reference.

您無法為 Windows 執行階段宣告自訂路由事件;路由事件受限於來自 Windows 執行階段的集合。You can't declare a custom routed event for Windows Runtime; routed events are limited to the set that comes from the Windows Runtime.

定義自訂事件通常是在練習定義自訂控制項的過程中一併完成的。Defining a custom event is usually done as part of the exercise of defining a custom control. 擁有一個含有屬性變更回呼的相依性屬性,並且也定義一個在某些或所有情況下會由相依性屬性回呼引發的自訂事件,是常見的模式。It's a common pattern to have a dependency property that has a property-changed callback, and to also define a custom event that's fired by the dependency property callback in some or all cases. 您控制項的取用者無法存取您定義的屬性變更回呼,但是提供一個通知事件則是最佳的下一步。Consumers of your control don't have access to the property-changed callback you defined, but having a notification event available is the next best thing. 如需詳細資訊,請參閱自訂相依性屬性For more info, see Custom dependency properties.