hendra.dev

A work in progress

Data Binding in .NET

Written on

I am now trying to use the concept of MVVM in my applications, as I believe a proper separation of concerns is very important in building an application, as from I personally experienced when developing in PHP with CodeIgniter, it really makes development much easier.

So, I think for an event driven application pattern such as WPF, Silverlight, and also WP7, (maybe Windows 8 as well), it seems pretty obvious to take advantage of the existing abstraction of the framework, so MVVM would go well with these kind of applications.

I can understand the concept of the MVVM pattern itself quite well, but I am not really able to grasp its implementation, I think it is because I don’t really understand the basic features of the framework that the pattern rely on. One of which is the Data Binding. So, here it is, my attempt to get better understanding on it.

:::xml
<TextBlock Text="{Binding Name}"></TextBlock>

while in the code behind,

:::csharp
namespace PhoneApp1
{
    public partial class MainPage : PhoneApplicationPage
    {
        public MyClass dataSource;
        public MainPage()
        {
            InitializeComponent();
            dataSource = new MyClass();
            dataSource.name = "Hello";
            this.DataContext = this.dataSource;
        }
    }

    public class MyClass
    {
        private string _name;
        public string name
        {
            get { return _name; }
            set { _name = value; }
        }
    }
}

There is one problem with this, when the value of the data source changes, the binding target on the UI won’t change along with it, so, to make the value of the binding target change along with the data source, we need to have our class implement INotifyPropertyChanged.

Let’s add a button that will change the value of the data source.

::xml
<TextBlock Text="{Binding name}"></TextBlock>
<Button Content="Button" Name="button1" Click="button1_Click"/>

And change the code behind to:

:::csharp
namespace PhoneApp1
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructor
        public MyClass dataSource;
        public int i = 0;
        public MainPage()
        {
            InitializeComponent();
            dataSource = new MyClass();
            dataSource.name = "Hello";
            this.DataContext = this.dataSource;
        }
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            i++;
            this.dataSource.name = i.ToString();
        }
    }

    public class MyClass : INotifyPropertyChanged
    {
        private string _name;
        public string name
        {
            get { return _name; }
            set
            {
                if (value != _name)
                {
                    _name = value;
                    Notify("name");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        void Notify(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Now, every time we click on the button, the value of the name property will change, and the TextBlock will change its content accordingly.

The INotifyPropertyChanged interface exposes the PropertyChanged event, where we would raise when the value of the data is changed, and there are the event handler present. One might ask, why? It is because when the class is being used as a data source in a binding, the binding target will subscribe to the event, and call the necessary method to update the view.

There are several modes of binding, the default is OneWay, which is a binding from the data source to the target, where any changes on the data source will be propagated to the target, but not vice versa, another mode is TwoWay, which is pretty much self descriptive, and also OneTime, which only set the data when the binding is created. So, if we add this textbox to the previous example,

:::xml
<TextBox Text="{Binding name,Mode=TwoWay}"/>

Whenever the value of the textbox is changed, the textblock that is bound to the same data source will update its value accordingly. Note that we would still need to implement INotifyPropertyChanged for the view to update as the value changes. We can specify what triggers the update back to the source via the UpdateSourceTrigger property. Its values depends on the dependency properties of the target, on the example textbox above, the default trigger is LostFocus, so the data source will be notified of the new value when the textbox has lost its focus. We can also trigger the update manually by setting the property to Explicit, which will only update the value when the application calls UpdateSource, e.g. when the user clicks on a button.

There are several ways to specify a binding source, the examples above specified the data source by setting the DataContext property of the current page from the code behind. The child elements within the page inherit the DataContext from its parent, we can see that this method is very convenient when we have a single data source that we use across the page. We can also set the DataContext property directly on the element directly, instead of inheriting it from its parent. To specify the data source for an element individually, one of the is by setting the Source property of the element, by setting the Source property, the data will be able to get the data from the source that it specify without depending on the data source of its parent element. Other options are RelativeSource, which is used to specify the data source relative to the target, and ElementName which is useful if you want to bind to another element in your view, e.g. binding the height of an element to a textbox value.

The examples above shows binding among data source and target of the same data type, e.g. string to string, but there are times where we may want to bind a source of a different data type, for example, binding the background colour of an element to an hexadecimal value, or displaying the same date in several different format. One way would be to create an additional property with a getter that gets its value from the source, but that isn’t a very good way, for one thing, two-way binding and property changed notification got more complicated than it should be. One solution is to use the ValueConverter, which is creating a custom class that implements the IValueConverter interface. I won’t talk about it too much in this post, maybe in some other post, you can read more about it on that link. .NET Framework also provide a number of converter that we can use.

So far, the examples shows only binding to a single data type, but we often need to bind to a collection, for example, binding a listbox to a list of person. To implement a collection, we can use one of the generic collection classes, such as, List<T>, Collection<T> and others. We can also implement a custom collection by implementing the IEnumerable interface, but to enable automatic UI update on change, we need a way to provide notification on data changes, just as we did with the single instance data source. To do that, the collection need to implement the INotifyPropertyChanged interface, but it is recommended to use the class provided by the .NET Framework which did the job for us, the ObservabeCollection<T> class. Let’s change the previous example to show a little bit of collection binding. Lets add a listbox to the our application.

:::xml
<ListBox Name="listBox1"ItemsSource="{Binding colls}"/>

Now, let’s change the MyClass a little bit, and change the previous button click handler to add more element to the collection.

:::csharp
namespace PhoneApp1
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructor
        public MyClass dataSource;
        public int i = 0;
        public MainPage()
        {
            InitializeComponent();
            dataSource = new MyClass();
            dataSource.name = "Hello";
            dataSource.colls = new ObservableCollection<string>() { "string 1", "string 2"};
            this.DataContext = this.dataSource;
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            i++;
            this.dataSource.name = i.ToString();
            dataSource.colls.Add(i.ToString());
        }
    }

    public class MyClass : INotifyPropertyChanged
    {
        private string _name;
        public string name
        {
            get { return _name; }
            set
            {
                if (value != _name)
                {
                    _name = value;
                    Notify("name");
                }
            }
        }

        public ObservableCollection<string> colls { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;
        void Notify(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Now, whenever we click the button, a new string will be added to the collection, and the listbox content will reflect the changes as well because of the INotifyCollectionChanged that is implemented by the ObservableCollection. Note that to detect when the value of individual element change, we still need to implement INotifyPropertyChanged.

Now, when we are working with a collection of data, often we need to filter, sort and group the data based on the current context of the application, to do that without changing the underlying data source, we can use collections view. I wouldn’t talk much about it in this post, but more information about it are available here and here

On the examples above, we declared our data bindings mostly with XAML, and that is indeed the most common usage of data binding, so let’s talk more about it. On our examples above, we declared a binding with the {Binding name}. This is actually a shortcut to declare the binding, we know that the child element will get its binding Source from its parents DataContext, and we can actually declare a path to specify which property of the source that we want to bind to. So, that declaration just now can be written as {Binding Source={StaticResource dataSource},Path=name}. We can use StaticResource to refer to an instance that we declare in the XAML, so to declare our dataSource in XAML, we can use something like this:

:::xml
<Page xmlns:c="clr-namespace:PhoneApp1">
<phone:PhoneApplicationPage.Resources>
  <c:MyClass x:Key="dataSource"/>
</phone:PhoneApplicationPage.Resources>

If the the dataContext itself is the data source that we want to bind to, we can just declare the binding with simply {Binding}.

References: