#614 – Events that Fire When You Switch Between Windows

If your application has multiple windows, the user can switch between the different windows.  The sequence of events when switching from one window (1) to another (2) is as follows:

  • Window.Deactivated  (#1)
  • Window.Activated  (#2)
  • Window.PreviewLostKeyboardFocus  (#1)
  • Window.PreviewGotKeyboardFocus  (#2)
  • Window.IsKeyboardFocusWithinChanged  (#1)
  • Window.IsKeyboardFocusWithinChanged  (#2)
  • Window.IsKeyboardFocusChanged  (#1)
  • Window.IsKeyboardFocusChanged  (#2)
  • Window.LostKeyboardFocus  (#1)
  • Window.GotKeyboardFocus  (#2)
  • Window.Deactivated  (#2)

#613 – Window Event Sequence

The full sequence of events fired for a Window object are as follows.

On application startup, if the Window is the application’s main window.  (Application events are also shown in the correct sequence).

  • Application.Startup
  • Window.Initialized
  • Window.IsVisibleChanged
  • Window.SizeChanged
  • Window.LayoutUpdated
  • Window.SourceInitialized
  • Application.Activated
  • Window.Activated
  • Window.PreviewGotKeyboardFocus
  • Window.IsKeyboardFocusWithinChanged
  • Window.IsKeyboardFocusedChanged
  • Window.GotKeyboardFocus
  • Window.LayoutUpdated
  • Window.Loaded
  • Window.ContentRendered

On normal application shutdown, the event sequence is:

  • Window.Closing
  • Window.IsVisibleChanged
  • Window.Deactivated
  • Application.Deactivated
  • Window.IsKeyboardFocusWithinChanged
  • Window.IsKeyboardFocusedChanged
  • Window.LostKeyboardFocus
  • Window.Closed
  • Application.Exit

When application/window loses focus (user switches to another application):

  • Window.Deactivated
  • Application.Deactivated
  • Window.IsKeyboardFocusWithinChanged
  • Window.IsKeyboardFocusedChanged
  • Window.LostKeyboardFocus

When application/window gains focus (user switches back to application):

  • Application.Activated
  • Window.Activated
  • Window.PreviewGotKeyboardFocus
  • Window.IsKeyboardFocusWithinChanged
  • Window.IsKeyboardFocusChanged
  • Window.GotKeyboardFocus

#612 – Application Event Sequence for Page-Based Applications

When you create a page-based application in WPF, you use a NavigationWindow as the main window, which in turn hosts one or more Page objects.  (If you set the StartupUri property of the Application to point to a Page, a NavigationWindow will be created automatically).

The sequence of Application events that fire for a page-based application are listed below.

When you start the application (assuming that a Page is specified as the StartupUri):

  • Startup
  • Navigating
  • Navigated
  • LoadCompleted
  • Activated

When you shut the application down normally:

  • Deactivated
  • Exit

When you navigate to a new Page:

  • Navigating
  • NavigationProgress  (1 or more times)
  • Navigated
  • LoadCompleted

User is logging out of or shutting down Windows:

  • Deactivated
  • SessionEnding

Application gains focus:

  • Activated

Application loses focus:

  • Deactivated

#611 – Set Application Exit Code in Exit Event Handler

If you’re running applications from the command line in Windows, you can return an exit code from the application and use it to indicate whether the application was successful at doing whatever it was supposed to do.  Traditionally, an exit code of 0 indicates that the application completed successfully and positive values indicate errors.

To set a WPF application’s exit code, set the ApplicationExitCode property of the ExitEventArgs object in the application’s Exit event.

        private void Application_Exit(object sender, ExitEventArgs e)
        {
            // Assume we have a boolean variable indicating whether the
            //   application did its work successfully

            // Use 0 to indicate success, 1 to indicate that something went wrong
            e.ApplicationExitCode = (everythingWorkedOk) ? 0 : 1;
        }

You could then check this code in a .bat file:

WpfApplication11.exe

REM This evaluates to true if exit code was >= 1
if errorlevel 1 (
    echo Failure
) else (
    echo Success !
)

echo Actual exit code was %errorlevel%

#610 – Application Event Sequence

The sequence of events for an application’s main Application object that are fired are as follows (all events listed in the order that they fire):

When you start the application:

  • Startup
  • Activated

When you shut the application down normally (i.e. close main window):

  • Deactivated
  • Exit

If the application experiences an unhandled exception:

  • DispatcherUnhandledException

If user is logging out of or shutting down Windows:

  • Deactivated
  • SessionEnding

Application gets focus:

  • Activated

Application loses focus:

  • Deactivated

#609 – Perform Initialization in Window.Loaded Handler

Quite often you will want to perform some sort of initialization when your application first starts up.  You could add code to do this in the constructor for your main Window object.  For example:

<StackPanel>
    <Label Name="lblToday" Content="Today is XXX"/>
</StackPanel>

 

        public MainWindow()
        {
            this.InitializeComponent();

            lblToday.Content = string.Format("Today is {0}", DateTime.Now.ToShortDateString());
        }

A better method, however, is to do this initialization in a handler for the Window.Loaded event.

        public MainWindow()
        {
            this.InitializeComponent();

            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            lblToday.Content = string.Format("Today is {0}", DateTime.Now.ToShortDateString());
        }

An exception that originates from the window’s constructor will be thrown as a XamlParseException (with the original exception in the InnerException). If, however, the exception originates from an event handler, it will not be wrapped in another exception.

#608 – Class Handlers Are Invoked Before Instance Handlers

If you have a class handler defined for a particular routed event, as well as one or more instance handlers on different elements, when the event fires, the class handler will be invoked before any instance handlers.

        public MainWindow()
        {
            this.InitializeComponent();

            // Class handler--called when user clicks on ANY Button
            EventManager.RegisterClassHandler(
                typeof(Button),
                ButtonBase.ClickEvent,
                new RoutedEventHandler(HandleAllButtons));
        }

        private void HandleAllButtons(object sender, RoutedEventArgs e)
        {
            Button b = (Button)e.Source;
            Trace.WriteLine(string.Format("* (CLASS handler) You clicked on [{0}] button", b.Content));
        }

        // Click handler for Eliot button
        private void btnEliot_Click(object sender, RoutedEventArgs e)
        {
            Trace.WriteLine("* (INSTANCE handler) You clicked on Eliot button");
        }

#607 – An Instance Handler vs. A Class Handler

In WPF, when you define an event handler, the event handler is typically an instance handler–i.e. an event handler associated with a specific instance of a control.  In the XAML below, we’ve defined an event handler for one particular Button.

<Button Content="Like" Click="btnLike_Click"/>

You can also define a class handler–an event handler that is associated with all instances of a particular type, rather than one instance.  It will be called whenever the event in question passes through any instance of the specified type.

For example, we can define a handler that will be called when the user clicks on any Button control.

        public MainWindow()
        {
            this.InitializeComponent();

            // Call when user clicks on any Button
            EventManager.RegisterClassHandler(
                typeof(Button),
                ButtonBase.ClickEvent,
                new RoutedEventHandler(HandleAllButtons));
        }

        private void HandleAllButtons(object sender, RoutedEventArgs e)
        {
            Button b = (Button)e.Source;
            Trace.WriteLine(string.Format("* You clicked on [{0}] button", b.Content));
        }

#606 – Reusing an Existing Routed Event in Your Class

When you are defining a new CLR event in a class that will wrap a routed event, you can either register your own new routed event or you can reuse a routed event that already exists in the WPF framework.

To reuse an existing routed event, you call the AddOwner method on the existing event.

    public class MyButton : Button
    {
        public static readonly RoutedEvent MyTextChangedEvent;

        static MyButton()
        {
            MyTextChangedEvent = TextBoxBase.TextChangedEvent.AddOwner(typeof(MyButton));
        }

        public event TextChangedEventHandler MyTextChanged
        {
            add { AddHandler(MyTextChangedEvent, value); }
            remove { RemoveHandler(MyTextChangedEvent, value); }
        }

        protected virtual void OnMyTextChanged()
        {
            TextChangedEventArgs evargs = new TextChangedEventArgs(MyTextChangedEvent, UndoAction.None);
            RaiseEvent(evargs);
        }

        public MyButton()
        {
            this.Click += new RoutedEventHandler(MyButton_Click);
        }

        void MyButton_Click(object sender, RoutedEventArgs e)
        {
            this.Content = this.Content + ".";
            OnMyTextChanged();
        }
    }

#605 – Using Subclasses of RoutedEventArgs

When you define your own routed event in a class, you raise the event using the UIElement.RaiseEvent method.  The RaiseEvent method accepts an instance of a RoutedEventArgs object.  Notice that this is same class type passed to event handlers that are declared using the RoutedEventHandler delegate type.

public delegate void RoutedEventHandler(Object sender, RoutedEventArgs e);

When firing a routed event, you can choose to use one of the subclasses of RoutedEventArgs, if you have additional information to pass back.

For example, you might declare your event to be of type MouseEventHandler and then pass back an instance of MouseEventArgs when raising the event.

        public event MouseEventHandler RightDrag
        {
            add { AddHandler(RightDragEvent, value); }
            remove { RemoveHandler(RightDragEvent, value); }
        }

        protected virtual void OnRightDrag(MouseEventArgs e)
        {
            MouseEventArgs evargs = new MouseEventArgs(e.MouseDevice, 0);
            evargs.RoutedEvent = RightDragEvent;
            RaiseEvent(evargs);
        }