Introduction
Before this article, I have discussed about the architecture of WPF, Markup extensions, dependency properties, logical trees and Visual trees, layout, transformation, etc. Today, I will discuss what we call the most important part of any WPF application, the binding. WPF comes with superior DataBinding
capabilities which enables the user to bind objects so that whenever the other object changes, the main object reflects its changes. The main motive of DataBinding
is to ensure that the UI is always synchronized with the internal object structure automatically.
Before going further, let‘s jot down the things that we have already discussed.
DataBinding
was present before the introduction of WPF. In ASP.NET, we bind data elements to render proper data from the control. We generally pass in a DataTable
and bind the Templates to get data from individual DataRows
. On the other hand, in case of traditional windows forms application, we can also bind a property with a data element. The Bindings can be added to properties of objects to ensure whenever the property changes the value, the data is internally reflected to the data. So in one word, DataBinding
is nothing new to the system. The main objective of DataBinding
is to show data to the application and hence reduce the amount of work the application developer needs to write to just make the application properly display data. In this article, I will discuss how you could use the Databinding
in WPF application and also create a sample application to demonstrate the feature in depth.
Binding in WPF
WPF puts the concept of Binding
further and introduced new features, so that we could use the Binding feature extensively. Binding establishes the connection between the application and the business layers. If you want your application to follow strict design pattern rules, DataBinding
concept will help you to achieve that. We will look into greater detail with how to do that in a while.
In WPF, we can bind two Properties, one Property and one DependencyProperty
, two DependencyProperties
etc. WPF also supports Command
Binding. Let‘s discuss how to implement them in detail.
Binding can be classified into few Types.
DataBinding / Object Binding
The most important and primary binding is Databinding
. WPF introduces objects like ObjectDataProvider
and XMLDataProvider
to be declared into XAML to enhance the capability of object binding. DataBinding
can be achieved by several ways. As shown by Adnan in his blog [^], we can make use of Binding capabilities by employing either XAML, XAML and C#, and C# itself. So WPF is flexible enough to handle any situation.
<TextBox x:Name="txtName" />
<TextBlock Text="{Binding ElementName=txtName, Path=Text.Length}" />
In the above situation, I have shown the most basic usage of Binding. The Text
property of TextBlock
is bound with the TextBox txtName
so that whenever you enter something on the TextBox
during runtime, the TextBlock
will show the length of the string
.
As a Markup Extension binding is actually a Class with properties, here we specified the value of the property ElementName
and Path
. The ElementName
ensures the object that the property belongs to. Path
determines the property path which the object needs to look into.
You can use ObjectDataProvider
to handle data in your XAML easily. ObjectDataProvider
can be added as Resource and later on can be referenced using StaticResource
. Let‘s see the code below:
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<ObjectDataProvider ObjectType="{x:Type m:StringData}"
x:Key="objStrings" MethodName="GetStrings"/>
</StackPanel.Resources>
<ListBox Name="lstStrings" Width="200" Height="300"
ItemsSource="{Binding Source={StaticResource objStrings}}" />
Just as shown above, the ObjectType
will get a Type, which is the internal class structure for which the method GetStrings
will be called for. From the ListBox
, I have referenced the Object using StaticResource
. Now in the code, you can declare a class:
public class StringData
{
ObservableCollection<String> lst= new ObservableCollection<String>();
public StringData()
{
lst.Add("Abhishek");
lst.Add("Abhijit");
lst.Add("Kunal");
lst.Add("Sheo");
}
public ObservableCollection<String> GetStrings()
{
return lst;
}
}
So you can see the list has been populated with the string
s.
Why ObservableCollection , the INotifyPropertyChanged, INotifyCollectionChanged?
Now as you can see, I have used ObvervableCollection
. This is important. ObservableCollection
sends automatic notification when a new item is inserted. Thus notifies the ListBox
to update the list. So if you place a button,which inserts some data in the ObservableCollection
, the Binding will automatically be notified by the collection and hence update the collection automatically. You don‘t need to manually insert the same in the ListBox
.
WPF Binding generally needs to be notified when it is modified. The interfaces INotifyPropertyChanged
and INotifyCollectionChanged
are needed to update the UIElement
which is bound with the data. So if you are crating a property which needed to update the UI when the value of it is modified, the minimum requirement is to implement the same from INotifyPropertyChanged
, and for collection (like ItemsSource
), it needs to implement INotifyCollectionChanged
. ObservableCollection
itself implements INotifyCollectionChanged
, so it has support to update the control whenever new item is inserted to the list or any old item is removed from the string
.
I have already discussed the two in detail in an article : Change Notification for Objects and Collection [^].
On the contrary, Sacha has a good point of getting rid of INotifyPropertyChanged
interface using Aspect Examples (INotifyPropertyChanged via aspects).
XML Binding
Similar to Object binding, XAML also supports XML binding. You can bind the data coming from XMLDataProvider
easily using built in properties like XPath
in Binding
class definition. Let‘s look into the code:
<TextBlock Text="{Binding XPath=@description}"/>
<TextBlock Text="{Binding XPath=text()}"/>
So, if you are in the node XYZ
, the InnerText
can be fetched using text()
property. The @
sign is used for Attributes. So using XPath
, you can easily handle your XML.
If you want to read more about XML binding, check: XML Binding in WPF [^].
Importance of DataContext
You might wonder why I have taken context of DataContext
while I am talking about WPF Bindings. DataContext
is actually a Dependency property. It points to Raw Data such that the object that we pass as DataContext
will inherit to all its child controls. I mean to say if you define the DataContext
for a Grid
, then all the elements that are inside the Grid
will get the same DataContext
.
<Grid DataContext="{StaticResource dtItem}">
<TextBox Text="{Binding MyProperty}" />
</Grid>
Here as I defined DataContext
for the Grid
, the TextBox
inside the grid can refer to the property MyProperty
as the dtItem
object will be automatically inherited to all its child elements. While using Binding, DataContext
is the most important part which you must use.
Binding Members
As you all know about Markup Extensions, Binding is actually a Markup Extension. It is a class Binding
with few properties. Let‘s discuss about the Members that are there in Binding
:
- Source: The source property holds the
DataSource
. By default, it references theDataContext
of the control. If you place Source property for the Binding, it will take that in lieu of originalDataContext
element. - ElementName: In case of
Binding
with anotherElement
,ElementName
takes the name of theElement
defined within the XAML for reference of the object.ElementName
acts as a replacement toSource
. If path is not specified for the Binding, it will useToString
to get the data from the Object passed as Source. - Path:
Path
defines the actual property path to get the String Data. If the end product is not astring
, it will also invokeToString
to get the data. - Mode: It defines how the Data will be flown.
OneWay
means object will be updated only when source is updated, on the contraryOneWayToSource
is the reverse.TwoWay
defines the data to be flown in both ways. - UpdateSourceTrigger: This is another important part of any
Binding
. It defines when the source will be updated. The value ofUpdateSourceTrigger
can be :- PropertyChanged: It is the default value. As a result, whenever anything is updated in the control, the other bound element will reflect the same.
- LostFocus: It means whenever the property loses its focus, the property gets updated.
- Explicit: If you choose this option, you need to explicitly set when to update the Source. You need to use
UpdateSource
ofBindingExpression
to update the control.Hide Copy CodeBindingExpression bexp = mytextbox.GetBindingExpression(TextBox.TextProperty); bexp.UpdateSource();
By this, the source gets updated.
- Converter:
Converter
gives you an interface to put an object which will be invoked whenever the Binding objects get updated. Any object that implementsIValueConverter
can be used in place ofConverter
. You can read more about it from Converter in Binding [^]. - ConverterParameter: It is used in addition to
Converter
to send parameters toConverter
. - FallbackValue: Defines the value which will be placed whenever the
Binding
cannot return any value. By default, it is blank. - StringFormat: A formatting
string
that indicates theFormat
to which the data will follow. - ValidatesOnDataErrors: When specified, the
DataErrors
will be validated. You can useIDataErrorInfo
to run your custom Validation block when Data object is updated. You can read more aboutIDataErrorInfo
from : Validate your application using IDataErrorInfo [^].
Binding in Code-behind
Similar to what you might do with XAML, you can also define binding in the codeBehind. To do this, you need to use:
Binding myBinding = new Binding("DataObject");
myBinding.Source = myDataObject;
myTextBlock.SetBinding(TextBlock.TextProperty, myBinding);
You can also specify the Binding
properties in this way.
Command Binding
WPF supports CommandBinding
. Each command object like Button
exposes a property called Command
which takes an object that implements ICommand
interface and will execute the method Execute
whenever object command gets fired.
Say, you want your command to be executed whenever the window Inputs gets invoked:
<Window.InputBindings>
<KeyBinding Command="{Binding CreateNewStudent}" Key="N" Modifiers="Ctrl" />
<MouseBinding Command="{Binding CreateNewStudent}"
MouseAction="LeftDoubleClick" />
</Window.InputBindings>
In the above code, the CreateNewStudent
is a property that exposes the object which inherits ICommand
interface and the Execute
method will be invoked whenever the Key Ctrl + N or LeftDoubleClick
of the window is invoked.
Note: In VS 2008, the InputBindings
only take Static
Command objects. There is a bug report for this [^] , and it will be fixed in later releases.
You can use CommandParameter
to pass parameters to the methods that make up the ICommand
interface.
<Button Content="CreateNew" Command="{Binding CreateNewStudent}" />
Similar to InputBindings
, you can use the Command
with a Button
. To execute, you need to create an object that implements ICommand
like below:
public class CommandBase : ICommand
{
private Func<object, bool> _canExecute;
private Action<object> _executeAction;
private bool canExecuteCache;
public CommandBase(Action<object>executeAction, Func<object, bool> canExecute)
{
this._executeAction = executeAction;
this._canExecute = canExecute;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
bool tempCanExecute = _canExecute(parameter);
canExecuteCache = tempCanExecute;
return canExecuteCache;
}
private event EventHandler _canExecuteChanged;
public event EventHandler CanExecuteChanged
{
add { this._canExecuteChanged += value; }
remove { this._canExecuteChanged -= value; }
}
protected virtual void OnCanExecuteChanged()
{
if (this._canExecuteChanged != null)
this._canExecuteChanged(this, EventArgs.Empty);
}
public void Execute(object parameter)
{
_executeAction(parameter);
}
#endregion
}
I have used a CommandBase
class to make the objects look less clumsy. The actual object class looks like:
private CommandBase createNewstudent;
public CommandBase CreateNewStudent
{
get
{
this.createNewstudent = this.createNewstudent ??
new CommandBase(param => this.CreateStudent(), param => this.CanCreateStudent);
return this.createNewstudent;
}
}
private object CreateStudent()
{
this.CurrentStudent = new StudentItem();
return this.CurrentStudent;
}
public bool CanCreateStudent
{
get { return true; }
}
Thus, you can see the createNewCommand
passes CreateStudent
lamda expression which is called whenever the object gets updated. The CanCreateStudent
is a property that will also be called and based on true
or false
, WPF will allow the command to execute.
The PropertyBinding
and CommandBinding
give a total package to separate the presentation logic from the Presentation Layer. This gives the architecture to put all the logic separated. Microsoft created the whole Expression blend using MVVM pattern which separates the View from the ViewModel
and hence gives a chance to handle Unit Testing easily even for presentation layer. We will discuss more about the topic later on the series.
MultiBinding
Similar to single Binding
, WPF also introduces the concept of MultiBinding
. In case of MultiBinding
, the data bound depends on more than one source. You can specify more than one binding expression and on each of them the actual output is dependent on.
<TextBlock DockPanel.Dock="Top" >
<TextBlock.Text>
<MultiBinding Converter="{StaticResource mbindingconv}">
<Binding ElementName="lst" Path="Items.Count" />
<Binding ElementName="txtName" Path="Text" />
<Binding ElementName="txtAge" Path="Text" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Here, the value for TextBlock
is dependent on 3 elements, the first one is the ListBox
count, then txtName
and txtAge
. I have used Converter
to ensure we find all the individual elements in the IMultiValueConverter
block and handle each value separately. The IMultiValueConverter
just similar to IValueConverter
can take the value and return the object that is bound to the Text
property.
public class MyMultiBindingConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
string returnval = "Total no of Data {0}, NewData : ";
if (values.Count() <= 0) return string.Empty;
returnval = string.Format(returnval, values[0]);
for (int i = 1; i < values.Count(); i++)
returnval += "- " + values[i];
return returnval;
}
public object[] ConvertBack(object value, Type[]
targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
For simplicity, I have just concat each of the values that are passed and return back the output.
In the sample application, I have produced the most simple Binding to ensure everything comes from the Model. You can find the sample application from the link at the top of this article.
Conclusion
I think you must be enjoying the series. Also feel free to write your comments. Thanks for reading.