Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
swdev:dotnet:events [2012/11/01 15:14]
smayr [Example]
swdev:dotnet:events [2015/06/15 15:28] (current)
ajdavis [Routed Events]
Line 164: Line 164:
 == Routed Events == == 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.+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]] 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]]
Line 174: Line 174:
 Writing a custom routed event is similar to writing a dependency property, Writing a custom routed event is similar to writing a dependency property,
 <code csharp> <code csharp>
-public static readonly RoutedEvent MyRoutedEvent = EventManager.RegisterRoutedEvent( +public static readonly RoutedEvent OnMyActionEvent = EventManager.RegisterRoutedEvent( 
-                               "MyRoutedEvent",            // Name of the custom routed event+                               "OnMyAction",               // Name of the custom routed event
                                RoutingStrategy.Bubble,     // The routing strategy                                RoutingStrategy.Bubble,     // The routing strategy
                                typeof(RoutedEventHandler), // Type of the event handler                                typeof(RoutedEventHandler), // Type of the event handler
Line 181: Line 181:
  
 // Provide CLR property wrapper for the routed event // Provide CLR property wrapper for the routed event
-public event RoutedEventHandler MyEvent+public event RoutedEventHandler OnMyAction
 { {
-    add { AddHandler(MyRoutedEvent, value); } +    add { AddHandler(OnMyActionEvent , value); } 
-    remove { RemoveHandler(MyRoutedEvent, value); }+    remove { RemoveHandler(OnMyActionEvent , value); }
 } }
 +
 +// Method to raise event
 +public void RaiseMyActionEvent()
 +{
 +    RaiseEvent(new RoutedEventArgs(MyControl.OnMyActionEvent ));
 +}
 +
 </code>  </code> 
  
Line 219: Line 226:
 </code>     </code>    
  
-Raise Event: +
-<code csharp> +
-public class MyButton: Button +
-+
-  // Somewhere the event gets triggered +
-  ... +
-  // Raise the routed event "ButtonClick" +
-  RaiseEvent(new RoutedEventArgs(Button.Click, this)); +
-+
-</code>+
 === Example === === Example ===
 ''ucPopup.xaml'': ''ucPopup.xaml'':
Line 312: Line 310:
  
         // Attach menu event handler         // Attach menu event handler
-        popupmnu1.MenuItemSelected += new RoutedEventHandler(ucPopupMnu_MenuItemSelected);+        //popupmnu1.MenuItemSelected += new RoutedEventHandler(ucPopupMnu_MenuItemSelected); 
 +        // or 
 +        AddHandler(ucPopupMnu.MenuItemSelectedEvent, new RoutedEventHandler(ucPopupMnu_MenuItemSelected));
     }     }
  
Line 378: Line 378:
 } }
 </code> </code>
 +=== 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:''
 +<code xml>
 +<UserControl x:Class="ezFIT.Tools.Controls.ucUserNotification"
 +             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="30" d:DesignWidth="300">
 +    <Grid>
 +        <TextBlock Name="txtUserNotification" Text="Notification" VerticalAlignment="Center" HorizontalAlignment="Center"/>
 +    </Grid>
 +</UserControl>
 +</code>
 +
 +''ucUserNotification.xaml.cs:''
 +<code csharp>
 +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
 +{
 +    ///============================================================================================
 +    /// <summary>
 +    /// Interaction logic for ucUserNotification.xaml
 +    /// </summary>
 +    ///============================================================================================
 +    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
 +
 +        ///----------------------------------------------------------------------------------------
 +        /// <summary>
 +        /// Constructor
 +        /// </summary>
 +        ///----------------------------------------------------------------------------------------
 +        public ucUserNotification()
 +        {
 +            InitializeComponent();
 +
 +            RefreshUserNotificationVisibility();
 +        }
 +
 +        ///----------------------------------------------------------------------------------------
 +        /// <summary>
 +        /// Refresh currently set visibility.
 +        /// </summary>
 +        ///----------------------------------------------------------------------------------------
 +        private void RefreshUserNotificationVisibility()
 +        {
 +            if (IsNotificationAreaVisible)
 +            {
 +                txtUserNotification.Visibility = Visibility.Visible;
 +            }
 +            else
 +            {
 +                txtUserNotification.Visibility = Visibility.Collapsed;
 +            }
 +        }
 +
 +        ///----------------------------------------------------------------------------------------
 +        /// <summary>
 +        /// Send user notification (triggering event).
 +        /// </summary>
 +        /// <param name="aMessage"></param>
 +        ///----------------------------------------------------------------------------------------
 +        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; }
 +        } 
 +
 +        ///----------------------------------------------------------------------------------------
 +        /// <summary>
 +        /// Constructor
 +        /// </summary>
 +        /// <param name="aRoutedEvent"></param>
 +        /// <param name="aMessage"></param>
 +        /// <param name="aTitle"></param>
 +        ///----------------------------------------------------------------------------------------
 +        public UserNotificationRoutedEventArgs(RoutedEvent aRoutedEvent, string aMessage, string aTitle="", 
 +            MessageBoxImage aMessageBoxImage=MessageBoxImage.None) : base(aRoutedEvent)
 +        {
 +            this._Message         = aMessage;
 +            this._Title           = aTitle;
 +            this._MessageBoxImage = aMessageBoxImage;
 +        }
 +    }
 +}
 +</code>
 +
 +UserControl to display user notification.  
 +''ucUserNotificationArea.xaml:''
 +<code xml>
 +<UserControl x:Class="ezFIT.Tools.Controls.ucUserNotificationArea"
 +             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="75" d:DesignWidth="300">
 +    <Grid>
 +        <Border Name="brdUserNotificationArea" 
 +                Margin="5"
 +                HorizontalAlignment="Center" VerticalAlignment="Top"
 +                BorderBrush="LightGray" BorderThickness="2" CornerRadius="5"
 +                Background="LightGray" Canvas.ZIndex="99">
 +            <Border.Effect>
 +                <DropShadowEffect Opacity="0.5" ShadowDepth="5" BlurRadius="5" />
 +            </Border.Effect>
 +            <Border  
 +                Margin="5"
 +                HorizontalAlignment="Center" VerticalAlignment="Top"
 +                BorderBrush="White" BorderThickness="2" CornerRadius="5"
 +                Background="White" Canvas.ZIndex="99">
 +                <StackPanel>
 +                    <Grid>
 +                        <TextBlock Name="txtTitle" Text="Notification" FontWeight="Bold" Margin="5,5,15,0" VerticalAlignment="Center" Foreground="Black" />
 +                        <Button Name="btnClose" Width="10" Height="10" Margin="5,-10,0,0" HorizontalAlignment="Right" Style="{StaticResource IconButtonStyle}" Click="btnClose_Click">
 +                            <Path Data="{StaticResource CloseX}" Width="8" Height="8"
 +                                  Stretch="Fill" Stroke="DarkGray" StrokeThickness="2" Margin="0" 
 +                                  HorizontalAlignment="Right" VerticalAlignment="Top" />
 +                        </Button>
 +                    </Grid>
 +                    <TextBlock Name="txtMessage" Text="No news is good news!" Margin="5,0,5,5" VerticalAlignment="Center"/>
 +                </StackPanel>
 +            </Border>
 +        </Border>
 +    </Grid>
 +</UserControl>
 +</code>
 +
 +''ucUserNotificationArea.xaml.cs:''
 +<code csharp>
 +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
 +{
 +    /// <summary>
 +    /// Interaction logic for ucUserNotificationArea.xaml
 +    /// </summary>
 +    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; }
 +        }
 +
 +        ///----------------------------------------------------------------------------------------
 +        /// <summary>
 +        /// Constructor
 +        /// </summary>
 +        ///----------------------------------------------------------------------------------------
 +        public ucUserNotificationArea()
 +        {
 +            InitializeComponent();
 +        }
 +
 +        ///----------------------------------------------------------------------------------------
 +        /// <summary>
 +        /// 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.
 +        /// </summary>
 +        /// <param name="aMessage"></param>
 +        /// <param name="aTimeSpanInSeconds">Time span in seconds (default is 10 secs)</param>
 +        ///----------------------------------------------------------------------------------------
 +        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();
 +            }
 +        }
 +
 +        ///----------------------------------------------------------------------------------------
 +        /// <summary>
 +        /// Set border color depending on specified messagebox image.
 +        /// </summary>
 +        ///----------------------------------------------------------------------------------------
 +        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;
 +            }
 +        }
 +        
 +        ///----------------------------------------------------------------------------------------
 +        /// <summary>
 +        /// Activate timer to display notification message.
 +        /// </summary>
 +        ///----------------------------------------------------------------------------------------
 +        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();
 +        }
 + 
 +        ///----------------------------------------------------------------------------------------
 +        /// <summary>
 +        /// Tick event handler for Timer.
 +        /// </summary>
 +        /// <param name="sender"></param>
 +        /// <param name="e"></param>
 +        ///----------------------------------------------------------------------------------------
 +        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
 +        }    
 +    
 +    }
 +}
 +
 +</code>
 +
 +UserControl placed in control that triggers a message:
 +<code xml>
 +<toolscontrols:ucUserNotification x:Name="msgUserNotification" IsNotificationAreaVisible="False"/>
 +</code>
 +
 +UserControl placed in parent control of the control that triggers a message.  This is where the notification will be displayed:
 +<code xml>
 +<!--User Notification Area-->
 +<toolscontrols:ucUserNotificationArea x:Name="ctrlUserNotificationArea" />
 +</code>
 +
 +Method to display message:            
 +<code csharp>
 +///----------------------------------------------------------------------------------------
 +/// <summary>
 +/// Loaded event handler for user control.
 +/// </summary>
 +/// <param name="sender"></param>
 +/// <param name="e"></param>
 +///----------------------------------------------------------------------------------------
 +private void UserControl_Loaded(object sender, RoutedEventArgs e)
 +{
 +    . . .
 +
 +    // Subscribe to Events
 +    AddHandler(ucUserNotification.OnUserNotificationRoutedEvent, new ucUserNotification.UserNotificationRoutedEventHandler(UserNotification));
 +}
 +
 +///----------------------------------------------------------------------------------------
 +/// <summary>
 +/// Set user notification in the display area.
 +/// </summary>
 +/// <param name="source"></param>
 +/// <param name="args"></param>
 +///----------------------------------------------------------------------------------------
 +private void UserNotification(object source, UserNotificationRoutedEventArgs args)
 +{      
 +    // Display message for 4 sec
 +    ctrlUserNotificationArea.SetTextMessage(args.Message, args.Title, 4 /* secs */, args.MessageBoxImage);
 +}
 +</code>        
 == Resources == == Resources ==
   * [[http://msdn.microsoft.com/en-us/library/system.eventargs.aspx|MSDN EventArgs Class]]   * [[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]]   * [[http://msdn.microsoft.com/en-us/library/ms752288.aspx|MSDN How to: Create a Custom Routed Event]]