Monday, November 23, 2015

WPF - Dependency Properties

In WPF applications, dependency property is a specific type of property which extends the CLR property. It takes the advantage of specific functionalities available in the WPF property system.
A class which defines a dependency property must be inherited from theDependencyObject class. Many of the UI controls class which are used in XAML are derived from the DependencyObject class and they support dependency properties, e.g. Button class supports the IsMouseOverdependency property.
The following XAML code creates a button with some properties.
<Window x:Class = "WPFDependencyProperty.MainWindow" 
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" 
   xmlns:local = "clr-namespace:WPFDependencyProperty"
   Title = "MainWindow" Height = "350" Width = "604"> 
 
   <Grid> 
      <Button  Height = "40" Width = "175" Margin = "10" Content = "Dependency Property"> 
         <Button.Style> 
            <Style TargetType = "{x:Type Button}"> 
               <Style.Triggers> 
     
                  <Trigger Property = "IsMouseOver" Value = "True"> 
                     <Setter Property = "Foreground" Value = "Red" /> 
                  </Trigger>
      
               </Style.Triggers>
            </Style> 
         </Button.Style> 
      </Button> 
   </Grid> 
 
</Window> 
The x:Type markup extension in XAML has a similar functionality like typeof() in C#. It is used when attributes are specified which take the type of the object such as <Style TargetType = "{x:Type Button}">
When the above code is compiled and executed, you would get the followingMainWindow. When the mouse is over the button, it will change the foreground color of a button. When the mouse leaves the button, it changes back to its original color.
Dependency Property

Why We Need Dependency Properties

Dependency property gives you all kinds of benefits when you use it in your application. Dependency Property can used over a CLR property in the following scenarios −
  • If you want to set the style
  • If you want data binding
  • If you want to set with a resource (a static or a dynamic resource)
  • If you want to support animation
Basically, Dependency Properties offer a lot of functionalities that you won’t get by using a CLR property.
The main difference between dependency properties and other CLR properties are listed below −
  • CLR properties can directly read/write from the private member of a class by using getter and setter. In contrast, dependency properties are not stored in local object.
  • Dependency properties are stored in a dictionary of key/value pairs which is provided by the DependencyObject class. It also saves a lot of memory because it stores the property when changed. It can be bound in XAML as well.

Custom Dependency Properties

In .NET framework, custom dependency properties can also be defined. Follow the steps given below to define custom dependency property in C#.
  • Declare and register your dependency property with system call register.
  • Provide the setter and getter for the property.
  • Define a static handler which will handle any changes that occur globally
  • Define an instance handler which will handle any changes that occur to that particular instance.
The following C# code defines a dependency property to set the SetTextproperty of the user control.
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

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 WpfApplication3 { 
   /// <summary> 
      /// Interaction logic for UserControl1.xaml 
   /// </summary> 
 
   public partial class UserControl1 : UserControl { 
 
      public UserControl1() { 
         InitializeComponent(); 
      }
  
      public static readonly DependencyProperty SetTextProperty = 
         DependencyProperty.Register("SetText", typeof(string), typeof(UserControl1), new 
            PropertyMetadata("", new PropertyChangedCallback(OnSetTextChanged))); 
    
      public string SetText { 
         get { return (string)GetValue(SetTextProperty); } 
         set { SetValue(SetTextProperty, value); } 
      } 
  
      private static void OnSetTextChanged(DependencyObject d,
         DependencyPropertyChangedEventArgs e) { 
         UserControl1 UserControl1Control = d as UserControl1; 
         UserControl1Control.OnSetTextChanged(e); 
      } 
  
      private void OnSetTextChanged(DependencyPropertyChangedEventArgs e) { 
         tbTest.Text = e.NewValue.ToString(); 
      }  
   } 
}
Here is the XAML file in which the TextBlock is defined as a user control and the Text property will be assigned to it by the SetText dependency property.
The following XAML code creates a user control and initializes its SetTextdependency property.
<Window x:Class = "WpfApplication3.MainWindow" 
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" 
   xmlns:views = "clr-namespace:WpfApplication3"
   Title = "MainWindow" Height = "350" Width = "604"> 
 
   <Grid> 
      <views:UserControl1 SetText = "Hellow World"/> 
   </Grid> 
 
</Window> 
Let's run this application. You can immediately observe that in our MainWindow, the dependency property for user control has been successfully used as a Text.
Dependency property for user

WPF - Routed Events

routed event is a type of event that can invoke handlers on multiple listeners in an element tree rather than just the object that raised the event. It is basically a CLR event that is supported by an instance of the Routed Event class. It is registered with the WPF event system. RoutedEvents have three main routing strategies which are as follows −
  • Direct Event
  • Bubbling Event
  • Tunnel Event

Direct Event

A direct event is similar to events in Windows forums which are raised by the element in which the event is originated.
Unlike a standard CLR event, direct routed events support class handling and they can be used in Event Setters and Event Triggers within your style of your Custom Control.
A good example of a direct event would be the MouseEnter event.

Bubbling Event

A bubbling event begins with the element where the event is originated. Then it travels up the visual tree to the topmost element in the visual tree. So, in WPF, the topmost element is most likely a window.

Tunnel Event

Event handlers on the element tree root are invoked and then the event travels down the visual tree to all the children nodes until it reaches the element in which the event originated.
The difference between a bubbling and a tunneling event is that a tunneling event will always start with a preview.
In a WPF application, events are often implemented as a tunneling/bubbling pair. So, you'll have a preview MouseDown and then a MouseDown event.
Given below is a simple example of a Routed event in which a button and three text blocks are created with some properties and events.
<Window x:Class = "WPFRoutedEvents.MainWindow" 
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" 
   Title = "MainWindow" Height = "450" Width = "604" ButtonBase.Click  = "Window_Click" >
 
   <Grid> 
      <StackPanel Margin = "20" ButtonBase.Click = "StackPanel_Click">
  
         <StackPanel Margin = "10"> 
            <TextBlock Name = "txt1" FontSize = "18" Margin = "5" Text = "This is a TextBlock 1" /> 
            <TextBlock Name = "txt2" FontSize = "18" Margin = "5" Text = "This is a TextBlock 2" /> 
            <TextBlock Name = "txt3" FontSize = "18" Margin = "5" Text = "This is a TextBlock 3" /> 
         </StackPanel> 
   
         <Button Margin = "10" Content = "Click me" Click = "Button_Click" Width = "80"/> 
      </StackPanel> 
   </Grid> 
 
</Window>
Here is the C# code for the Click events implementation for Button, StackPanel, and Window.
using System.Windows; 
 
namespace WPFRoutedEvents { 
   /// <summary> 
      /// Interaction logic for MainWindow.xaml 
   /// </summary>
 
   public partial class MainWindow : Window { 
 
      public MainWindow() { 
         InitializeComponent(); 
      }  
  
      private void Button_Click(object sender, RoutedEventArgs e) { 
         txt1.Text = "Button is Clicked"; 
      } 
  
      private void StackPanel_Click(object sender, RoutedEventArgs e) { 
         txt2.Text = "Click event is bubbled to Stack Panel"; 
      } 
  
      private void Window_Click(object sender, RoutedEventArgs e) { 
         txt3.Text = "Click event is bubbled to Window"; 
      }
  
   } 
}
When you compile and execute the above code, it will produce the following window −
Routed Event
When you click on the button, the text blocks will get updated, as shown below.
Click on Button
If you want to stop the routed event at any particular level, then you will need to set the e.Handled = true;
Let’s change the StackPanel_Click event as shown below −
private void StackPanel_Click(object sender, RoutedEventArgs e) { 
   txt2.Text = "Click event is bubbled to Stack Panel"; 
   e.Handled = true; 
}
When you click on the button, you will observe that the click event will not be routed to the window and will stop at the stackpanel and the 3rd text block will not be updated.
click event

Custom Routed Events

In .NET framework, custom routed event can also be defined. You need to follow the steps given below to define a custom routed event in C#.
  • Declare and register your routed event with system call RegisterRoutedEvent.
  • Specify the Routing Strategy, i.e. Bubble, Tunnel, or Direct.
  • Provide the event handler.
Let’s take an example to understand more about custom routed events. Follow the steps given below −
  • Create a new WPF project with WPFCustomRoutedEvent
  • Right click on your solution and select Add > New Item...
  • The following dialog will open, now select Custom Control (WPF) and name it MyCustomControl.
Custom Routed Events
  • Click the Add button and you will see that two new files (Themes/Generic.xaml and MyCustomControl.cs) will be added in your solution.
The following XAML code sets the style for the custom control in Generic.xaml file.
<ResourceDictionary 
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" 
   xmlns:local = "clr-namespace:WPFCustomRoutedEvent">
 
   <Style TargetType = "{x:Type local:MyCustomControl}"> 
      <Setter Property = "Margin" Value = "50"/> 
      <Setter Property = "Template"> 
         <Setter.Value> 
            <ControlTemplate TargetType = "{x:Type local:MyCustomControl}">
    
               <Border Background = "{TemplateBinding Background}" 
                  BorderBrush = "{TemplateBinding BorderBrush}" 
                  BorderThickness = "{TemplateBinding BorderThickness}"> 
                  <Button x:Name = "PART_Button" Content = "Click Me" /> 
               </Border> 
     
            </ControlTemplate> 
         </Setter.Value> 
      </Setter> 
   </Style> 
 
</ResourceDictionary>
Given below is the C# code for the MyCustomControl class which inherits from the Control class in which a custom routed event Click is created for the custom control.
using System.Windows; 
using System.Windows.Controls;  

namespace WPFCustomRoutedEvent { 

   public class MyCustomControl : Control { 
 
      static MyCustomControl() { 
         DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), 
            new FrameworkPropertyMetadata(typeof(MyCustomControl))); 
      } 
  
      public override void OnApplyTemplate() { 
         base.OnApplyTemplate();
   
         //demo purpose only, check for previous instances and remove the handler first 
         var button  =  GetTemplateChild("PART_Button") as Button; 
         if (button ! =  null) 
         button.Click + =  Button_Click;  
      } 
  
      void Button_Click(object sender, RoutedEventArgs e) { 
         RaiseClickEvent(); 
      } 
  
      public static readonly RoutedEvent ClickEvent  =  
         EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, 
         typeof(RoutedEventHandler), typeof(MyCustomControl)); 
   
      public event RoutedEventHandler Click { 
         add { AddHandler(ClickEvent, value); } 
         remove { RemoveHandler(ClickEvent, value); } 
      } 
  
      protected virtual void RaiseClickEvent() { 
         RoutedEventArgs args = new RoutedEventArgs(MyCustomControl.ClickEvent); 
         RaiseEvent(args); 
      }
  
   } 
}
Here is the custom routed event implementation in C# which will display a message box when the user clicks it.
using System.Windows;  

namespace WPFCustomRoutedEvent { 
   // <summary> 
      // Interaction logic for MainWindow.xaml
   // </summary> 
 
   public partial class MainWindow : Window { 
 
      public MainWindow() { 
         InitializeComponent(); 
      }  
  
      private void MyCustomControl_Click(object sender, RoutedEventArgs e) { 
         MessageBox.Show("It is the custom routed event of your custom control"); 
      } 
  
   } 
}
Here is the implementation in MainWindow.xaml to add the custom control with a routed event Click.
<Window x:Class = "WPFCustomRoutedEvent.MainWindow" 
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" 
   xmlns:local = "clr-namespace:WPFCustomRoutedEvent"
   Title = "MainWindow" Height = "350" Width = "604"> 
 
   <Grid> 
      <local:MyCustomControl Click = "MyCustomControl_Click" /> 
   </Grid> 
 
</Window>
When the above code is compiled and executed, it will produce the following window which contains a custom control.
custom control
When you click on the custom control, it will produce the following message.
click on Custom control.jpg