From dd0052ecec60abe5feb560ee918c47f89a896ab1 Mon Sep 17 00:00:00 2001 From: Tris Shores <86677757+v-trisshores@users.noreply.github.com> Date: Mon, 14 Mar 2022 17:26:19 -0500 Subject: [PATCH] Content update - Preview events (user story 1878475) (#1332) * Add article, toc, and redirects * Add a prerequisites section to 3 other event articles * Further edits * Update dotnet-desktop-guide/net/wpf/toc.yml Co-authored-by: Andy (Steve) De George <67293991+adegeo@users.noreply.github.com> * Update article links * Minor clarification Co-authored-by: Andy (Steve) De George <67293991+adegeo@users.noreply.github.com> --- .openpublishing.redirection.json | 8 ++ .../how-to-add-an-event-handler-using-code.md | 8 +- .../how-to-create-a-custom-routed-event.md | 4 + .../net/wpf/events/preview-events.md | 71 +++++++++++ .../snippets/preview-events/csharp/App.xaml | 9 ++ .../preview-events/csharp/App.xaml.cs | 11 ++ .../preview-events/csharp/AssemblyInfo.cs | 10 ++ .../csharp/CodeSampleCsharp.csproj | 25 ++++ .../preview-events/csharp/MainWindow.xaml | 23 ++++ .../preview-events/csharp/MainWindow.xaml.cs | 95 ++++++++++++++ .../csharp/Properties/Resources.Designer.cs | 63 +++++++++ .../csharp/Properties/Resources.resx | 120 ++++++++++++++++++ .../preview-events/vb/Application.xaml | 9 ++ .../preview-events/vb/Application.xaml.vb | 6 + .../preview-events/vb/AssemblyInfo.vb | 9 ++ .../preview-events/vb/CodeSampleVb.vbproj | 23 ++++ .../preview-events/vb/MainWindow.xaml | 23 ++++ .../preview-events/vb/MainWindow.xaml.vb | 90 +++++++++++++ .../visual-basic-and-wpf-event-handling.md | 4 + dotnet-desktop-guide/net/wpf/toc.yml | 2 + redirects_generator/definitions.json | 5 + 21 files changed, 616 insertions(+), 2 deletions(-) create mode 100644 dotnet-desktop-guide/net/wpf/events/preview-events.md create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/App.xaml create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/App.xaml.cs create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/AssemblyInfo.cs create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/CodeSampleCsharp.csproj create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/MainWindow.xaml create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/MainWindow.xaml.cs create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/Properties/Resources.Designer.cs create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/Properties/Resources.resx create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/Application.xaml create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/Application.xaml.vb create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/AssemblyInfo.vb create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/CodeSampleVb.vbproj create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/MainWindow.xaml create mode 100644 dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/MainWindow.xaml.vb diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index 74ff216..85e4485 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -628,6 +628,14 @@ { "source_path": "dotnet-desktop-guide/framework/wpf/events/visual-basic-and-wpf-event-handling.md", "redirect_url": "/dotnet/desktop/wpf/advanced/visual-basic-and-wpf-event-handling?view=netframeworkdesktop-4.8" + }, + { + "source_path": "dotnet-desktop-guide/net/wpf/advanced/preview-events.md", + "redirect_url": "/dotnet/desktop/wpf/events/preview-events?view=netdesktop-6.0" + }, + { + "source_path": "dotnet-desktop-guide/framework/wpf/events/preview-events.md", + "redirect_url": "/dotnet/desktop/wpf/advanced/preview-events?view=netframeworkdesktop-4.8" } ] } diff --git a/dotnet-desktop-guide/net/wpf/events/how-to-add-an-event-handler-using-code.md b/dotnet-desktop-guide/net/wpf/events/how-to-add-an-event-handler-using-code.md index 1940b86..058b0e3 100644 --- a/dotnet-desktop-guide/net/wpf/events/how-to-add-an-event-handler-using-code.md +++ b/dotnet-desktop-guide/net/wpf/events/how-to-add-an-event-handler-using-code.md @@ -21,6 +21,10 @@ You can assign an event handler to an element in Windows Presentation Foundation [!INCLUDE [desktop guide under construction](../../includes/desktop-guide-preview-note.md)] +## Prerequisites + +The article assumes a basic knowledge of routed events, and that you've read [Routed events overview](/dotnet/desktop/wpf/advanced/routed-events-overview?view=netframeworkdesktop-4.8&preserve-view=true). To follow the examples in this article, it helps if you're familiar with Extensible Application Markup Language (XAML) and know how to write Windows Presentation Foundation (WPF) applications. + ## Syntax for event handler assignment C# supports event handler assignment using: @@ -31,7 +35,7 @@ C# supports event handler assignment using: VB supports event handler assignment using: - The [AddHandler](/dotnet/visual-basic/language-reference/statements/addhandler-statement) statement with the [AddressOf](/dotnet/visual-basic/language-reference/operators/addressof-operator) operator, which is also used in the CLR event handling model. -- The [Handles](/dotnet/visual-basic/language-reference/statements/handles-clause) keyword in the event handler definition. For more information, see [Visual Basic and WPF event handling](/dotnet/desktop/wpf/advanced/visual-basic-and-wpf-event-handling?view=netframeworkdesktop-4.8&preserve-view=true). +- The [Handles](/dotnet/visual-basic/language-reference/statements/handles-clause) keyword in the event handler definition. For more information, see [Visual Basic and WPF event handling](visual-basic-and-wpf-event-handling.md). - The method, together with the `AddressOf` operator to reference the event handler. ## Example @@ -68,7 +72,7 @@ The `ButtonCreatedByCode_Click` event handler obtains the following information > [!NOTE] > A key difference between a routed event and a CLR event is that a routed event traverses the element tree, looking for handlers, whereas a CLR event doesn't traverse the element tree and handlers can only attach to the source object that raised the event. As a result, a routed event `sender` can be any traversed element in the element tree. -For more information on how to create and handle routed events, see [How to create a custom routed event](/dotnet/desktop/wpf/advanced/how-to-create-a-custom-routed-event?view=netframeworkdesktop-4.8&preserve-view=true) and [Handle a routed event](/dotnet/desktop/wpf/advanced/how-to-handle-a-routed-event?view=netframeworkdesktop-4.8&preserve-view=true). +For more information on how to create and handle routed events, see [How to create a custom routed event](how-to-create-a-custom-routed-event.md) and [Handle a routed event](/dotnet/desktop/wpf/advanced/how-to-handle-a-routed-event?view=netframeworkdesktop-4.8&preserve-view=true). ## See also diff --git a/dotnet-desktop-guide/net/wpf/events/how-to-create-a-custom-routed-event.md b/dotnet-desktop-guide/net/wpf/events/how-to-create-a-custom-routed-event.md index 27a5a47..fe97bf8 100644 --- a/dotnet-desktop-guide/net/wpf/events/how-to-create-a-custom-routed-event.md +++ b/dotnet-desktop-guide/net/wpf/events/how-to-create-a-custom-routed-event.md @@ -17,6 +17,10 @@ Windows Presentation Foundation (WPF) application developers and component autho [!INCLUDE [desktop guide under construction](../../includes/desktop-guide-preview-note.md)] +## Prerequisites + +The article assumes a basic knowledge of routed events, and that you've read [Routed events overview](/dotnet/desktop/wpf/advanced/routed-events-overview?view=netframeworkdesktop-4.8&preserve-view=true). To follow the examples in this article, it helps if you're familiar with Extensible Application Markup Language (XAML) and know how to write Windows Presentation Foundation (WPF) applications. + ## Routed event steps The basic steps to create a routed event are: diff --git a/dotnet-desktop-guide/net/wpf/events/preview-events.md b/dotnet-desktop-guide/net/wpf/events/preview-events.md new file mode 100644 index 0000000..c99dde3 --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/preview-events.md @@ -0,0 +1,71 @@ +--- +title: "Preview events" +description: Learn about preview events in Windows Presentation Foundation (WPF) and how to use preview events for composite control event handling. +ms.date: "03/09/2022" +dev_langs: + - "csharp" + - "vb" +helpviewer_keywords: + - "Preview events [WPF]" + - "suppressing events [WPF]" + - "events [WPF], Preview" + - "events [WPF], suppressing" +--- + + +# Preview events (WPF .NET) + +Preview events, also known as tunneling events, are routed events that traverse downward through the element tree from the application root element to the element that raised the event. The element that raises an event is reported as the in the event data. Not all event scenarios support or require preview events. This article describes where preview events exist and how applications or components can interact with them. For information on how to create a preview event, see [How to create a custom routed event](how-to-create-a-custom-routed-event.md). + +[!INCLUDE [desktop guide under construction](../../includes/desktop-guide-preview-note.md)] + +## Prerequisites + +The article assumes a basic knowledge of routed events, and that you've read [Routed events overview](/dotnet/desktop/wpf/advanced/routed-events-overview?view=netframeworkdesktop-4.8&preserve-view=true). To follow the examples in this article, it helps if you're familiar with Extensible Application Markup Language (XAML) and know how to write Windows Presentation Foundation (WPF) applications. + +## Preview events marked as handled + +Be cautious when marking preview events as handled in event data. Marking a preview event as handled on an element other than the element that raised it can prevent the element that raised it from handling the event. Sometimes marking preview events as handled is intentional. For example, a composite control might suppress events raised by individual components and replace them with events raised by the complete control. Custom events for a control can provide customized event data and trigger based on component state relationships. + +For [input events](/dotnet/desktop/wpf/advanced/input-overview), event data is shared by both the preview and non-preview (bubbling) equivalents of each event. If you use a preview event class-handler to mark an input event as handled, class-handlers for the bubbling input event typically won't be invoked. Or, if you use a preview event instance-handler to mark an event as handled, instance-handlers for the bubbling input event typically won't be invoked. Although you can configure class and instance handlers to be invoked even if an event is marked as handled, that handler configuration isn't common. For more information about class handling and how it relates to preview events, see [Marking routed events as handled and class handling](/dotnet/desktop/wpf/advanced/marking-routed-events-as-handled-and-class-handling?view=netframeworkdesktop-4.8&preserve-view=true). + +> [!NOTE] +> Not all preview events are [tunneling]() events. For example, the input event follows a downward route through the element tree, but is a [direct]() routed event that's raised and reraised by each in the route. + +## Preview events in composite controls + +Preview events are commonly used by composite controls to handle input events raised by their components. For example, a composite control may suppress input events at the component level and replace them with one or more events that provide customized event data. For example, the WPF marks the input event as handled in its method and raises the event. The `MouseLeftButtonDown` event and its event data still continue along the element tree route, but because the event is marked as in event data, only handlers that are configured to respond to handled events are invoked. + +If you want other elements toward the root of your application to handle a routed event that's marked as handled, you can either: + +- Attach handlers by calling the method and setting the parameter `handledEventsToo` to `true`. This approach requires attaching the event handler in code-behind, after obtaining an object reference to the element that it will attach to. + +- If the event marked as handled is a bubbling event, attach handlers for the equivalent preview event if available. For instance, if a control suppresses the event, you can attach a handler for the event instead. This approach only works for base element input events that implement both [tunneling]() and [bubbling]() routing strategies and share event data. + +The following example implements a rudimentary custom control named `componentWrapper` that contains a . The control is added to a named `outerStackPanel`. + +:::code language="xaml" source="./snippets/preview-events/csharp/MainWindow.xaml" id="CustomControlEventSuppression"::: + +The `componentWrapper` control listens for the bubbling event raised by its `TextBox` component whenever a keystroke occurs. On that occurrence, the `componentWrapper` control: + +1. Marks the `KeyDown` bubbling routed event as handled to suppress it. As a result, only the `outerStackPanel` handler that's configured in code-behind to respond to _handled_ `KeyDown` events is triggered. The `outerStackPanel` handler attached in XAML for `KeyDown` events isn't invoked. + +1. Raises a custom bubbling routed event named `CustomKey`, which triggers the `outerStackPanel` handler for the `CustomKey` event. + +:::code language="csharp" source="./snippets/preview-events/csharp/MainWindow.xaml.cs" id="EventSuppressionWorkarounds"::: +:::code language="vb" source="./snippets/preview-events/vb/MainWindow.xaml.vb" id="EventSuppressionWorkarounds"::: + +The example demonstrates two workarounds for getting the suppressed `KeyDown` routed event to invoke an event handler attached to the `outerStackPanel`: + +- Attach a event handler to the `outerStackPanel`. Since a preview input routed event precedes the equivalent bubbling routed event, the `PreviewKeyDown` handler in the example runs ahead of the `KeyDown` handler that suppresses both preview and bubbling events through their shared event data. + +- Attach a `KeyDown` event handler to the `outerStackPanel` by using the method in code-behind, with the `handledEventsToo` parameter set to `true`. + +> [!NOTE] +> Marking preview or non-preview equivalents of input events as handled are both strategies for suppressing events raised by the components of a control. The approach you use depends on your application requirements. + +## See also + +- [Marking routed events as handled, and class handling](/dotnet/desktop/wpf/advanced/marking-routed-events-as-handled-and-class-handling?view=netframeworkdesktop-4.8&preserve-view=true) +- [Routed events overview](/dotnet/desktop/wpf/advanced/routed-events-overview?view=netframeworkdesktop-4.8&preserve-view=true) +- [How to create a custom routed event](how-to-create-a-custom-routed-event.md) diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/App.xaml b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/App.xaml new file mode 100644 index 0000000..867d2f0 --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/App.xaml.cs b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/App.xaml.cs new file mode 100644 index 0000000..bdad66d --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/App.xaml.cs @@ -0,0 +1,11 @@ +using System.Windows; + +namespace CodeSampleCsharp +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/AssemblyInfo.cs b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/CodeSampleCsharp.csproj b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/CodeSampleCsharp.csproj new file mode 100644 index 0000000..5621ca9 --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/CodeSampleCsharp.csproj @@ -0,0 +1,25 @@ + + + + WinExe + net6.0-windows + true + en-us + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/MainWindow.xaml b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/MainWindow.xaml new file mode 100644 index 0000000..7a9a57a --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/MainWindow.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/MainWindow.xaml.cs b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/MainWindow.xaml.cs new file mode 100644 index 0000000..4e5efef --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/MainWindow.xaml.cs @@ -0,0 +1,95 @@ +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; + +namespace CodeSampleCsharp +{ + // + public partial class MainWindow : Window + { + public MainWindow() + { + InitializeComponent(); + + // Attach a handler on outerStackPanel that will be invoked by handled KeyDown events. + outerStackPanel.AddHandler(KeyDownEvent, new RoutedEventHandler(Handler_PrintEventInfo), + handledEventsToo: true); + } + + private void ComponentWrapper_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) + { + Handler_PrintEventInfo(sender, e); + + Debug.WriteLine("KeyDown event marked as handled on componentWrapper.\r\n" + + "CustomKey event raised on componentWrapper."); + + // Mark the event as handled. + e.Handled = true; + + // Raise the custom click event. + componentWrapper.RaiseCustomRoutedEvent(); + } + + private void Handler_PrintEventInfo(object sender, System.Windows.Input.KeyEventArgs e) + { + string senderName = ((FrameworkElement)sender).Name; + string sourceName = ((FrameworkElement)e.Source).Name; + string eventName = e.RoutedEvent.Name; + string handledEventsToo = e.Handled ? " Parameter handledEventsToo set to true." : ""; + + Debug.WriteLine($"Handler attached to {senderName} " + + $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}"); + } + + private void Handler_PrintEventInfo(object sender, RoutedEventArgs e) + { + string senderName = ((FrameworkElement)sender).Name; + string sourceName = ((FrameworkElement)e.Source).Name; + string eventName = e.RoutedEvent.Name; + string handledEventsToo = e.Handled ? " Parameter handledEventsToo set to true." : ""; + + Debug.WriteLine($"Handler attached to {senderName} " + + $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}"); + } + + // Debug output: + // + // Handler attached to outerStackPanel triggered by PreviewKeyDown event raised on componentTextBox. + // Handler attached to componentTextBox triggered by KeyDown event raised on componentTextBox. + // Handler attached to componentWrapper triggered by KeyDown event raised on componentTextBox. + // KeyDown event marked as handled on componentWrapper. + // CustomKey event raised on componentWrapper. + // Handler attached to componentWrapper triggered by CustomKey event raised on componentWrapper. + // Handler attached to outerStackPanel triggered by CustomKey event raised on componentWrapper. + // Handler attached to outerStackPanel triggered by KeyDown event raised on componentTextBox. Parameter handledEventsToo set to true. + } + + public class ComponentWrapper : StackPanel + { + // Register a custom routed event using the Bubble routing strategy. + public static readonly RoutedEvent CustomKeyEvent = + EventManager.RegisterRoutedEvent( + name: "CustomKey", + routingStrategy: RoutingStrategy.Bubble, + handlerType: typeof(RoutedEventHandler), + ownerType: typeof(ComponentWrapper)); + + // Provide CLR accessors for assigning an event handler. + public event RoutedEventHandler CustomKey + { + add { AddHandler(CustomKeyEvent, value); } + remove { RemoveHandler(CustomKeyEvent, value); } + } + + public void RaiseCustomRoutedEvent() + { + // Create a RoutedEventArgs instance. + RoutedEventArgs routedEventArgs = new(routedEvent: CustomKeyEvent); + + // Raise the event, which will bubble up through the element tree. + RaiseEvent(routedEventArgs); + } + } + // +} diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/Properties/Resources.Designer.cs b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/Properties/Resources.Designer.cs new file mode 100644 index 0000000..d61a56f --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CodeSampleCsharp.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeSampleCsharp.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/Properties/Resources.resx b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/Properties/Resources.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/csharp/Properties/Resources.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/Application.xaml b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/Application.xaml new file mode 100644 index 0000000..f225972 --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/Application.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/Application.xaml.vb b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/Application.xaml.vb new file mode 100644 index 0000000..084cbe9 --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/Application.xaml.vb @@ -0,0 +1,6 @@ +Class Application + + ' Application-level events, such as Startup, Exit, and DispatcherUnhandledException + ' can be handled in this file. + +End Class diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/AssemblyInfo.vb b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/AssemblyInfo.vb new file mode 100644 index 0000000..07b608b --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/AssemblyInfo.vb @@ -0,0 +1,9 @@ +'The ThemeInfo attribute describes where any theme specific and generic resource dictionaries can be found. +'1st parameter: where theme specific resource dictionaries are located +'(used if a resource is not found in the page, +' or application resource dictionaries) + +'2nd parameter: where the generic resource dictionary is located +'(used if a resource is not found in the page, +'app, and any theme specific resource dictionaries) + diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/CodeSampleVb.vbproj b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/CodeSampleVb.vbproj new file mode 100644 index 0000000..90d032b --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/CodeSampleVb.vbproj @@ -0,0 +1,23 @@ + + + + WinExe + net6.0-windows + CodeSampleVb + en-us + true + + + + + + + + + + + + + + + diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/MainWindow.xaml b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/MainWindow.xaml new file mode 100644 index 0000000..692b05d --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/MainWindow.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/MainWindow.xaml.vb b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/MainWindow.xaml.vb new file mode 100644 index 0000000..1109da5 --- /dev/null +++ b/dotnet-desktop-guide/net/wpf/events/snippets/preview-events/vb/MainWindow.xaml.vb @@ -0,0 +1,90 @@ +' +Partial Public Class MainWindow + Inherits Window + + Public Sub New() + InitializeComponent() + + ' Attach a handler on outerStackPanel that will be invoked by handled KeyDown events. + outerStackPanel.[AddHandler](KeyDownEvent, New RoutedEventHandler(AddressOf Handler_PrintEventInfo), + handledEventsToo:=True) + End Sub + + Private Sub ComponentWrapper_KeyDown(sender As Object, e As KeyEventArgs) + Handler_PrintEventInfo(sender, e) + Debug.WriteLine("KeyDown event marked as handled on componentWrapper." & + vbCrLf & "CustomKey event raised on componentWrapper.") + + ' Mark the event as handled. + e.Handled = True + + ' Raise the custom click event. + componentWrapper.RaiseCustomRoutedEvent() + End Sub + + Private Sub Handler_PrintEventInfo(sender As Object, e As KeyEventArgs) + Dim senderName As String = CType(sender, FrameworkElement).Name + Dim sourceName As String = CType(e.Source, FrameworkElement).Name + Dim eventName As String = e.RoutedEvent.Name + Dim handledEventsToo As String = If(e.Handled, " Parameter handledEventsToo set to true.", "") + Debug.WriteLine($"Handler attached to {senderName} " & + $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}") + End Sub + + Private Sub Handler_PrintEventInfo(sender As Object, e As RoutedEventArgs) + Dim senderName As String = CType(sender, FrameworkElement).Name + Dim sourceName As String = CType(e.Source, FrameworkElement).Name + Dim eventName As String = e.RoutedEvent.Name + Dim handledEventsToo As String = If(e.Handled, " Parameter handledEventsToo set to true.", "") + Debug.WriteLine($"Handler attached to {senderName} " & + $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}") + End Sub + + ' Debug output + ' + ' Handler attached to outerStackPanel triggered by PreviewKeyDown event raised on componentTextBox. + ' Handler attached to componentTextBox triggered by KeyDown event raised on componentTextBox. + ' Handler attached to componentWrapper triggered by KeyDown event raised on componentTextBox. + ' KeyDown event marked as handled on componentWrapper. + ' CustomKey event raised on componentWrapper. + ' Handler attached to componentWrapper triggered by CustomKey event raised on componentWrapper. + ' Handler attached to outerStackPanel triggered by CustomKey event raised on componentWrapper. + ' Handler attached to outerStackPanel triggered by KeyDown event raised on componentTextBox. Parameter handledEventsToo set to true. +End Class + + Public Class ComponentWrapper + Inherits StackPanel + + ' Register a custom routed event with the Bubble routing strategy. + Public Shared ReadOnly CustomKeyEvent As RoutedEvent = + EventManager.RegisterRoutedEvent( + name:="CustomKey", + routingStrategy:=RoutingStrategy.Bubble, + handlerType:=GetType(RoutedEventHandler), + ownerType:=GetType(ComponentWrapper)) + + ' Provide CLR accessors to support event handler assignment. + Public Custom Event CustomKey As RoutedEventHandler + + AddHandler(value As RoutedEventHandler) + [AddHandler](CustomKeyEvent, value) + End AddHandler + + RemoveHandler(value As RoutedEventHandler) + [RemoveHandler](CustomKeyEvent, value) + End RemoveHandler + + RaiseEvent(sender As Object, e As RoutedEventArgs) + [RaiseEvent](e) + End RaiseEvent + + End Event + + Public Sub RaiseCustomRoutedEvent() + ' Create a RoutedEventArgs instance & raise the event, + ' which will bubble up through the element tree. + Dim routedEventArgs As New RoutedEventArgs(routedEvent:=CustomKeyEvent) + [RaiseEvent](routedEventArgs) + End Sub + End Class +' diff --git a/dotnet-desktop-guide/net/wpf/events/visual-basic-and-wpf-event-handling.md b/dotnet-desktop-guide/net/wpf/events/visual-basic-and-wpf-event-handling.md index 9236091..0ea96e2 100644 --- a/dotnet-desktop-guide/net/wpf/events/visual-basic-and-wpf-event-handling.md +++ b/dotnet-desktop-guide/net/wpf/events/visual-basic-and-wpf-event-handling.md @@ -14,6 +14,10 @@ If you're coding in Visual Basic .NET, you can use the language-specific [Handle [!INCLUDE [desktop guide under construction](../../includes/desktop-guide-preview-note.md)] +## Prerequisites + +The article assumes a basic knowledge of routed events, and that you've read [Routed events overview](/dotnet/desktop/wpf/advanced/routed-events-overview?view=netframeworkdesktop-4.8&preserve-view=true). To follow the examples in this article, it helps if you're familiar with Extensible Application Markup Language (XAML) and know how to write Windows Presentation Foundation (WPF) applications. + ## Syntax The syntax for a `Sub` declaration that uses the [Handles](/dotnet/visual-basic/language-reference/statements/handles-clause) keyword is: `Sub Handles .`. That syntax designates a procedure as the event handler that will run when an event specified by `` is raised on an object specified by ``. The event must be a member of the object's class or base class. The following example shows how to attach an event handler to a XAML element using `Handles`. diff --git a/dotnet-desktop-guide/net/wpf/toc.yml b/dotnet-desktop-guide/net/wpf/toc.yml index 40579a1..1228a8f 100644 --- a/dotnet-desktop-guide/net/wpf/toc.yml +++ b/dotnet-desktop-guide/net/wpf/toc.yml @@ -80,6 +80,8 @@ items: items: - name: Events items: + - name: Preview events + href: events/preview-events.md - name: Visual Basic and WPF event handling href: events/visual-basic-and-wpf-event-handling.md - name: Common tasks diff --git a/redirects_generator/definitions.json b/redirects_generator/definitions.json index d143353..ebc155f 100644 --- a/redirects_generator/definitions.json +++ b/redirects_generator/definitions.json @@ -343,6 +343,11 @@ "SourceUrl": "/dotnet/desktop/wpf/advanced/visual-basic-and-wpf-event-handling?view=netframeworkdesktop-4.8", "TargetUrl": "/dotnet/desktop/wpf/events/visual-basic-and-wpf-event-handling?view=netdesktop-6.0" }, + { + "Redirect": "TwoWay", + "SourceUrl": "/dotnet/desktop/wpf/advanced/preview-events?view=netframeworkdesktop-4.8", + "TargetUrl": "/dotnet/desktop/wpf/events/preview-events?view=netdesktop-6.0" + }, // Systems - Properties {