Showing posts with label DataBnding. Show all posts
Showing posts with label DataBnding. Show all posts

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

Sunday, 3 March 2013

Part 3: Binding ListBox to XML

This post is the third in the series of ListBox control. Here is the reference of all the post in this series for quick reference.

Part - 1: ListBox basics - Adding Items manually to ListBox and understanding properties of ListBox

Part - 2: Data Binding in ListBox (Binding ListBox to data source)

Part - 3: Binding ListBox to XML

Part - 4: Applying style to ListBox

Part - 5: Applying style to ListBox, Continued

Part - 6: ListBox custom ControlTemplate

 

Part 3: Binding ListBox to XML

WPF provides very handy control “XmlDataProvider” that can be used to load XML from variety of sources (i.e provides xml data in XAML, separate XML file or set the XMLDocument programmatically).

In XAML below, we added XmlDataProvider to windows resources and provided xml data in XAML.

<XmlDataProvider x:Key="FruitsXML">
<x:XData>
<Fruits>
<Fruit Name="Apple">
<ImagePath>Images\\Apple.png</ImagePath>
<Calories>61</Calories>
<Vitamins>A,C</Vitamins>
</Fruit>
<Fruit Name="Orange">
<ImagePath>Images\\Orange.png</ImagePath>
<Calories>51</Calories>
<Vitamins>A,B1,C</Vitamins>
</Fruit>
</Fruits>
</x:XData>
</XmlDataProvider>


Binding ListBox to XmlDataProvider

<ListBox Name="lstFruits" ItemsSource="{Binding Source={StaticResource ResourceKey=FruitsXML}, XPath=Fruits/Fruit}" DisplayMemberPath="@Name" SelectedValuePath="ImagePath"></ListBox>


There are 4 important parts in binding:



Binding Source is FruitsXML which is a XmlDataProvider static resource defined in windows resources section.


XPath: Source is set to entire xml data. Hence we set XPath to “Fruits/Fruit” which binds ListBox to all Fruit elements.


DisplayMemberPath: Each item in ListBox is bound an instance of Fruit element from XML, so we need to choose which child element/attribute of Fruit element to display. We choose to display Name attribute. “@” symbol is used to indicate attribute.


SelectedValuePath: SelectedValuePath is set to “ImagePath” element of Fruit. Hence when we access “SelectedValue” property in code, it will return ImagePath of selected item.


Using XML File instead of static XML data


XmlDataProvider has Source property which can be used to specify path of XML file to load data. We can move XML data provided in XAML to separate XML file and then specify Source property of XmlDataProvider XML file. Remaining code would be same for binding ListBox.

Part - 2: Data Binding in ListBox

This post is the second in the series of ListBox control. In previous post, we learned some basic of ListBox control, adding items manually to ListBox control in XAML and code behind, and examining some properties of ListBox control.

Here is the reference of all the post in this series for quick reference.

Part - 1: ListBox basics - Adding Items manually to ListBox and understanding properties of ListBox

Part - 2: Data Binding in ListBox (Binding ListBox to data source)

Part - 3: Binding ListBox to XML

Part - 4: Applying style to ListBox

Part - 5: Applying style to ListBox, Continued

Part - 6: ListBox custom ControlTemplate

 

Part - 2: Data Binding in ListBox

In last post we learnt how to add static items to ListBox in XAML and code. But Most of the times we need to populate ListBox control by binding the control to some data source, instead of adding items manually. Data source can be xml, business object, DataTable and some other type that contains data.

Binding ListBox to Custom Business Object

Let’s create a simple class “Fruit” to hold fruit information.

public class Fruit
{
public string Name { get; set; }
public string ImagePath { get; set; }
public Int32 Calories { get; set; }
public string Vitamins { get; set; }
}


Add simple ListBox control to XAML

<ListBox Name="lstFruits"></ListBox>


In code behind (constructor), Create a list of Fruits and bind to ListBox control.

List<Fruit> myFruits = new List<Fruit>()
{
new Fruit() { Name = "Apple", ImagePath = "Images\\Apple.png", Calories = 61, Vitamins = "A,C" },
new Fruit() { Name = "Orange", ImagePath = "Images\\Orange.png", Calories = 51, Vitamins = "A,B1,C" },
new Fruit() { Name = "Grape", ImagePath = "Images\\Grapes.png", Calories = 40, Vitamins = "C" },
new Fruit() { Name = "Mango", ImagePath = "Images\\Mango.png", Calories = 80, Vitamins = "A,B1,C" }
};

lstFruits.ItemsSource = myFruits;


ItemSource property can be bound to any data source that implements IEnumerable. Generic List is one of them that implements IEnumerable.


Output:



In above image, it is visible that ListBox control is correctly bound to the data source as it shows 4 items in the list. Each item in ListBox is bound to an instance of Fruit class. As we have not specified any property of Fruit class, by default ToString() method of Fruit object is called which returns “ListboxSample.Fruit” value. i.e <Namespace.ClassName>


Modify XAML to bind to Name property by adding DisplayMemberPath

<ListBox Name="lstFruits" DisplayMemberPath="Name"></ListBox>

and here is the output



Now we examine the properties as we did in post 1.



One noticeable difference is that “SelectedItem” and “SelectedValue” property returns type “ListBoxSample.Fruit” instead of “ListBox.ListBoxItem” as each item is bound to instance of Fruit class. So to access properties we type cast to Fruit class.


Now let’s add one more property “SelectedValuePath”.

<ListBox Name="lstFruits" DisplayMemberPath="Name" SelectedValuePath="ImagePath"></ListBox>

Examine the difference.



lstFruits.SelectedValue now directly returns value of “ImagePath” property from Fruit class instead of an instance of Fruit class. Many times its quite useful to bind “SelectedValuePath” property to Unique Identifier (ID) of class so we can directly get Unique Identifier of selected item.


Some more fun with ListBox DataBinding.


Add Image control below ListBox control. Bind Image control Source property to SelectedValue property of ListBox control. This is called Element to Element binding.

<Image Grid.Row="1" Source="{Binding ElementName=lstFruits, Path=SelectedValue}" Stretch="Uniform"></Image>


Run the application and select items in ListBox.



As we have specified SelectedValuePath property of ListBox to ImagePath property of Fruit class, when any item is selected in ListBox control, SelectedValue property  of ListBox will return ImagePath of selected fruit item. Now we have bound Source property of Image control to SelectedValue property of ListBox control which would return ImagePath. Hence selecting fruit in ListBox will show image below.


ListBox also provides an alternative way of achieving same result.


IsSynchronizedWithCurrentItem


When ListBox control is bound to Data Source, internally an instance of ICollectionView is created depending on the type of of Data Source and then this view is bound to ListBox. If IsSynchronizedWithCurrentItem of ListBox is set to True, when user select any item in ListBox control, same item is set as current item in underlying view. This way if any other control (in our case Image control) is bound to any property of same data source, then it would reflect value from currently selected item in ListBox.


Let’s see what it means?


First of all we need to bind both ListBox control and Image control to same data source. Here DataContext property becomes more useful. DataContext property is present in every control and can be set to any data source. When DataContext is set in parent control, all child controls can inherit that data source from parent control. Hence we set DataContext property of entire Window.

List<Fruit> myFruits = new List<Fruit>()
{
new Fruit() { Name = "Apple", ImagePath = "Images\\Apple.png", Calories = 61, Vitamins = "A,C" },
new Fruit() { Name = "Orange", ImagePath = "Images\\Orange.png", Calories = 51, Vitamins = "A,B1,C" },
new Fruit() { Name = "Grape", ImagePath = "Images\\Grapes.png", Calories = 40, Vitamins = "C" },
new Fruit() { Name = "Mango", ImagePath = "Images\\Mango.png", Calories = 80, Vitamins = "A,B1,C" }
};

this.DataContext = myFruits;


DataContext of entire window is set to list of fruits. Hence ListBox control and Image control in Window would inherit data source from Window.

<ListBox Name="lstFruits" ItemsSource="{Binding}" DisplayMemberPath="Name" SelectedValuePath="ImagePath" IsSynchronizedWithCurrentItem="True"></ListBox>

<Image Grid.Row="1" Stretch="Uniform" Source="{Binding ImagePath}"></Image>


As ListBox inherits data source from parent, ItemSource for ListBox is set to empty Binding. IsSynchronizedWithCurrentItem = True will make sure that any item selected in ListBox will be the current item in ICollectionView.


Image control is bound to ImagePath property of Fruit class. hmm, that sounds interesting. how come any ContentControl like Image control can be bound to collection. That’s beauty of WPF DataBinding. When any property of ContentControl is bound to a property in list, current item (by default first item) in the list will be used for binding.


Now run the application and see the action.




When application is run, by default first item is the current item in ICollectionView. Hence “Apple” is selected in ListBox and Apple is displayed in Image control.


Now as we change the selection in ListBox, current item in ICollectionView is updated and hence image gets displayed for selected item.



This is very handy feature and useful in different scenarios.


I hope this would provide you a good insight into DataBinding.