Monday, 13 May 2013

Data Binding XAML control to property in code behind

Sample code this article

Binding to property defined in code behind

using System.Windows;

namespace WPFDataBindingCodeBehind
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private string _helloText = "Hello World";
public string HelloText
{
get { return _helloText; }
set { _helloText = value; }
}

public MainWindow()
{
InitializeComponent();
}
}
}

One important point to note in code behind above is that _helloText string has been assigned value at the time of Declaration. Hence when controls are loaded and binding happens, this value is already available and displayed. Below we would see what happens when we change value in Window Load event and how to handle that.


1. Binding using RelativeResource

<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=HelloText}"></TextBlock>

2. Binding using ElementName


Here we are using Element to Element Binding syntax. Name of Window is set to “myWindow”. Then Text property binds to “HelloText” property in element “myWindow”.

<Window x:Class="WPFDataBindingCodeBehind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="myWindow"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBlock Text="{Binding ElementName=myWindow, Path=HelloText}"></TextBlock>
</StackPanel>
</Window>

3. Binding using DataContext of Window set to Self


Here DataContext of entire window is set to code behind. All controls in this window would inherit this DataContext and hence their source for data binding would become code behind. So all controls can directly bind to properties.

<Window x:Class="WPFDataBindingCodeBehind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<StackPanel>
<TextBlock Text="{Binding Path=HelloText}"></TextBlock>
</StackPanel>
</Window>

4. Binding using DataContext of Parent element set to Window



Instead of setting DataContext of entire window to code behind, this syntax would set DataContext of parent StackPanel to code behind. Syntax for setting DataContext is similar to syntax is method 1.

<Window x:Class="WPFDataBindingCodeBehind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
<TextBlock Text="{Binding Path=HelloText}"></TextBlock>
</StackPanel>
</Window>

Important Note


Now, If we change the value in Window Load event then this changes would not be reflected. TextBlock will still display “Hello World” string. This is because when controls are loaded in InitializeComponent(), binding happens for first time and value is set to “Hello World”.

<Window x:Class="WPFDataBindingCodeBehind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
using System.Windows;
using System.ComponentModel;

namespace WPFDataBindingCodeBehind
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private string _helloText = "Hello World";
public string HelloText
{
get { return _helloText; }
set { _helloText = value; }
}

public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
HelloText = "Value Changed";
}

}
}

Implementing INotifyPropertyChanged


Once binding happens, any changes to the source value needs to be notified to the target control so that control can update the binding value. To achieve this, WPF provides INotifyPropertyChanged mechanism.

using System.Windows;
using System.ComponentModel;

namespace WPFDataBindingCodeBehind
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _helloText = "Hello World";
public string HelloText
{
get { return _helloText; }
set { _helloText = value; }
}

public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
HelloText = "Value Changed";
RaisePropertyChanged("HelloText");

}


public event PropertyChangedEventHandler PropertyChanged;

private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Binding Collections


Collection can be bound same way as property above. We have declared an ObservableCollection<string> named “myString” and instantiated at the time of declaration. This string is then bound to ListBox. ObservableCollection is a specialized type of List which provides built-in implementation of INotifyPropertyChanged and hence when any items are added/removed from the collection, same is notified to the target control.


XAML

<Window x:Class="WPFDataBindingCodeBehind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="myWindow"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<StackPanel>
<ListBox ItemsSource="{Binding ElementName=myWindow, Path=MyStrings}"></ListBox>
</StackPanel>
</Window>

Code Behind

using System.Windows;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace WPFDataBindingCodeBehind
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<string> myStrings = new ObservableCollection<string>();
public ObservableCollection<string> MyStrings
{
get { return myStrings; }
set { myStrings = value; }
}

public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyStrings.Add("Apple");
MyStrings.Add("Oranges");
MyStrings.Add("Mango");
MyStrings.Add("Banana");
MyStrings.Add("Watermelon");
}
}
}

Saturday, 4 May 2013

Markup Extension in WPF

Download Code – Sample containing all Markup Extensions

Markup Extension is very useful and handy feature in XAML.

Instead of directly specifying string values for properties in XAML, Markup extension can be used to provide values to properties that can be retrieved from different sources.

Let’s take a simple example of setting Background color of TextBlock.

Directly specify string value for Background property

<TextBlock Background="LightBlue"></TextBlock>

Using Markup Extension


We will use StaticResource Markup Extension to set Background property value. StaticResource means referring to a resource defined in XAML. So we define a resource for LightBlue color.

<Window.Resources>
<SolidColorBrush x:Key="LightBlueColor" Color="LightBlue"></SolidColorBrush>
</Window.Resources>

Now, Markup Extension can be set using Attribute syntax or Property Element Syntax.


Attribute syntax: This syntax is used in most of the cases when using Markup Extension. Syntax uses opening and closing curly braces ( { } ). Type of Markup Extension to be used is identified through string immediately after opening curly braces.

<TextBlock Background="{StaticResource ResourceKey=LightBlueColor}"></TextBlock>

Here, StaticResource string after opening curly braces indicates XAML parser to use StaticResource Markup Extension. StaticResource has a ResourceKey property which should be set to the key value of resource to be used. StaticResource extension would internally use this ResourceKey to lookup and return the resource.


Property Element syntax: Syntax is similar to using any other element using angle brackets ( <> )

<TextBlock>
<TextBlock.Background>
<StaticResource ResourceKey="LightBlueColor"></StaticResource>
</TextBlock.Background>
</TextBlock>

You might not get StaticResource in intellisense when using property Element syntax but it compiles and works fine.


Markup Extension is simple and easy to use but adds lots of power and flexibility to programming language. Once you start exploring different built-in Markup Extension provided in WPF you will start realizing that.


Types of Markup Extension


WPF provides several built-in Markup extensions which can be broadly classified into 2 categories.



XAML Defined - Markup Extension that are defined as an intrinsic feature of XAML as a language. Note: XAML language is also used for creating UI in other platforms like Silverlight, Windows Phone, Windows 8 etc. These extension are present in XAML namespace and accessed using default “x:” prefix.


WPF Specific - Markup Extension that are specific to WPF.


Below chart provides a quick overview of different Markup Extensions and their short description.


WPF MarkupExtension


I have added all Markup Extensions in sample attached at the top of this post. I have also posted code snippets in separate post here.


Here is a nice MSDN link which has links to different Markup Extension.


In next post, we will look at creating custom Markup Extension.

WPF Markup Extension Sample Code Snippets

In last post, we have seen an brief overview of Markup Extension and different types of Markup Extensions.

I thought it would be useful to create a sample that uses all Markup Extensions. You can download the sample: Download Code – Sample containing all Markup Extensions

Also I am posting code snippets for all Markup Extension for quick reference.

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 WPFMarkupExtension
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private string simpleText = "Text set using Binding Markup Extension";
public string SimpleText
{
get { return simpleText; }
set { simpleText = value; }
}

private string firstName = "Text set using";
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}

private string lastName = "MultiBinding Markup Extension";
public string LastName
{
get { return lastName; }
set { lastName = value; }
}

//private string bindingValue1 = "first binding";
//public string BindingValue1
//{
// get
// {
// throw new ApplicationException();
// return bindingValue1;
// }
//}

private string bindingValue2 = "Second binding returned value successfully";
public string BindingValue2
{
get { return bindingValue2; }
set { bindingValue2 = value; }
}

public static string StaticField = "Text set using Static Markup Extension";

public MainWindow()
{
InitializeComponent();
}
}
}

 

<Window x:Class="WPFMarkupExtension.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFMarkupExtension"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="600" Width="600">
<Window.Resources>
<SolidColorBrush x:Key="LightBlueColor" Color="LightBlue"></SolidColorBrush>
<local:MyConverter x:Key="myConverter"></local:MyConverter>

<!--Usage of x:Array Marukup Extension-->
<x:ArrayExtension x:Key="fruits" Type="sys:String">
<sys:String>Apple</sys:String>
<sys:String>Orage</sys:String>
<sys:String>Mango</sys:String>
<sys:String>Banana</sys:String>
</x:ArrayExtension>

<!--Usage of x:Type Maukup Extension-->
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="5"></Setter>
<Setter Property="Padding" Value="5"></Setter>
</Style>

<!--Usage of TemplateBinding Markup Extension-->
<ControlTemplate x:Key="EllipseButton" TargetType="{x:Type Button}">
<Grid>
<!--TemplateBinding is used to set property of control in ControlTemplate to property of
parent control to which this template is applied.-->
<!--Instead of specifying hard code value for Fill property, we inherit property from
parent control. This adds flexibility to template.-->
<Ellipse Fill="{TemplateBinding Background}"></Ellipse>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"></ContentPresenter>
</Grid>
</ControlTemplate>
</Window.Resources>
<StackPanel>

<GroupBox Header="XAML Defined">
<StackPanel>
<!--Usage of x:Null Markup Extension-->
<!--Style defined for TextBlock in Window.Resources section applies to all TextBlock.
We want this particular TextBlock not to use that style. So we set Style to Null-->
<TextBlock Style="{x:Null}" Text="Null Markup extension usage"></TextBlock>

<!--Usage of x:Static Markup Extension-->
<!--local refers to namespace of current project which is imported in Window element at top
MainWindow refers directly to Class (and not instance of Class)
StaticField is static property in MainWindow class-->
<TextBlock Text="{x:Static Member=local:MainWindow.StaticField}"></TextBlock>
</StackPanel>
</GroupBox>

<GroupBox Header="WPF Specific">
<!--Usage of RelativeResource Markup Extension-->
<!--DataContext for StackPanel is set to current window (means code behind).
Hence all controls in this StackPanel will inherit this DataContext as source for binding.-->
<StackPanel DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
<TextBlock Background="LightBlue"
Text="Background property set using direct string vlaue"></TextBlock>

<!--Usage of StaticResource Markup Extension (Attribute Syntax) -->
<TextBlock Background="{StaticResource ResourceKey=LightBlueColor}"
Text="Background property set using StaticResource Markup Extension - Attribute Syntax"></TextBlock>

<!--Usage of StaticResource Markup Extension (Property Element Syntax) -->
<TextBlock Text="Background property set using StaticResource Markup Extension - Property Element Syntax">
<TextBlock.Background>
<StaticResource ResourceKey="LightBlueColor"></StaticResource>
</TextBlock.Background>
</TextBlock>

<!--Usage of DynamicResource Markup Extension (Attribute Syntax) -->
<TextBlock Background="{DynamicResource ResourceKey=LightBlueColor}"
Text="Background property set using DynamicResource Markup Extension"></TextBlock>

<!--Usage of Binding Markup Extension (Attribute Syntax) -->
<TextBlock Text="{Binding SimpleText}"></TextBlock>

<!--Usage of Binding Markup Extension (Property Element Syntax) -->
<TextBlock>
<TextBlock.Text>
<Binding Path="SimpleText"></Binding>
</TextBlock.Text>
</TextBlock>

<!--Usage of MultiBinding Markup Extension-->
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<!--StringFormat is can be used for simple string concat/manipulation.
For complex requirements ValueConverter can be used as commented below-->
<!--<MultiBinding Converter="{StaticResource ResourceKey=myConverter}"
StringFormat="{}{0} {1}">-->
<Binding Path="FirstName"></Binding>
<Binding Path="LastName"></Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>

<!--Usage of StaticResource Markup Extension-->
<ListBox ItemsSource="{StaticResource ResourceKey=fruits}"></ListBox>

<!--Usage of PriorityBinding Markup Extension-->
<TextBlock HorizontalAlignment="Left">
<TextBlock.Text>
<!--PriorityBinding can be used for sharing same data template with multiple
controls or for showing progress in async operations.-->
<PriorityBinding>
<!-- // First Binding is not present or binding throws exception
or does not return some value then Second Binding is used -->
<Binding Path="BindingValue1"></Binding>
<Binding Path="BindingValue2"></Binding>
</PriorityBinding>
</TextBlock.Text>
</TextBlock>

<!--Usage of TemplateBinding Markup Extension-->
<!--See EllipseButton ControlTemplate-->
<Button Width="150" Height="75" Content="TemplateBinding usage" HorizontalAlignment="Left"
Background="LightBlue" Template="{StaticResource ResourceKey=EllipseButton}">
</Button>
</StackPanel>
</GroupBox>
</StackPanel>
</Window>