This is an old revision of the document!


Events (Direct)

Events help user controls communicate with parent controls.

A user control would create an event delegate:

public delegate void OnMyEventHandler(object sender, EventArgs e);
public event OnMyEventHandler OnMyEvent;

When the user control needs to trigger the event, call the event handler:

if (OnMyEvent != null)
{
  OnMyEvent(this, new EventArgs());
}  

Or create a function to handle it:

protected void MyEventEvent()
{
    OnMyEventHandler handler = OnMyEvent;
    if (handler != null)
    {
        handler(this, new EventArgs());
    }
}

The parent control implements an event handler to respond to that event:

public MyClassConstructor()
{
  usercontrol myUserControl = new usercontrol();
  myUserControl.OnMyEvent += new userControl.OnMyEventHandler(catchevent);
}
 
...
void catchevent(object sender, EventArgs e)
{
     // Do some work to handle event
     Panel2.Controls.remove(((control)sender));
     //or Panel2.Controls.Clear();
}

Source: Diamonddrake, Daniweb.com

Example

A class would create an event delegate:

public class TParamData
{
  // Events
  public delegate void OnChangedHandler(object sender, EventArgs e);
  public event OnChangedHandler OnChanged;
  ...
  // Fields
  ...
  // Methods
  ...
}  

When the object instance needs to trigger the event, call the event handler:

public class TParamData
{
  // Events
  ...
  // Fields and Properties
  private int m_Data;
  public int Data
  {
     set 
     {
         m_Data = value;
         if (OnChange != null)
         {
           OnChanged(this, new EventArgs());  // trigger OnChanged event
         }
     }
   }
   ...  

The parent class implements an event handler to respond to that event:

public class MyClass
{
   // Fields
   private TParamData myParam;
 
   // Constructor
   public MyClass()
   {
     myParam = new TParamData();
     myParam.OnChanged += new TParamData.OnChangedHandler(Param_OnChanged);
   }
 
   // Event Handler 
   void Param_OnChanged(object sender, EventArgs e)
   {
     // Do some work if Param is changed
     this.WriteToInstrument(myParam);
   }
   ...
}   
PropertyChanged Event

To provide INotifyPropertyChanged support for classes implementing that interface, you need to provide the PropertyChanged event.

Example

using System.Collections.ObjectModel;  // For INotifyPropertyChanged
...
 
//========================================================================
// Class TMyClass
//========================================================================
public class TPerson : INotifyPropertyChanged
{
  // Declare events
  public event PropertyChangedEventHandler PropertyChanged;
 
  // Fields
  private string m_Name = "";
  private int    m_Age  = 0;
 
  // Properties
  public string Name
  { 
    get{ return m_Name; }
    set{ m_Name = value; OnPropertyChanged("Name"); }
  }
  public string Age
  { 
    get{ return m_Age; }
    set{ m_Age = value; OnPropertyChanged("Age"); }
  }
 
  // Constructor
  public TMyClass()
  {
  }
 
  // Handler
  protected void OnPropertyChanged(string PropertyName)
  {
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(PropertyName));
    }
  }
  ...
}  
Routed Events

In WPF, you also have access to Routed Events, which have additional advantages. Events can “tunnel” or “bubble” to other components in the visual tree.

RoutedEvents are particularly useful if the listener doesn't have a direct reference to the source of the event. For example you have a container control which must react to a specific event, but the container will not always know exactly which controls inside it can/will throw it. The controls can be several levels deep, or created by a template. – Source: Bubblewrap@Stackoverflow.com

Example

An example where a container handles an event from a control that it does not know about: (Source: http://soumya.wordpress.com/2009/12/31/wpf-simplified-part-7-routed-events)

Writing a custom routed event is similar to writing a dependency property,

public static readonly RoutedEvent MyRoutedEvent = EventManager.RegisterRoutedEvent(
                               "MyRoutedEvent",            // Name of the custom routed event
                               RoutingStrategy.Bubble,     // The routing strategy
                               typeof(RoutedEventHandler), // Type of the event handler
                               typeof(MyControl));         // The type of the owner of this routed event 
 
// Provide CLR property wrapper for the routed event
public event RoutedEventHandler MyEvent
{
    add { AddHandler(MyRoutedEvent, value); }
    remove { RemoveHandler(MyRoutedEvent, value); }
}
 
// Method to raise event
public void RaiseMyRoutedEvent()
{
  RaiseEvent(new RoutedEventArgs(MyControl.MyRoutedEvent));
}

The tunneling and bubbling of a routed event occurs when every element in the route exposes that event. But WPF supports tunneling and bubbling of routed events through elements that don’t even define that event – this is possible via attached properties. Attached events operate much like attached properties (and their use with tunneling or bubbling is very similar to using attached properties with property value inheritance), elements can handle events that are declared in a different element.

Attached Event using XAML:

<Grid Button.Click="ButtonClickHandler">
    <Button Height="100" Width="100" Content="Some Text"/>
</Grid>

Grid doesn’t have a Click event, but WPF allows the Button.Click event to be raised on the Grid. The Grid can then handle this event in it’s handler “ButtonClickHandler”. Every routed event can be used as an attached event. We can also hook up the attached event in code,

Attached Event programmatically:

<Grid x:Name="MyGrid">
    <Button Height="100" Width="100" Content="Some Text"/>
</Grid>
// In code...
this.MyGrid.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonClickHandler));

Handler definition:

private void ButtonClickHandler(object sender, RoutedEventArgs e)
{
    // do something
    . . .
    e.Handled = true;  // mark event as handled so other listeners do try to handle it as well
}

Example

ucPopup.xaml:

<UserControl x:Class="InterUsrCtrlComm.ucPopupMnu"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="200">
    <Grid>
        <ToggleButton IsChecked="{Binding ElementName=popopmnuColorSelection, Path=IsOpen}" 
                      Content="Open Popup Menu" 
                      Width="130" Height="50" />
        <Popup Name="popopmnuColorSelection" 
               Visibility="Visible" Width="150"
               Placement="Bottom" 
               AllowsTransparency="False" 
               PopupAnimation="Fade" 
               VerticalOffset="-100" HorizontalOffset="100">
            <StackPanel>
                <TextBlock Background="Transparent" Foreground="White" FontWeight="Bold">Select one (double click):</TextBlock>
                <ListBox Name="lstColors" MouseDoubleClick="lstColors_MouseDoubleClick">
                    <ListBoxItem>Red</ListBoxItem>
                    <ListBoxItem>Green</ListBoxItem>
                    <ListBoxItem>Blue</ListBoxItem>
                </ListBox>
            </StackPanel>
        </Popup>
    </Grid>
</UserControl>

ucPopup.xaml.cs:

public partial class ucPopupMnu : UserControl
{
    // Register the routed event
    public static readonly RoutedEvent MenuItemSelectedEvent =
        EventManager.RegisterRoutedEvent("MenuItemSelected", RoutingStrategy.Bubble,
        typeof(RoutedEventHandler), typeof(ucPopupMnu));
 
    // CLR accessor (.NET wrapper) for routed event
    public event RoutedEventHandler MenuItemSelected
    {
        add { AddHandler(MenuItemSelectedEvent, value); }
        remove { RemoveHandler(MenuItemSelectedEvent, value); }
    }
 
    public ListBoxItem SelectedItem
    { 
        get { return (ListBoxItem)lstColors.SelectedItem; }
        set { lstColors.SelectedItem = value; }
    }
 
    public ucPopupMnu()
    {
        InitializeComponent();
    }
 
    private void lstColors_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        ListBoxItem selection = (ListBoxItem)lstColors.SelectedItem;
        //txtSelectedColor.Text = selection.Content.ToString();
 
        // Raise the routed event "selected"
        RaiseEvent(new RoutedEventArgs(ucPopupMnu.MenuItemSelectedEvent));
    }
}

And to handle events elsewhere:

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
 
        // Attach menu event handler
        popupmnu1.MenuItemSelected += new RoutedEventHandler(ucPopupMnu_MenuItemSelected);
    }
 
    /// <summary>
    /// Event handler for Menu Item Selection in Popup menu.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ucPopupMnu_MenuItemSelected(object sender, RoutedEventArgs e)
    {
        // Take action based on the menu selection
        ListBoxItem selection = popupmnu1.SelectedItem;  // Get menu selection
        MessageDlg.Show( selection.Content.ToString() ); // Display it
    }
}

Another example using a Button (Source: http://msdn.microsoft.com/en-us/library/ms752288.aspx):

<Window  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:SDKSample;assembly=SDKSampleLibrary"
    x:Class="SDKSample.RoutedEventCustomApp">
    <Window.Resources>
      <Style TargetType="{x:Type custom:MyButtonSimple}">
        <Setter Property="Height" Value="20"/>
        <Setter Property="Width" Value="250"/>
        <Setter Property="HorizontalAlignment" Value="Left"/>
        <Setter Property="Background" Value="#808080"/>
      </Style>
    </Window.Resources>
    <StackPanel Background="LightGray">
	    <custom:MyButtonSimple Name="mybtnsimple" Tap="TapHandler">Click to see Tap custom event work</custom:MyButtonSimple>
    </StackPanel>
</Window>
public class MyButtonSimple: Button
{
    // Create a custom routed event by first registering a RoutedEventID
    // This event uses the bubbling routing strategy
    public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
        "Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));
 
    // Provide CLR accessors for the event
    public event RoutedEventHandler Tap
    {
            add { AddHandler(TapEvent, value); } 
            remove { RemoveHandler(TapEvent, value); }
    }
 
    // This method raises the Tap event
    void RaiseTapEvent()
    {
            RoutedEventArgs newEventArgs = new RoutedEventArgs(MyButtonSimple.TapEvent);
            RaiseEvent(newEventArgs);
    }
    // For demonstration purposes we raise the event when the MyButtonSimple is clicked
    protected override void OnClick()
    {
        RaiseTapEvent();
    }
}
Resources