Archive

Posts Tagged ‘Data Binding’

Using Data Binding with Static Resources in WPF

June 16th, 2009 Pedro Sampaio 2 comments

If you’ve been using WPF for more than a couple of weeks, I’m sure you wrote code like this a lot of times: you define a resource and reference it with StaticResource.

<Window x:Class="PSampaio.StaticResourcesWithBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Width="400" Height="200">
	<Window.Resources>
		<SolidColorBrush x:Key="RedBrush" Color="#FFFF0000" />
	</Window.Resources>
	<Grid>
		<TextBlock Text="Hello World" FontSize="48" Foreground="{StaticResource RedBrush}" />
	</Grid>
</Window>

However, if you applications are getting increasingly more complex, then it can start to get more tricky. In this post, I’ll be showing how to use resources for which you don’t know in design time the key to use in xaml (maybe because you’re loading them dynamically), by binding them to view model properties (which, by the way, you should be using).

Setting the scene

Suppose you’re indeed loading the resources dynamically, like so:

<Application x:Class="PSampaio.StaticResourcesWithBinding.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Window1.xaml">
	<Application.Resources>
		<SolidColorBrush x:Key="RedBrush" Color="#FFFF0000" />
	</Application.Resources>
</Application>

Yes, yes, I know. You can reference that resource the usual way, but this is just sample code, so assume you can’t. We’re also using a view model that contains a property with the key for the resource you want to use (that maybe you got that after loading the resource dynamically). That model is being instanced from the window constructor. Again, let me stress that this is sample code that you need to adapt to fit your own circumstances.

public Window1()
{
	InitializeComponent();

	DataContext = new MyViewModel("RedBrush");
}
namespace PSampaio.StaticResourcesWithBinding
{
	public class MyViewModel
	{
		public string MyResourceKey { get; private set; }

		public MyViewModel(string myResourceKey)
		{
			MyResourceKey = myResourceKey;
		}
	}
}

Are you with me so far? Good. Lets move on!

The obvious solution

Your first reaction to this will be to actually use the binding in a StaticResource. You can go ahead and try it if you want to. You’ve get to something like this:

<Window x:Class="PSampaio.StaticResourcesWithBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Width="400" Height="200">
	<Grid>
		<TextBlock Text="Hello World" FontSize="48" Foreground="{StaticResource {Binding MyResourceKey}}" />
	</Grid>
</Window>

This code will compile just fine, but if you run it, you’ll get an exception. This is because the StaticResource key is only evaluated when it’s needed, and when it tries to provide a value, it can’t find a resource named ‘{System.Windows.Data.Binding}’. So we’ll have to find another way, since this clearly won’t work.

A better solution

The only place were we can make this work is the markup itself, so lets start from the top, with the syntax we want to use, and go from there all the way to the bottom. We want this to work:

<Window x:Class="PSampaio.StaticResourcesWithBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Width="400" Height="200">
	<Grid>
		<TextBlock Text="Hello World" FontSize="48" Foreground="{BindableStaticResource {Binding MyResourceKey}}" />
	</Grid>
</Window>

In order for this to work, we need to create a new markup extension, so lets do just that. Create a new class that derives from the original StaticResourceExtension, called BindableStaticResourceExtension. Notice that the suffix is optional in XAML, and that you’ll need to add the namespace prefix in the markup. Create it with 2 constructors, one empty, and one with the Binding. You should have this class for now.

using System.Windows;
using System.Windows.Data;

namespace PSampaio.StaticResourcesWithBinding
{
	public class BindableStaticResource : StaticResourceExtension
	{
		public Binding Binding { get; set; }

		public BindableStaticResource()
		{
		}

		public BindableStaticResource(Binding binding)
		{
			Binding = binding;
		}
	}
}

Now the tricky part, when and how do you get the actual resource key, and what exactly do you do with it. The ‘when’ part is the easy one: there’s one method you can override public override object ProvideValue(IServiceProvider serviceProvider) that the framework will call in order to get the value. In this method, you have to evaluate the binding and get its result (the ‘how’) and set it to the ResourceKey in the base StaticResourceExtension class (the ‘what to do’). After that, you can delegate the rest of the work to the base class and let it provide it’s value. I’ll first show the code, and then go through it.

private static readonly DependencyProperty DummyProperty;

static BindableStaticResource()
{
	DummyProperty = DependencyProperty.RegisterAttached("Dummy",
										     typeof (Object),
										     typeof (DependencyObject),
										     new UIPropertyMetadata(null));
}

public override object ProvideValue(IServiceProvider serviceProvider)
{
	var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
	var targetObject = (FrameworkElement)target.TargetObject;

	MyBinding.Source = targetObject.DataContext;
	var DummyDO = new DependencyObject();
	BindingOperations.SetBinding(DummyDO, DummyProperty, MyBinding);

	ResourceKey = DummyDO.GetValue(DummyProperty);

	return base.ProvideValue(serviceProvider);
}

It may take a little to fully understand what’s going on here, but it’s fairly easy (once you figure it out). You need to query the service provider for an IProvideValueTarget. The returned object has two properties: TargetObject and TargetProperty. This tells you which property of which object we’re evaluating the binding for. You set the source of the binding to the data context of the target object, meaning “look here for the path you want”. This is the only use you’ll have for the target object

You then set the binding on a dummy DependencyObject, with a dummy property. If you use TargetProperty here, the binding operation will try to convert the resulting value to the type of the property. In this case, it would try to convert the string “RedBrush” to an actual Brush object, as that’s the type of TargetProperty (Foreground is a Brush). After this, all you need to do is get the value from the dummy property.

Now that ResourceKey contains the resource key we need, just call base.ProvideValue(serviceProvider) and let StaticResourceExtension try and find a resource with that key. Hope this helps!

You can download the code from this link.

Shout it kick it on DotNetKicks.com

An Introduction to ObservableCollection in WPF

April 28th, 2009 Pedro Sampaio 5 comments

The ObservableCollection<T> is one of the most important features of WPF data binding. From the documentation:

Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.

Most people seem confused about this, so it’s worth a clear note. An ObservableCollection is just like a regular collection. If you check the documentation, you can see that ObservableCollection is defined as:

public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged

So you have access to all the members you would have in Collection<T>. Again, refer to the documentation for the list of all those members.

Notice that, like any collection that derives from Collection<T>, its methods (namelly Add and Remove) accept null parameters and do not throw an exception.

But the main feature of the ObservableCollection<T> are the events it raises when the items it contains change. By implementing the INotifyCollectionChanged and INotifyPropertyChanged interfaces, the collection has events for CollectionChanged and PropertyChanged. All these events are related. The first one is raised whenever something changed in the collection, be it Add, Remove, Move, etc. This also trigger the PropertyChanged event for the Items[] property. When you’re adding or removing items, PropertyChanged is also raised for the Count property.

But enough theory, here’s a small example of how to use it. First of all, we start with our domain model, the Person.

namespace PSampaio.ObservableCollectionSample
{
	public class Person
	{
		public string FirstName { get; set; }
		public string LastName { get; set; }
		public string FullName { get { return string.Format("{0} {1}", FirstName, LastName); } }
	}
}

Next, we have a ViewModel that uses this domain model.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;

namespace PSampaio.ObservableCollectionSample
{
	public class PickOneViewModel
	{
		readonly Random random = new Random();

		public List<Person> AvailablePeople { get; private set; }
		public ICommand PickOnecommand { get; private set; }
		public ObservableCollection<Person> SelectedPeople { get; private set; }

		public PickOneViewModel()
		{
			AvailablePeople = new List<Person>
			{
				new Person {FirstName = "John", LastName = "Doe"},
				new Person {FirstName = "Michael", LastName = "Jones"},
				new Person {FirstName = "Jane", LastName = "Smith"},
			};

			PickOnecommand = new DelegateCommand<object>(obj =>
			{
				int availablePersonIndex = random.Next(0, AvailablePeople.Count);
				SelectedPeople.Add(AvailablePeople[availablePersonIndex]);
			});

			SelectedPeople = new ObservableCollection<Person>();
		}
	}
}

This PickOneViewModel has three properties, that we will bind to in the Window XAML. AvailablePeople is just a list of the people available to be selected. It’s just a standard List because we do not want to update it, just use it as a source for SelectedPeople. PickOneCommand is a DelegateCommand. This delegate command was based on the CompositeWPF class with the same name, and lets the you create an ICommand that’s defined with delegates, instead of CommandBindings. Finally, SelectedPeople is an ObservableCollection that starts empty and where we will be adding the (random) person selected each time we execute PickOneCommand.

The UI for this sample is fairly simple. Just add the ViewModel as an ObjectDataProvider to the window resources and use it as a DataSource for the window contents.

<Window
	x:Class="PSampaio.ObservableCollectionSample.Window1"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:Sample="clr-namespace:PSampaio.ObservableCollectionSample"
	Title="Window1"
	Width="300"
	Height="300">

	<Window.Resources>
		<ObjectDataProvider
			x:Key="Viewmodel"
			ObjectType="{x:Type Sample:PickOneViewModel}"/>
	</Window.Resources>

	<DockPanel DataContext="{StaticResource Viewmodel}">
		<ListView DockPanel.Dock="Left" Width="130"
			ItemsSource="{Binding AvailablePeople}"
			DisplayMemberPath="FullName" />
		<ListView DockPanel.Dock="Right" Width="130"
			ItemsSource="{Binding SelectedPeople}"
			DisplayMemberPath="FullName" />
		<Button Width="20"
			Command="{Binding PickOnecommand}"
			Content=">>"/>
	</DockPanel>
</Window>

It really is that simple. What is happening is that, when you click on the button, PickOneCommand is executed, adding one random person to the SelectedPerson collection. When that occurs, the list raises a CollectionChanged event that WPF listens. By now, the data binding knows that, because the list has changed, that it needs to update its contents, which it does.

So now you know how to use ObservableCollection. Please keep in mind that you don’t have use a view model, although I would consider it a best practice, that allows you to effectively separate the content from the presentation. Also, you can replace DelegateCommand with your CommandBindings if you prefer. Again, this is a matter of preference, as I believe using it makes for cleaner code.

kick it on DotNetKicks.com

Categories: Development Tags: ,

Creating a Custom Observable Collection in WPF

April 13th, 2009 Pedro Sampaio 8 comments

One of the biggest advantages WPF has is its Data Binding features. Data Binding can be done in several ways, but the most common is using the INotifyPropertyChanged and INotifyCollectionChanged interfaces. For simple objects, all you need to do is implement the INotifyPropertyChanged, and raise the PropertyChanged event when your properties change. Maybe something like this:

public class MySampleClass : INotifyPropertyChanged
{
    public int MyProperty
    {
        get
        {
            return _myProperty;
        }
        set
        {
            if (_myProperty == value)
                return;

            _myProperty = value;
            OnPropertyChanged("MyProperty");
        }
    }

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

Instead of having each class implement the interface, you can create a base class that already does that, and inherit from this new one. You can see an example on this post. However, remember that you can only inherit from one class, so make sure you structure you class hierarchy properly. My advice is to use this only in presentation objects (or view models, if you’re using MVVM)

When you want to bind to a collection, you will want to use ObservableCollection<T>. This collection implements both interfaces, therefore notifying you when the items in the collection change and when the items’ properties change. This works great if you’re using standard collections. The problem arises when you want to use a custom collection. When you create an ObservableCollection<T> on v3.5, you have 3 constructors that rely on Collection<T> to create a new list. If you pass a List<T> or IEnumerable<T>, all that it does is copy the items, between both collections. This will of course make you custom collection useless. For this to work, you will have to create a new “Observable” wrapper around your custom collection. Lets start with the interface. All we need is a collection that implements the two interfaces we discussed earlier.

public interface ICustomObservableCollection<T> : ICollection<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
}

Before we move on the the implementation, first we need to better understand what is it that we’re trying to do and how to achieve it. We have a custom collection that we need to add additional features, so we need to “decorate” our collection with new behavior. For this we can follow the Decorator Pattern.

public class CustomObservableCollection<T> : ICustomObservableCollection<T>
{
    #region Fields
    ...
    #endregion

    #region Properties
    protected ICollection<T> InnerCollection { get; private set; }
    #endregion

    #region Constructors

    public CustomObservableCollection(ICollection<T> innerCollection)
    {
        if (innerCollection == null)
            throw new ArgumentNullException("innerCollection");

        InnerCollection = innerCollection;
    }

    #endregion

    #region Implementation

    #region Implementation of INotifyCollectionChanged
    ...
    #endregion

    #region Implementation of INotifyPropertyChanged
    ...
    #endregion

    #region Implementation of IEnumerable
    ...
    #endregion

    #region Implementation of ICollection<T>
    ...
    #endregion

    #endregion
}

Now that we have both collections wrapped on inside the other, all we need to do is provide the implementation of the interfaces. Starting with INotifyPropertyChanged:

#region Implementation of INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, e);
    }
}

private void OnPropertyChanged(string propertyName)
{
    OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}

#endregion

Then, we can implement INotifyCollectionChanged. For this implementation to work correctly, we need to add a monitor, so that we detect collection reentrancy. We can do this with this SimpleMonitor class, that implements IDisposable, and just keeps a counter of entries. We also need to provide methods tor activating the monitor.

#region SimpleMonitor
protected IDisposable BlockReentrancy()
{
    this._monitor.Enter();
    return this._monitor;
}

protected void CheckReentrancy()
{
    if ((this._monitor.Busy && (CollectionChanged != null)) && (CollectionChanged.GetInvocationList().Length > 1))
    {
        throw new InvalidOperationException("Collection Reentrancy Not Allowed");
    }
}

[Serializable]
private class SimpleMonitor : IDisposable
{
    private int _busyCount;

    public bool Busy
    {
        get { return this._busyCount > 0; }
    }

    public void Enter()
    {
        this._busyCount++;
    }

    #region Implementation of IDisposable

    public void Dispose()
    {
        this._busyCount--;
    }

    #endregion
}
#endregion

This SimpleMonitor is created in the CustomObservableCollection constructor, so we need to account for that too.

#region Fields
private readonly SimpleMonitor _monitor;
#endregion

#region Constructors

public CustomObservableCollection(ICollection innerCollection)
{
    this._monitor = new SimpleMonitor();

    if (innerCollection == null)
    {
        throw new ArgumentNullException("innerCollection");
    }

    InnerCollection = innerCollection;
}

#endregion

Now, we’re ready to provide an implementation for INotifyCollectionChanged.

#region Implementation of INotifyCollectionChanged

public event NotifyCollectionChangedEventHandler CollectionChanged;

protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
    if (CollectionChanged != null)
    {
        using (BlockReentrancy())
        {
            CollectionChanged(this, e);
        }
    }
}

private void OnCollectionChanged(NotifyCollectionChangedAction action, object item)
{
    OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item));
}

private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index)
{
    OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
}

private void OnCollectionReset()
{
    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}

#endregion

The next one is IEnumerable. For this one, we will delegate to the InnerCollection.

#region Implementation of IEnumerable

public IEnumerator GetEnumerator()
{
    return InnerCollection.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

#endregion

Finally, provide the implementation for ICollection<T>. Again, this will delegate to the InnerCollection raising events when needed. Notice the use of the CountString and IndexerName fields.

#region Fields

private const string CountString = "Count";
private const string IndexerName = "Item[]";

private readonly SimpleMonitor _monitor;

#endregion
#region Implementation of ICollection

public void Add(T item)
{
    CheckReentrancy();
    InnerCollection.Add(item);
    OnPropertyChanged(CountString);
    OnPropertyChanged(IndexerName);
    OnCollectionChanged(NotifyCollectionChangedAction.Add, item, InnerCollection.Count);
}

public void Clear()
{
    CheckReentrancy();
    InnerCollection.Clear();
    OnPropertyChanged(CountString);
    OnPropertyChanged(IndexerName);
    OnCollectionReset();
}

public bool Contains(T item)
{
    return InnerCollection.Contains(item);
}

public void CopyTo(T[] array, int arrayIndex)
{
    InnerCollection.CopyTo(array, arrayIndex);
}

public bool Remove(T item)
{
    CheckReentrancy();
    bool result = InnerCollection.Remove(item);
    OnPropertyChanged(CountString);
    OnPropertyChanged(IndexerName);
    OnCollectionChanged(NotifyCollectionChangedAction.Remove, item);
    return result;
}

public int Count
{
    get { return InnerCollection.Count; }
}

public bool IsReadOnly
{
    get { return InnerCollection.IsReadOnly; }
}

#endregion

You can download the source code here.

Update: Added the source code as a compressed file. You can get it here.

kick it on DotNetKicks.com