|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
Preface And ThanksI am a .NET programmer, but a busy one, I do VB .NET and C#, ASP .NET / Winforms / WPF / WCF Flash Silverlight the lot. Basically I keep my toe in. But when I started writing this article series I naturally chose my favourite language (which happens to be C#). I since got an email from an individual who requested that I publish this series with source code in VB .NET and C#. I simply stated I didn't have time. So this individual (Robert Ranck) volunteered to help out, and do the translation to VB .NET, based on my orginal C# projects So for that and the subsequent VB .NET projects that you will find here I ask you to thank Robert Ranck. Cheers Robert, your contributions will surely make this series more open to all .NET developers. And another thanks also goes out Karl Shifflett (AKA the blog/article machine, also known as the Molenator) for answering my dumb VB .NET questions. And I'd also like to mention that Karl has just started a more advanced series of WPF articles (which at present will be in VB.NET, but will hopefully appear in C# as well). Karls new series will be excellent and I urge you all to encourage Karl on this series. Its not easy obligating ones self to write an entire series in one language let alone 2. Karls 1st article is located right here, have a look for yourself. Personally I love it. IntroductionThis article is the 5th in my series of beginners articles for WPF. In this article we will discuss databinding. The proposed schedule for this series will still be roughly as follows:
In this article I'm aiming to cover, is a brief introduction into the following:
I will NOT be covering the following collection based binding areas, so if you want to know more about these you'll have to do a bit of extra research using the links provided (see i'm 1/2 ok, at least I researched the correct links for you) The General Idea Behind DatabindingDatabinding is actually not that new (ok how its done in WPF is new) but we have has binding in ASP .NET and Winforms for some time
now. WPF has borrowed from both of these to create a really really good binding framework. But what is this binding stuff, for those of you
that have never heard of it.
DataContextThere is one important thing to know before we get into the ins and outs of
Databinding, and that is the <Window.Resources>
<src:LeagueList x:Key="MyList" />
...
...
</Window.Resources>
...
...
<DockPanel DataContext="{Binding Source={StaticResource MyList}}">
You can also use code to set DataContext, simply by using a <someElement>.DataContext = <someValue> Another thing to note is that if some object that inherits from a parents <MenuItem Tag="{Binding}">
This means that the entire object that is used as its parents Basic Databinding ConceptsBefore we can proceed onto looking at the nitty griity of databinding there are several key areas which we need to cover first. Namely
Direction Of The Data FlowAs shown above in the the general idea behind databinding section, flow of binding could be 2 way. There are several possibilities that can be configured when using databinding. These are controlled using the Binding.Mode values:
Use the Binding.Mode property to specify the direction of the data flow. To detect source changes in one-way or two-way bindings, the source must implement a suitable property change notification mechanism such as INotifyPropertyChanged. For an example, see How to: Implement Property Change Notification. Change notification is such an important lesson to learn in databinding that we need to look at that right now. So lets have a look at an example of using this interface INotifyPropertyChanged. To support OneWay or TwoWay binding to enable your binding target properties to automatically reflect the dynamic changes of the binding source, your class needs to provide the proper property changed notifications, this is where INotifyPropertyChanged is used. using System.ComponentModel;
namespace SDKSample
{
// This class implements INotifyPropertyChanged
// to support one-way and two-way bindings
// (such that the UI element updates when the source
// has been changed dynamically)
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public Person()
{
}
public Person(string value)
{
this.name = value;
}
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("PersonName");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
And heres a VB .NET version Imports System.ComponentModel
' This class implements INotifyPropertyChanged
' to support one-way and two-way bindings
' (such that the UI element updates when the source
' has been changed dynamically)
Public Class Person
Implements INotifyPropertyChanged
Private personName As String
Sub New()
End Sub
Sub New(ByVal Name As String)
Me.personName = Name
End Sub
' Declare the event
Public Event PropertyChanged As PropertyChangedEventHandler
Implements INotifyPropertyChanged.PropertyChanged
Public Property Name() As String
Get
Return personName
End Get
Set(ByVal value As String)
personName = value
' Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Name")
End Set
End Property
' Create the OnPropertyChanged method to raise the event
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
End Class
To implement INotifyPropertyChanged you need to declare the PropertyChanged event and create the OnPropertyChanged method. Then for each property you want change notifications for, you call OnPropertyChanged whenever the property is updated. I can not tell you how important the INotifyPropertyChanged interface is, but believe me its very very important, and if you plan to use Binding in WPF, just get used to using the INotifyPropertyChanged interface. What Triggers Source UpdatesBindings that are TwoWay or OneWayToSource listen for changes in the target property and propagate them back to the source. This is known as updating the source. For example, you may edit the text of a TextBox to change the underlying source value. As described in the last section, the direction of the data flow is determined by the value of the Binding.Mode property of the binding. However, does your source value get updated while you are editing the text or after you finish editing the text and point your mouse away from the TextBox? The Binding.UpdateSourceTrigger property of the binding determines what triggers the update of the source. The options available are as follows : The following table provides an example scenario for each Binding.UpdateSourceTrigger value using the TextBox as an example:
Basic Databinding SyntaxThere are many properties that may be used within the Binding class, as such I will not have time to cover all of them, though I shall attempt to go through the most common buts of syntax. As most binding will usually be set in XAML I will be concentrating on the XAML syntax, though it should be noted that anything that can be done in XAML can also be done in C#/VB .NET code behind. Ok so lets have a look at the basic syntax (we will cover more advanced stuff in the sections below). The most basic form of binding if to create a binding that binds to a value of an existing element (this is covered in more detail below also) I just wanted to introduce the syntax and go back and show you how to do the Binding.Mode and Binding.UpdateSourceTrigger stuff first. So here is probably one of the simplist Bindings that you will see. This example has 2 buttons, the 1st button (btnSource) has a Yellow Background property. The 2nd button uses the 1st button (btnSource), as the source for a Binding where the 1st button (btnSource) Background value is being used to set the 2nd buttons Background. <Button x:Name="btnSource" Background="Yellow" Width="150" Height="30">Yellow BUtton</Button>
<Button Background="{Binding ElementName=btnSource, Path=Background}"
Width="150" Height="30">I am bound to be Yellow Background</Button>
So that'd fairly simple right. But I just wanted to go back and have a quick look at how we could also use the Binding.Mode and Binding.UpdateSourceTrigger properties within the binding sytnax. Well as it turns out, its fairly easy, we just add the extra property and its desired value into the binding expression such as
<TextBox x:Name="txtSource" Width="150" Height="30"/>
<TextBox Width="150" Height="30" Text="{Binding ElementName=txtSource,
Path=Text, Mode=TwoWay, UpdateSourceTrigger=LostFocus }"/>
An Important Note : Recall from part 2 that I mentioned that Binding was a markup MarkupExtension. As such the XAML parser knows how to treat the { } sections. But really this is just shorthand, that can (if you prefer) be expressed using the longer more verbose syntax as shown below. <Button Margin="10,0,0,0" Content="Im bound to btnSource, using long Binding syntax">
<Button.Background>
<Binding ElementName="btnSource" Path="Background"/>
</Button.Background>
</Button>
This is a decision you will have to make yourself, me personally I prefer the { } syntax, though you dont get any intellisense help within Visual Studio if you do use the {} syntax. Databinding To UI ElementsWhen you set out to set up a Binding there are several different things you need to consider
Once you know or have considered all this, its really as easy as ABC. As part of the demo solution, you will find a project entitled "BindingToUIElements" which when run, will look like the following:
This simple demo application shows 3 different bindings going on. Ill briefly discuss each of these now. 1. Simply Element Binding (Default Mode)This simple example uses the 1st buttons Background as the source value for the other 2 Buttons Background The code for which is as follows: <!-- Simple Element Binding-->
<Label Content="Simple Element Binding" Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10" Background="Gainsboro">
<Label Content="Simple Element Binding"/>
<Button x:Name="btnSource" Margin="10,0,0,0" Background="Pink" Content="Im btnSource"/>
<Button Margin="10,0,0,0" Background="{Binding ElementName=btnSource,
Path=Background}" Content="Im bound to btnSource"/>
<Button Margin="10,0,0,0" Content="Im bound to btnSource, using long Binding syntax">
<Button.Background>
<Binding ElementName="btnSource" Path="Background"/>
</Button.Background>
</Button>
</StackPanel>
2. More Elaborate Binding (Default Mode)This simple example uses the The code for which is as follows: <Label Content="More Elaborate Binding" Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10" Background="Gainsboro">
<Label Content="Choose a color"/>
<ComboBox Name="cmbColor" SelectedIndex="0">
<ComboBoxItem>Green</ComboBoxItem>
<ComboBoxItem>Blue</ComboBoxItem>
<ComboBoxItem>Red</ComboBoxItem>
</ComboBox>
<Button Margin="10,0,0,0" Background="{Binding ElementName=cmbColor,
Path=SelectedItem.Content}" Content="Im bound to btnSource"/>
</StackPanel>
3. TwoWay Binding using UpdateSourceTriggerThis simple example uses 2 The code for which is as follows: <!-- Using UpdateSourceTrigger/Mode-->
<Label Content="Using UpdateSourceTrigger/Mode" Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10" Background="Gainsboro">
<TextBlock TextWrapping="Wrap"
Text="This uses TwoWay Binding and UpdateSourceTrigger=PropertyChanged.
Type in one textbox then the other,and see them update each other" Width="400"/>
<TextBox x:Name="txtSource" Width="50" Height="25" Margin="5,0,0,0"/>
<TextBox Width="50" Height="25" Margin="5,0,0,0"
Text="{Binding ElementName=txtSource,
Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }"/>
</StackPanel>
Databinding To XmlXML is used a lot these days, both for configuration information and data exchange, and indeed these days even for UI design. Remember XAML is a XML derivative. But that's not all we can use XML data for. We can infact bind to XML data. This is fairly easy to do in XAML, we can either have the XAML hold the XML data (though this is probably not the norm) or use external XML files. Either way the normal proceeding is to use a As part of the demo solution, you will find a project entitled "BindingToXML" which when run, will look like the following:
The top 2 sections of this demo app using XAML held XML data, and the bottom 2 use an external XML file. XmlDataProvider Using XAML Held Data (inline XML data)It is entirely possible to hold all the XML data within the XAML file, and use this data as a source for Binding. Lets see an example of this <Window x:Class="BindingToXML.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
Title="Window1" Height="800" Width="800">
<Window.Resources>
<!-- inline XML data-->
<XmlDataProvider x:Key="xmlData" XPath="Films">
<x:XData>
<Films xmlns="">
<Film Type="Horror" Year="1987">
<Director>Sam Raimi</Director>
<Title>Evil Dead II</Title>
</Film>
<Film Type="Murder" Year="1991">
<Director>Jonathan Demme</Director>
<Title>Silence Of The Lambs</Title>
</Film>
<Film Type="Sc" Year="1979">
<Director>Ridley Scott</Director>
<Title>Alien</Title>
</Film>
</Films>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<ScrollViewer>
<StackPanel Orientation="Vertical">
<!-- Simple XPath Binding using inline XML data-->
<Label Content="Show all Films (using inline XML data)"
Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10"
Background="Gainsboro">
<ListBox ItemsSource="{Binding Source={StaticResource xmlData},
XPath=Film/Title}"/>
</StackPanel>
<!-- More Complicated XPath Binding using inline XML data-->
<Label Content="Show Only Films After 1991 (using inline XML data)"
Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10"
Background="Gainsboro">
<ListBox ItemsSource="{Binding Source={StaticResource xmlData},
XPath=*[@Year>\=1991]}"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Window>
It can be seen from this example, that we use an in line (in the XAML) xml
dataset, for the The 2nd XmlDataProvider Using External XML FileAs I say though its going to be more common to use external XML files with
the <XmlDataProvider x:Key="xmlDataSeperateFile" XPath="Resteraunts" Source="XMLFile1.xml">
</XmlDataProvider>
Using this arrangement is much the same as we saw before where we can use XPath to fetch all the nodes or use XPath to only match
those nodes where the attribute match the requirements. In this example the 2nd <ListBox ItemsSource="{Binding Source={StaticResource xmlDataSeperateFile}, XPath=*[@Type\=\'Mexican\']}"/>
And heres the full example <Window x:Class="BindingToXML.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
Title="Window1" Height="800" Width="800">
<Window.Resources>
<!-- external XML data-->
<XmlDataProvider x:Key="xmlDataSeperateFile" XPath="Resteraunts"
Source="XMLFile1.xml">
</XmlDataProvider>
</Window.Resources>
<ScrollViewer>
<StackPanel Orientation="Vertical">
<!-- Simple XPath Binding using seperate XML data-->
<Label Content="Show all Resteraunts (using seperate XML data)"
Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10"
Background="Gainsboro">
<ListBox ItemsSource="{Binding Source=
{StaticResource xmlDataSeperateFile},
XPath=Resteraunt/Name}"/>
</StackPanel>
<!-- More Complicated XPath Binding using seperate XML data-->
<Label Content="Show Only Mexican Resteraunts (using inline XML data)"
Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10"
Background="Gainsboro">
<ListBox ItemsSource="{Binding Source={StaticResource xmlDataSeperateFile},
XPath=*[@Type\=\'Mexican\']}"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Window>
And heres the associated XML file (should you be curious about its structure) <?xml version="1.0" encoding="utf-8" ?>
<Resteraunts xmlns="">
<Resteraunt Type="Meat">
<Name>The MeatHouse</Name>
<Phone>01237 78516</Phone>
<email>yada@here.com</email>
</Resteraunt>
<Resteraunt Type="Veggie">
<Name>VegHead</Name>
<Phone>99999</Phone>
<email>mmm-vegies@here.com</email>
</Resteraunt>
<Resteraunt Type="Mexican">
<Name>Mexican't (El Mariachi)</Name>
<Phone>464654654</Phone>
<email>mex@here.com</email>
</Resteraunt>
</Resteraunts>
NOTE : Using XML to provide Binding values, is fine, but dont expect that you will
be able to update XML simply by using a Binding To XLINQAlthough I'm not going to go into this, Beatriz The Binding Queen Costa has a good blog entry right here if you are curious Databinding To CollectionsTypically WPF development will more than likely involve binding to an entire collection at some point. Now this is very very easy to do in WPF. As with most things there is many ways to do this, i'll outline 2 possible way, but of course there will be more. I just like these ways thats all. One important thing to ALWAYS keep in mind is change notification, recall we addressed that for individual classes by using the INotifyPropertyChanged interface. But what about Collections that will hold objects, what should we do about that? Well as luck would have it, these days there is a nice As part of the demo solution, you will find a project entitled "BindingToCollections" which when run, will look like the following:
So binding to such a collection becomes a snap. Heres 2 possible ways to do
this with using Binding To Collections In Code BehindThe 1st 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.Collections.ObjectModel;
namespace BindingToCollections
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
private People people = new People();
public Window1()
{
InitializeComponent();
people.Add(new Person { PersonName = "Judge Mental" });
people.Add(new Person { PersonName = "Office Rocker" });
people.Add(new Person { PersonName = "Sir Real" });
this.lstBox1.ItemsSource = people;
}
}
}
And in VB .NET Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Data
Imports System.Windows.Documents
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Media.Imaging
Imports System.Windows.Navigation
Imports System.Windows.Shapes
Imports System.Collections.ObjectModel
''' <summary>
''' Interaction logic for Window1.xaml
''' </summary>
Partial Public Class Window1
Inherits Window
Private people As New People()
Public Sub New()
InitializeComponent()
Dim _Person As New Person
_Person.PersonName = "Judge Mental"
people.Add(_Person)
_Person.PersonName = "Office Rocker"
people.Add(_Person)
_Person.PersonName = "Sir Real"
people.Add(_Person)
lstBox1.ItemsSource = people
End Sub
End Class
And the matching XAML for this Binding is as follows <Window x:Class="BindingToCollections.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindingToCollections"
WindowStartupLocation="CenterScreen"
Title="Window1" Height="800" Width="800">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<!-- ListBox Source Set In Code Behind-->
<StackPanel Orientation="Vertical">
<Label Content="ListBox Source Set In Code Behind"
Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<ListBox x:Name="lstBox1"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Window>
Binding To Collections In XAMLThe 2nd <Window x:Class="BindingToCollections.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindingToCollections"
WindowStartupLocation="CenterScreen"
Title="Window1" Height="800" Width="800">
<Window.Resources>
<local:People x:Key="people">
<local:Person PersonName="Freddy Star"/>
<local:Person PersonName="Stick Head"/>
</local:People>
</Window.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<!-- ListBox Source Set By Using Resources -->
<StackPanel Orientation="Vertical">
<Label Content="ListBox Source Set By Using Resources"
Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<ListBox x:Name="lstBox2"
ItemsSource="{Binding Source={StaticResource people}}"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Window>
You can see that we have an instance of the I should point out that these examples are merely demonstrating how to Bind to collections. Probably in a production system, the collection would be part of a BAL layer/ or maybe part of a model within a MVC/MVVM pattern. Im going for quick an dirty to get the point across. One other thing that I would like to bring your attention to is the rendering
of the items within the
This is obviously not the desired effect, but happens because WPF does not
know what properties to show and how they should be shown for a NOTE : As I stated earlier I did not cover grouping/sorting/filtering etc etc, and if you really want to know more about these, please use the links provided at the start of this article. Databinding Value ConvertorsImagine a situation where we have a bound data value that we wish to format in some way, say by using a short date format instead of a long date fomat. Up until now we have simply used the raw Binding value. So this wouldnt be possible, we would have to make sure the bound value had what we want to display. Luckily WPF has a trick up its sleeve, we can use a class that implements the IValueConverter interface to provide a new value for the binding. ValueConverters are like the sprintf of the WPF world. You can use a ValueConverter to literally provide a new object to a Binding. This may be an object of the same type as the orginal object, or could be a totally new object. For example in WPF there is a principle of a Freezable Object which is an imutable object. If you try and animate a Frozen object, you will have problems. I have in the past circumnaviagted this issue with using a Clone ValueConverter to provide a cloned object to a Binding which is then used in an animation. But more typically you may use ValueConverters for small formatting changes.
ValueConverters sit between the source value and the bound target property, and their sole purpose is so take a value and supply a different value. The IValueConverter interface contains the following methods that must be implemented. object Convert(object value, Type targetType, object parameter, CultureInfo culture) Which is used to convert from the source object into the new value which will be used by the target object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) Which is used to convert from the target object back to the source. This will be used where a Bindings Binding.Mode has been set to TwoWay or OneWayToSource. More often than not this will not be used and will simply throw an exception. So how do we use of of these ValueConverters in our code. Well quite simply
we use the normal Bindings expression but we also state which converter to use
for the As part of the demo solution, you will find a project entitled "ValueConverters" which when run, will look like the following:
This small example actually uses 2 ValueConverters, the top one converts from
words to a First the XAML <Window x:Class="ValueConverters.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ValueConverters"
WindowStartupLocation="CenterScreen"
Title="Window1" Height="800" Width="800">
<Window.Resources>
<local:WordToColorConverter x:Key="wordToColorConv"/>
<local:DateConverter x:Key="dateConv"/>
</Window.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<!-- Using Vaue Converter To Convert Text Into Colors -->
<Label Content="Using Vaue Converter To Convert Text To Fill Color"
Margin="5,0,0,0" FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10" Background="Gainsboro">
<TextBlock TextWrapping="Wrap" Text="Using Vaue Converter. Type 'hot' or 'cold'
into the textbox and watch the rectangle change color" Width="400"/>
<TextBox x:Name="txtSource" Width="50" Height="25" Margin="5,0,0,0"/>
<Rectangle Width="50" Height="25" Margin | ||||||||||||||||||||||||||||||