WPF Events and Commands

  1. Subscribing to events
    1. Example subscribing to Click event
      <Button x:Name="button1" Click="button1_Click">Press Me</Button>
      
      Click event handler
    2. Or subscribe in code
      button1.Click += new RoutedEventHandler(button1_Click);
      
    3. Event handler must be in code-behind
      private void button1_Click(object sender, RoutedEventArgs e)
      {
      	label1.Content = "Button pressed!";
      }
      
  2. Event routing
    1. All WPF events are routed events
      1. Can travel up UI containers (from child to parent)
      2. Can travel down UI containers (from parent to child)
      3. List of all UI events
    2. Three types of routed events
      1. Direct events: Don't travel up or down (see Click example)
      2. Bubbling events: Travel up (Source to Window)
        <GroupBox Name="myGroupBox" Header="Bubbling Example"
            MouseLeftButtonUp="MyCallback">
            <Label x:Name="myLabel" 
        		MouseLeftButtonUp="MyCallback">Click Me</Label>
        </GroupBox>
        
        private void MyCallback(object sender, MouseButtonEventArgs e)
        {
        	// Label notified of event first, then GroupBox
        }
        
      3. Tunneling events: Travel down (top of containment hierarchy to Source)
        <GroupBox Name="myGroupBox" Header="Tunneling Example"
            PreviewMouseLeftButtonUp="MyCallback">
            <Label x:Name="myLabel" 
        		PreviewMouseLeftButtonUp="MyCallback">Click Me</Label>
        </GroupBox>
        
        private void MyCallback(object sender, MouseButtonEventArgs e)
        {
        	// GroupBox notified of event first, then Label
        }
        

        Note: All tunneling events start with "Preview"

  3. Event handler parameters
    1. sender - object where the handler was invoked
      private void button_Click(object sender, RoutedEventArgs e)
      {
      	// Using a shared handler, determine which button was pressed
      	Button srcButton = e.Source as Button;
      	if (srcButton.Name == "button1")
      		myLabel.Content = "button 1 was pressed";
      	else
      		myLabel.Content = "button 2 was pressed";
      }
      
    2. RoutedEventArgs - contains state info and event data associated with the event
      // KeyEventArgs inherits from RoutedEventArgs
      private void Grid1_PreviewKeyDown(object sender, KeyEventArgs e)
      {
          // Stop event from tunneling down further
          e.Handled = true;
      }
      
  4. Commands
    1. Overview
      1. Command is an action or task that may be triggered by different user interactions
      2. Example: Edit > Copy and a Copy toolbar button might both need to trigger the same command
      3. Commands can better synchronize a task's availability (make both Copy options disabled if no text is selected)
      4. Some common built-in commands: New, Open, Save, Close, Cut, Copy, Paste, Undo, Redo
      5. Full list of ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands, and ComponentCommands
      6. CommandBindings used to link a command to code
        1. First raises PreviewExecuted event that tunnels down the element tree searching for an element that has a CommandBinding associated with the command
        2. Raises Executed event if the preview event is not handled
    2. Example
      <Window x:Class="HelloWpf.MainWindow"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              Title="MainWindow" Height="190" Width="300">
          <Window.CommandBindings>
              <CommandBinding Command="Close" Executed="CloseCommand_Executed" />
          </Window.CommandBindings>
      
          <Grid>
              <ToolBar HorizontalAlignment="Left" VerticalAlignment="Top">
                  <Button Command="Copy">Copy</Button>
                  <Button Command="Paste">Paste</Button>
                  <Button Command="Close">Exit</Button>
              </ToolBar>
              <RichTextBox HorizontalAlignment="Left" Height="128" 
      			Margin="0,32,0,0" VerticalAlignment="Top" />
          </Grid>
      </Window>
      
      // Close command handler
      private void CloseCommand_Executed(object sender, ExecutedRoutedEventArgs e)
      {
      	Application.Current.Shutdown();
      }
      
      Example 1     Example 2
    3. Availability of a command can be controlled with CanExecute event
      <CommandBinding Command="Close" Executed="CloseCommand_Executed"
         CanExecute="CloseCommand_CanExecute" />
      
      // Don't allow closing if the file is not saved
      private void CloseCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
      {	
      	e.CanExecute = fileIsSaved;  // bool
      }
      
    4. Custom commands
      1. If the predefined commands are insufficient, a new command can be created as a custom RoutedCommand
      2. Example custom command
        <Window x:Class="CustomCommandDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:CustomCommandDemo" ... >
        	
           <Window.CommandBindings>
              <CommandBinding Command="{x:Static local:MainWindow.MyCommand}"
                 Executed="MyCommand_Executed"
                 CanExecute="MyCommand_CanExecute" />
           </Window.CommandBindings>
        
        public partial class MainWindow : Window
        {
           // Make command accessible to XAML
           public static RoutedCommand MyCommand = new RoutedCommand();
           ...
           
           private void MyCommand_Executed(object sender, ExecutedRoutedEventArgs e)
           {
              // Do something
           }
           
           private void MyCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
           {
              E.CanExecute = true;
           }
        }