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(); } }