== 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: [[http://www.daniweb.com/forums/thread234998.html|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 down" or "bubble up" 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: [[http://stackoverflow.com/questions/2536950/c-wpf-routedevent-in-wpf-class-that-isnt-a-uielement|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 OnMyActionEvent = EventManager.RegisterRoutedEvent(
"OnMyAction", // 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 OnMyAction
{
add { AddHandler(OnMyActionEvent , value); }
remove { RemoveHandler(OnMyActionEvent , value); }
}
// Method to raise event
public void RaiseMyActionEvent()
{
RaiseEvent(new RoutedEventArgs(MyControl.OnMyActionEvent ));
}
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 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**:
// 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'':
Select one (double click):RedGreenBlue
''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:
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Attach menu event handler
//popupmnu1.MenuItemSelected += new RoutedEventHandler(ucPopupMnu_MenuItemSelected);
// or
AddHandler(ucPopupMnu.MenuItemSelectedEvent, new RoutedEventHandler(ucPopupMnu_MenuItemSelected));
}
///
/// Event handler for Menu Item Selection in Popup menu.
///
///
///
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]]):
Click to see Tap custom event work
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();
}
}
=== Example of Custom RoutedEvent ===
UserNotification logic is composed of a u''cUserNotification'' component that triggers the event, and a ''ucUserNotificationArea'' component to handle and display the event. This last component is usually placed in any parent control that needs to respond to user notifications triggered in child controls. There are no direct event subscriptions other than the AddHandle() performed on the parent control. It portrays how notifications can be triggers in any child controls, and then handled as an event at some ancestor control without having to know anything about the child control that triggered the event. This mechanism only works for VisualTree components. The alternative is to use CLR events, but then there must be direct subscriptions to events from the parent to the child control in order to handle those events, something we are avoiding here with this mechanism.
UserControl for UserNotification event trigger.
''ucUserNotification.xaml:''
''ucUserNotification.xaml.cs:''
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ezFIT.Tools.Controls
{
///============================================================================================
///
/// Interaction logic for ucUserNotification.xaml
///
///============================================================================================
public partial class ucUserNotification : UserControl
{
//-----------------------------------
// Fields
//-----------------------------------
private string _Message = "";
private string _Title = "";
//-----------------------------------
// Properties
//-----------------------------------
public string Message
{
get { return _Message; }
set { _Message = value;}
}
public string Title
{
get { return _Title; }
set { _Title = value; }
}
public bool IsNotificationAreaVisible { get; set; }
//-----------------------------------
// RoutedEvent: OnUserNotification
//-----------------------------------
#region RoutedEvent: OnUserNotification
public static readonly RoutedEvent OnUserNotificationRoutedEvent = EventManager.RegisterRoutedEvent(
"OnUserNotificationRoutedEvent", // Name of the custom routed event
RoutingStrategy.Bubble, // The routing strategy
//typeof(RoutedEventHandler), // Type of the event handler
typeof(UserNotificationRoutedEventHandler), // Type of the event handler
typeof(ucUserNotification)); // The type of the owner of this routed event
// Custom handler delegate (instead of RoutedEventHandler)
public delegate void UserNotificationRoutedEventHandler(object sender, UserNotificationRoutedEventArgs e);
// CLR accessor (property wrapper) for the routed event
//public event RoutedEventHandler OnUserNotification
//{
// add { AddHandler(OnUserNotificationRoutedEvent, value); }
// remove { RemoveHandler(OnUserNotificationRoutedEvent, value); }
//}
// CLR accessor (property wrapper) for the custom routed event
public event UserNotificationRoutedEventHandler OnUserNotification
{
add { AddHandler(OnUserNotificationRoutedEvent, value); }
remove { RemoveHandler(OnUserNotificationRoutedEvent, value); }
}
// Method to raise event
//private void RaiseOnUserNotificationRoutedEvent()
//{
// RoutedEventArgs newEventArgs = new RoutedEventArgs(ucEnvironmentManager.OnUserNotificationRoutedEvent);
// RaiseEvent(newEventArgs);
//}
private void RaiseOnUserNotificationRoutedEvent(string aMessage, string aTitle="", MessageBoxImage aMessageBoxImage=MessageBoxImage.None)
{
UserNotificationRoutedEventArgs newEventArgs = new UserNotificationRoutedEventArgs(ucUserNotification.OnUserNotificationRoutedEvent, aMessage, aTitle, aMessageBoxImage);
RaiseEvent(newEventArgs);
}
#endregion
///----------------------------------------------------------------------------------------
///
/// Constructor
///
///----------------------------------------------------------------------------------------
public ucUserNotification()
{
InitializeComponent();
RefreshUserNotificationVisibility();
}
///----------------------------------------------------------------------------------------
///
/// Refresh currently set visibility.
///
///----------------------------------------------------------------------------------------
private void RefreshUserNotificationVisibility()
{
if (IsNotificationAreaVisible)
{
txtUserNotification.Visibility = Visibility.Visible;
}
else
{
txtUserNotification.Visibility = Visibility.Collapsed;
}
}
///----------------------------------------------------------------------------------------
///
/// Send user notification (triggering event).
///
///
///----------------------------------------------------------------------------------------
public void SendUserNotification(string aMessage, string aTitle, MessageBoxImage aMessageBoxImage=MessageBoxImage.None)
{
_Message = aMessage;
_Title = aTitle;
RaiseOnUserNotificationRoutedEvent(aMessage, aTitle, aMessageBoxImage);
}
}
///============================================================================================
/// UserNotificationEventArgs: a custom event inherited from EventArgs.
///============================================================================================
public class UserNotificationRoutedEventArgs : RoutedEventArgs
{
private readonly string _Message;
private readonly string _Title;
private readonly MessageBoxImage _MessageBoxImage;
public string Message
{
get { return _Message; }
}
public string Title
{
get { return _Title; }
}
public MessageBoxImage MessageBoxImage
{
get { return _MessageBoxImage; }
}
///----------------------------------------------------------------------------------------
///
/// Constructor
///
///
///
///
///----------------------------------------------------------------------------------------
public UserNotificationRoutedEventArgs(RoutedEvent aRoutedEvent, string aMessage, string aTitle="",
MessageBoxImage aMessageBoxImage=MessageBoxImage.None) : base(aRoutedEvent)
{
this._Message = aMessage;
this._Title = aTitle;
this._MessageBoxImage = aMessageBoxImage;
}
}
}
UserControl to display user notification.
''ucUserNotificationArea.xaml:''
''ucUserNotificationArea.xaml.cs:''
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading; // DispatcherTimer
namespace ezFIT.Tools.Controls
{
///
/// Interaction logic for ucUserNotificationArea.xaml
///
public partial class ucUserNotificationArea : UserControl
{
//--------------------------------------------------------
// Fields
//--------------------------------------------------------
private string _Message = "";
private string _Title = "";
private double _MessageTimeSpanInSeconds = 10.0;
private MessageBoxImage _MessageBoxImage = MessageBoxImage.None;
DispatcherTimer _Timer = new DispatcherTimer();
//--------------------------------------------------------
// Properties
//--------------------------------------------------------
public string Text
{
get { return _Message; }
set { _Message = value; SetTextMessage(_Message); }
}
public string Message
{
get { return _Message; }
set { _Message = value; SetTextMessage(_Message); }
}
public string Title
{
get { return _Title; }
set { _Title = value; }
}
public double MessageTimeSpanInSeconds
{
get { return _MessageTimeSpanInSeconds; }
set { _MessageTimeSpanInSeconds = value; }
}
public MessageBoxImage MessageBoxImage
{
get { return _MessageBoxImage; }
set { _MessageBoxImage = value; }
}
///----------------------------------------------------------------------------------------
///
/// Constructor
///
///----------------------------------------------------------------------------------------
public ucUserNotificationArea()
{
InitializeComponent();
}
///----------------------------------------------------------------------------------------
///
/// Set text message to the specified time span.
/// Time span is specified in seconds (default is 10 secs). Set to 0.0 if permanent display is desired.
///
///
/// Time span in seconds (default is 10 secs)
///----------------------------------------------------------------------------------------
public void SetTextMessage(string aMessage, string aTitle="", double aTimeSpanInSeconds = 10.0,
MessageBoxImage aMessageBoxImage=MessageBoxImage.None)
{
txtMessage.Text = aMessage;
txtTitle.Text = aTitle;
MessageTimeSpanInSeconds = aTimeSpanInSeconds;
MessageBoxImage = aMessageBoxImage;
// Title visibility
if (aTitle.Trim() == string.Empty)
{
txtMessage.Visibility = Visibility.Collapsed;
}
else
{
txtMessage.Visibility = Visibility.Visible;
}
SetBorderColor();
// Make control visible for specified time (or permanent if time is 0.0)
this.Visibility = Visibility.Visible;
if (aTimeSpanInSeconds > 0.0)
{
ActivateTimer();
}
}
///----------------------------------------------------------------------------------------
///
/// Set border color depending on specified messagebox image.
///
///----------------------------------------------------------------------------------------
private void SetBorderColor()
{
// Border visibility
switch (MessageBoxImage)
{
case System.Windows.MessageBoxImage.Question:
case System.Windows.MessageBoxImage.Warning: //case System.Windows.MessageBoxImage.Exclamation:
brdUserNotificationArea.BorderBrush = Brushes.Yellow;
break;
case System.Windows.MessageBoxImage.Error: //case System.Windows.MessageBoxImage.Stop: //case System.Windows.MessageBoxImage.Hand:
brdUserNotificationArea.BorderBrush = Brushes.Crimson;
break;
default:
case System.Windows.MessageBoxImage.Information: //case System.Windows.MessageBoxImage.Asterisk:
case System.Windows.MessageBoxImage.None:
brdUserNotificationArea.BorderBrush = Brushes.LightGray; // Brushes.LimeGreen;
break;
}
}
///----------------------------------------------------------------------------------------
///
/// Activate timer to display notification message.
///
///----------------------------------------------------------------------------------------
private void ActivateTimer()
{
// using System.Windows.Threading;
//DispatcherTimer timer = new DispatcherTimer();
_Timer.Interval = TimeSpan.FromSeconds(_MessageTimeSpanInSeconds); // Default: 10 seconds
_Timer.Tick += new EventHandler(timer_Tick); // Subscribe to Tick event handler
_Timer.Start();
}
///----------------------------------------------------------------------------------------
///
/// Tick event handler for Timer.
///
///
///
///----------------------------------------------------------------------------------------
void timer_Tick(object sender, EventArgs e)
{
// Do something every tick
//((DispatcherTimer)sender).Stop(); // stop timer
//_Timer.Stop(); // stop timer
//this.Visibility = Visibility.Collapsed; // hide this usercontrol
HideNotification();
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
HideNotification();
}
private void HideNotification()
{
//((DispatcherTimer)sender).Stop(); // stop timer
_Timer.Stop(); // stop timer
this.Visibility = Visibility.Collapsed; // hide this usercontrol
}
}
}
UserControl placed in control that triggers a message:
UserControl placed in parent control of the control that triggers a message. This is where the notification will be displayed:
Method to display message:
///----------------------------------------------------------------------------------------
///
/// Loaded event handler for user control.
///
///
///
///----------------------------------------------------------------------------------------
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
. . .
// Subscribe to Events
AddHandler(ucUserNotification.OnUserNotificationRoutedEvent, new ucUserNotification.UserNotificationRoutedEventHandler(UserNotification));
}
///----------------------------------------------------------------------------------------
///
/// Set user notification in the display area.
///
///
///
///----------------------------------------------------------------------------------------
private void UserNotification(object source, UserNotificationRoutedEventArgs args)
{
// Display message for 4 sec
ctrlUserNotificationArea.SetTextMessage(args.Message, args.Title, 4 /* secs */, args.MessageBoxImage);
}
== Resources ==
* [[http://msdn.microsoft.com/en-us/library/system.eventargs.aspx|MSDN EventArgs Class]]
* [[http://msdn.microsoft.com/en-us/library/ms752288.aspx|MSDN How to: Create a Custom Routed Event]]