Page 1 of 1

Basic WPF Databinding

#1 Curtis Rutland  Icon User is online

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 4309
  • View blog
  • Posts: 7,463
  • Joined: 08-June 10

Posted 12 October 2011 - 03:45 PM

*
POPULAR

Basic WPF Databinding

For reference: this tutorial started out as an idea from this thread.

So, it's painfully easy to use Windows Forms or even WPF to create a GUI application. The problem is, most people tend to make "smart GUIs"; forms that are very tightly integrated with their data. This makes it easy to put together simple forms quickly, but has the negative effect of making it quite difficult to use or move the data somewhere else in your program.

So we'll look at a more "correct" way to do this in WPF. Strictly speaking, the most correct pattern is "MVVM", whcih stands for Model-View-ViewModel. I'm not going to focus on pure MVVM at the moment. Our model will also act as our View Model, for instance.

Why is this important to learn about?

Well, unless you only plan on making one-form applications for the rest of your career, it's a good idea to learn the best-practices way to make your data portable and your UIs clean and loosely bound.

Definitions of terms used.
"MVVM": Model-View-ViewModel, a design pattern based on MVC (Model-View-Controller).

Note: All examples were created using Visual Studio 2010, targetting the .NET Framework 4.0 Client Profile. I'll do my best to point out anything that might not work in older versions.

Also, you can find the full code for this solution on my github page:
https://github.com/c...aBindingExample

I suggest you do, or at least look at it online. The DreamInCode forum software is changing the casing of some of my code, such as "onpropertychanged" (it should be in PascalCase), as well as injecting some HTML inside the word "Expression", which shows up in Code tags.

Basic WPF Databinding

Concept
Well, one of the first things to understand about WPF databinding is that the goal is to remove as much code as possible from the control's .cs file. Believe it or not, we're not going to edit our Contact user control's .cs file at all. The idea is to create a "View Model" that contains easily-bindable data, and special commands that our controls can bind to, then provide an instance of that ViewModel to our UserControl as it's DataContext.

Speaking of DataContext, let's disciss this first. DataContext is a property of every control (in fact, every object that has FrameworkElement in its inheritence chain). It's purpose is to provide the base object to which the control binds to. It's the default root of every binding path, unless otherwise specified. So, when we want our control to bind to an object, we set that object to it's DataContext property.

Set Up
Create a new WPF Project. I'm calling mine "DataBindingExample", but you can call yours anything you want (as long as you know that the name becomes the default namespace for a project). Add two new folders to the project once it's created: "Models" and "Views". This is preference only, not necessary. The reason I do this is because it allows for easier organization, and it also adds another level to the namespace, which gives us logical organization as well as physical separation. Also, this allows us to name our ViewModel and our UserControl with the same name without conflict, since they belong to separate namespaces.

Design the ViewModel
Let's design our ViewModel class before anything else. In the Models folder, right click and "Add -> Class...". Call it "Contact". This will act as both our Model and our ViewModel. Remember, the Model is what we'd call a "Domain Model", in that it's a representation of some business object. The ViewModel is a class that makes it easy to bind a model's data to a view's controls. In this case, they'll be one and the same.

The first thing to do is to tell our ViewModel to implement INotifyPropertyChanged.

public class Contact : INotifyPropertyChanged { //...


This is an interface that allows bound controls to know when the property they're bound to are updated, and have the changes reflected in the UI. It's a very simple interface, all it consists of is one event (for more information on events, read this tutorial. For a more in-depth examination, read this tutorial). You'll need to add the following code to satisfy this interface's requirements:

public event PropertyChangedEventHandler PropertyChanged;


Now, we'll add a private helper method to allow us to easily invoke this event:

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


This basically checks to see if there are any subscribers to the event. If there are, it triggers it with the property name that changed.

Now, on to the data properties. This is the meat of the class! This is where all the important data that defines a class instance will live.

private string dicHandle;
public string DicHandle {
    get { return dicHandle; }
    set {
        if (dicHandle == value) return;
        dicHandle = value;
        onpropertychanged("DicHandle");
    }
}

private string email;
public string Email {
    get { return email; }
    set {
        if (email == value) return;
        email = value;
        onpropertychanged("Email");
    }
}

private string firstName;
public string FirstName {
    get { return firstName; }
    set {
        if (firstName == value) return;
        firstName = value;
        onpropertychanged("FirstName");
    }
}

private string lastName;
public string LastName {
    get { return lastName; }
    set {
        if (lastName == value) return;
        lastName = value;
        onpropertychanged("LastName");
    }
}

private string phoneNumber;
public string PhoneNumber {
    get { return phoneNumber; }
    set {
        if (phoneNumber == value) return;
        phoneNumber = value;
        onpropertychanged("PhoneNumber");
    }
}


What we've done here is define five simple string properties: "DicHandle", "FirstName", "LastName", "Email", and "PhoneNumber". The private lowerCase version is the field, and the public UpperCase is the Property. Inside the setter for each property, trigger the PropertyChangedEvent with the name of the Property, if the new value is different than the old one. Let's add one more override just for debugguing purposes.

public override string ToString() {
    return string.Format("{0} | {1} {2} | {3} | {4}", DicHandle, FirstName, LastName, Email, PhoneNumber);
}


Now, that's enough for binding! That's all we need for a complete ViewModel to be bound to a control. Let's make that control now.

Designing the User Control
Right click on the Views folder, and click "Add -> User Control", and name it "Contact".
note: this won't conflict with our ViewModel's type because by default, project folders add their name to the namespace for items created in them. So this Contact we've created is going to be DataBindingExample.Views.Contact, as compared to DataBindingExample.Models.Contact

Let's add some rows, columns, labels, and textboxes. The important parts for databinding are the TextBoxes, for this example. Here's the XAML:

<UserControl x:Class="DataBindingExample.Views.Contact"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="1.5*" />
        </Grid.ColumnDefinitions>
        
        <TextBlock Text="New Contact" Style="{StaticResource TitleText}"/>
        
        <TextBlock Text="DIC Handle" Style="{StaticResource LabelText}" Grid.Row="1" />
        <TextBlock Text="First Name" Style="{StaticResource LabelText}" Grid.Row="2" />
        <TextBlock Text="Last Name" Style="{StaticResource LabelText}" Grid.Row="3" />
        <TextBlock Text="Email" Style="{StaticResource LabelText}" Grid.Row="4" />
        <TextBlock Text="Phone Number" Style="{StaticResource LabelText}" Grid.Row="5" />
        
        <TextBox Style="{StaticResource EntryBox}" Grid.Row="1" Grid.Column="1" Text="{Binding Path=DicHandle, Mode=TwoWay}" />
        <TextBox Style="{StaticResource EntryBox}" Grid.Row="2" Grid.Column="1" Text="{Binding Path=FirstName, Mode=TwoWay}" />
        <TextBox Style="{StaticResource EntryBox}" Grid.Row="3" Grid.Column="1" Text="{Binding Path=LastName, Mode=TwoWay}" />
        <TextBox Style="{StaticResource EntryBox}" Grid.Row="4" Grid.Column="1" Text="{Binding Path=Email, Mode=TwoWay}" />
        <TextBox Style="{StaticResource EntryBox}" Grid.Row="5" Grid.Column="1" Text="{Binding Path=PhoneNumber, Mode=TwoWay}" />
    </Grid>
</UserControl>



note: I've included Styles for each of these elements. If you're interested in using them, paste the following spoiler's contents into the App.xaml file, between the Application.Resources tag. Otherwise, remove the Style attribute from each element.
Spoiler


The bindings are all defined on the TextBoxes. We put our binding values in the properties we want to bind to. In this case, we're only interested in binding text, so we place it in the Text property, but this can go in literally any property that's defined. We could be binding to it's Grid.Row, Grid.Column, anything it defines. It makes it quite easy to make dynamic UIs.

The binding syntax looks like this: {Binding Path=Path, Mode=TwoWay}. The path tells it which property to bind to, and the mode says we want changes to be reflected both ways (from the UI to the ViewModel, and from the ViewModel to the UI). There are other values that can be set, but these are the only ones we're interested in for this tutorial.

Remember that the root of the binding path is, by default, the control's DataContext property. In our case, we're going to make a new Contact model, and set it to the UserControl's DataContext from the Mainwindow.

Putting it Together
Now we need to use our ViewModel and our View. Let's open up Mainwindow.xaml. It should look like this by default:

<Window x:Class="DataBindingExample.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">
    <Grid>
        
    </Grid>
</Window>



First, we need to add the xmlns so the XAML parser knows where to find our control. Put this line in after the other xmlns lines:

xmlns:views="clr-namespace:DataBindingExample.Views"


This tells the parser that anything with the xml namespace "views" is found in the .NET namespace "DataBindingExample.Views".

Next, let's add an instance of our UserControl to the grid. Put this between the Grid tags:

<views:Contact x:Name="contactForm" />


The designer should now show our contact form filling the window. Let's go over to the Mainwindow.xaml.cs file. Change it to look like the following:

public MainWindow() {
    InitializeComponent();
    Loaded += MainWindowLoaded;
}

void MainWindowLoaded(object sender, RoutedEventArgs e) {
    contactForm.DataContext = new Contact();
}



This subscribes to the Loaded event of MainWindow and tells it to run the code in MainWindowLoaded when the form loads. Inside that method, all we do is create a new Contact and assign it to our User Control's DataContext property, as I mentioned earlier.

Now you can press F5 to run the application.

Attached Image

Of course, this is pretty boring. You can't see anything happening! Read more to see how to use Commands to actually do something with the data being stored in the Model.

Using Commands
Let's go back to our Models.Contact class. We need to make a few changes. First, let's add a simple validator property:

internal bool IsValid {
	get { //simple rules for now
		return !string.IsNullOrWhiteSpace(DicHandle);
	}
}



We could make this have more complicated vaildation rules, but for now, we'll say we just want to use DicHandle to make our model valid.

Next, we need something to hold our command, that our View can bind to:

public ICommand SubmitContact { get; private set; }


note: you'll need to add using System.Windows.Input; to the top of the file with the other using statements for this code to work.

Desinging our ICommand
Now, you may be asking what an ICommand is? It's an interface that allows bound UI controls to invoke actions. Instead of adding event handlers in our User Control to deal with button presses, we'll add an ICommand that the button can bind to.

Of course, we need to create a class that implements ICommand. Here it is:

public class SubmitContactCommand : ICommand {

    public event EventHandler CanExecuteChanged;
    private void OnCanExecuteChanged() {
        if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs());
    }

    private readonly Contact contact;
    private bool canExecute;

    public SubmitContactCommand(Contact contact) {
        this.contact = contact;
        this.contact.PropertyChanged += ContactPropertyChanged;
    }

    void ContactPropertyChanged(object sender, PropertyChangedEventArgs e) {
        if (canExecute == contact.IsValid) return;
        canExecute = contact.IsValid;
        OnCanExecuteChanged();
    }

    public void Execute(object parameter) {
        Debug.WriteLine("Contact Submitted: " + contact);
    }

    public bool CanExecute(object parameter) {
        return canExecute;
    }
}



I'll go through each of the methods and events one by one:

CanExecuteChanged: this event should be triggered when the state of the command changes in such a way that it can or can't be executed. For instance, if the model is invalid, we don't want to execute the command. So we'd change the CanExecute value, and we'd need to trigger the event to let other things to know that the event is now not able to be used.

OnCanExecuteChanged: this is a helper method to check our event for subscribers before triggering the event.

contact: this is a field holding an instance of a Contact.

canExecute: a field that holds our execution state.

SubmitContactCommand: this is our constructor. Note that it takes a Contact object as a parameter. In here, we assign it to our contact field, and subscribe to it's PropertyChanged event.

ContactPropertyChanged: this is the subscription to said event. This is triggered every time a property is changed. Here, we check to see if the contact is in a valid state, and set our canExecute value accordingly. If the value has changed, we trigger the CanExecuteChanged event.

Execute: this is the method that will be called when the command is invoked. In our case, all we're doing is writing to the Debug stream.

CanExecute: a getter style method that determines if the Command is able to be invoked.

One last thing we need to do to our User Control to make it work with a command: give it a constructor that instantiates a command! In our Contact.cs file:

public Contact() {
    SubmitContact = new SubmitContactCommand(this);
}


Remember, SubmitContact is our ICommand property that our button will bind to.

Updating the User Control to use the ICommand
Open Contact.xaml. There's only two small changes we have to make here. First, add another RowDefinition. Just copy one of the previous ones and paste it again. This will give us a total of seven rows.

Next, we'll add a Button:

<Button Content="Submit" Grid.Row="6" Grid.Column="1" Margin="5" Command="{Binding Path=SubmitContact}" />


Fairly simple. In this case, we're not binding to the button's displayable properties, we're binding to it's Command property. This command is triggered when the button is clicked. Remember, SubmitContact was the ICommand property in our ViewModel.

Now, run the application again.

Attached Image

See that the button is disabled? That's becase the CanExecute method is returning false, since our Model's in an invalid state (no DicHandle value). Let's fill it in:

Attached Image

Now the button is enabled. Click it. You don't see anything happen, but if you check the "Output" window, you'll see our command was executed (highlighted in red):

Attached Image

Now, instead of writing to the Debug stream, we could have inserted that record into a database, or serialized it and stored it to disk. We could have done any number of things. But the most important thing is that we didn't write a single line of code in our User Control's .cs file! Our data is completely decoupled from our UI, so if we felt like it, we could use a completely different UI to display this data, and we wouldn't have to change any code, just XAML.

In Conclusion
DataBinding's somewhat a foreign concept to many people. They're used to manually connecting data to UI in the UI's codebehind. Of course, this leads to very tight coupling, and a lot of unnecessary work when changes must be made. Now, I've just scratched the surface of binding. There are so many more features and abilities. I suggest you get a good WPF or Silverlight book and learn about them that way.

Thanks for reading, and I hope this has been enlightening. You can find the full code for this example on my Github page:
https://github.com/c...aBindingExample

Is This A Good Question/Topic? 7
  • +

Replies To: Basic WPF Databinding

#2 pcaddict  Icon User is offline

  • New D.I.C Head

Reputation: 6
  • View blog
  • Posts: 42
  • Joined: 11-February 09

Posted 13 October 2011 - 10:37 AM

Thanks Curtis, this really helps clear up some confusion with binding in WPF. The only question that remains unclear here (to me) is implementing ICommand and how it compares to using RoutedEvents as shown in this MSDN example. It looks like both implementations are capable of handling validation in similar ways. Does using RoutedEvents violate MVVM or introduce tight coupling?
Was This Post Helpful? 0
  • +
  • -

#3 Curtis Rutland  Icon User is online

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 4309
  • View blog
  • Posts: 7,463
  • Joined: 08-June 10

Posted 13 October 2011 - 12:00 PM

It looks like using the RoutedCommand involves adding code to your UI's codebehind. Which isn't necessarily a bad thing and doesn't necessarily violate MVVM, but I prefer keeping my UIs as dumb as possible, bound to models that are smart.

You can also look into Prism, which has it's own way of dealing with commands, and is a very good set of libraries to use.
Was This Post Helpful? 0
  • +
  • -

#4 eclipsed4utoo  Icon User is offline

  • Not Your Ordinary Programmer
  • member icon

Reputation: 1524
  • View blog
  • Posts: 5,957
  • Joined: 21-March 08

Posted 19 October 2011 - 07:03 AM

One thing to note: MVVM isn't "no code in the code-behind". It's simply a way to separate concerns. If you have things like animations that you need to run on a button click, that would go in the code-behind, not in the ViewModel.

UI specific code should be in the UI.
Was This Post Helpful? 0
  • +
  • -

#5 synlight  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 84
  • View blog
  • Posts: 557
  • Joined: 14-September 11

Posted 17 September 2013 - 10:55 AM

"The problem is, most people tend to make "smart GUIs"; forms that are very tightly integrated with their data. This makes it easy to put together simple forms quickly, but has the negative effect of making it quite difficult to use or move the data somewhere else in your program."

This is EXACTLY what I needed today. I'm working on a fairly simple program, and the deeper I got into it (onward from simple queries).. I realized I must be doing something wrong. Thanks for a great tutorial!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1