Quantcast
Channel: MSDN Blogs
Viewing all articles
Browse latest Browse all 35736

How to make a library portable and data binding friendly at the same time?

$
0
0

Here is the challenge. 

  1. Create a portable library that could be used by almost anything including Console Apps, Windows Phone, ASP.NET, Xbox 360
  2. Make it data binding friendly for targets like WPF, Silverlight and Windows 8 XAML Metro Apps by supporting property and collection changed notifications.
  3. Make it efficient so that users who don’t need data binding are not penalized with a performance hit and having to reference libraries they don’t need.
  4. Write Unit Tests to demonstrate that the solution works for both data binding and non-data binding users.

I was a little stumped so I decided to ask about this on StackOverflow see C# Class library collections ObservableCollection T vs Collection T and based on what the community came up with, here is my solution – can you do better?

Download Sample Code

The Base Class: Foo

If you don’t want data binding, Foo is for you.  It is a simple class.  It has a property “Num” and a collection “Strings” with a method called Populate().  You’ll notice that I created some virtual members to allow the derived class to do what it needs to do.

public class Foo
{
private readonly Collection<string> strings = new Collection<string>();

/// <summary>
/// Gets the Num value
/// </summary>
/// <remarks>
/// This property is virtual so that the derived Observable class can provide property changed notifications
/// </remarks>
public virtual int Num { get; set; }

/// <summary>
/// Gets the collection of strings
/// </summary>
public ReadOnlyCollection<string> Strings
{
get
{
return new ReadOnlyCollection<string>(this.strings);
}
}

/// <summary>
/// The collection of strings
/// </summary>
/// <remarks>
/// This property is protected virtual so that the Observable class can substitue an
/// ObservableCollection
/// </remarks>
protected virtual ICollection<string> StringsCollection
{
get
{
return this.strings;
}
}

/// <summary>
/// Populates the string collection
/// </summary>
/// <param name="max">The maximum size for the collection</param>
public void Populate(int max = 1000)
{
// If no num was specified, create a random value
if (Num == 0)
{
var r = new Random();
this.Num = r.Next(1, max);
}

if (Num > max)
{
throw new InvalidOperationException("Num is greater than max");
}

for (var i = 0; i < this.Num; i++)
{
this.StringsCollection.Add(string.Format("string {0}", i));
}
}
}

The Derived Class: ObservableFoo

ObservableFoo is just like Foo except that it is data binding ready because it supports INotifyPropertyChanged and uses an ObservableCollection<string> to do it’s work.  The first thing ObservableFoo must do is to get the base class Foo to use an ObservableCollection<string> so it overrides the StringCollection property.

Second, Foo.Strings is a ReadOnlyCollection<string> which won’t do for data binding.  Instead we need to return a  data binding friendly ReadOnlyObservableCollection<string>. To do this I hide the Foo.Strings property with the new modifier.

Third, I override the Num property so I can fire a PropertyChanged notification when it is set.

Finally, I place ObservableFoo into a different assembly named FooLibrary.Observable since this project must reference System.Windows and forces all those who use it to do the same.

public sealed class ObservableFoo : Foo, INotifyPropertyChanged
{
private readonly ObservableCollection<string> strings = new ObservableCollection<string>();

public event PropertyChangedEventHandler PropertyChanged;

public override int Num
{
get
{
return base.Num;
}
set
{
base.Num = value;
this.OnPropertyChanged("Num");
}
}

public new ReadOnlyObservableCollection<string> Strings
{
get
{
return new ReadOnlyObservableCollection<string>(this.strings);
}
}

protected override ICollection<string> StringsCollection
{
get
{
return this.strings;
}
}

private void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

The Unit Tests

Testing Foo is pretty straight forward so I won't go into detail about that here, but testing ObservableFoo was quite interesting.  I have found that in most projects with Model / View / View Model (MVVM) that people neglect to test property change notifications.  Then you get subtle bugs in the UI because the property change notifications are not firing as expected.  I wanted to create a test that would verify that ObservableFoo was indeed observable so I created the FooObserver.  First a test that uses it.

/// <summary>
/// Given
/// * An ObservableFoo
/// When
/// * Populated
/// Then
/// * The number of CollectionChangedNotifications is equal to Num
/// * The number of PropertyChangedNotifications is equal to Num * 2
/// </summary>
[TestMethod]
public void ObservableFooRaisesChangeNotificationsWhenPopulated()
{
// Arrange
const int Expected = 100;
const int ExpectedPropNotifications = Expected * 2;

var foo = new ObservableFoo() { Num = Expected };

// Create an observer to monitor the notifications
var observer = new FooObserver(foo);

// Act
foo.Populate();

// Assert
// Each item that was added to the collection should trigger a CollectionChangedNotification
Assert.AreEqual(Expected, observer.CollectionChangedNotifications.Count);

// Each time an item is added 2 property changed notifications are raised
// Count and Item[]
Assert.AreEqual(ExpectedPropNotifications, observer.PropertyChangedNotifications.Count);
}

Here you can see that my observer simply attaches itself to the ObservableFoo and captures the notifications received so I can assert them.  The implementation is not too difficult but interesting.

public class FooObserver
{
private readonly Collection<NotifyCollectionChangedEventArgs> collectionChangedNotifications =
new Collection<NotifyCollectionChangedEventArgs>();

private readonly Collection<PropertyChangedEventArgs> propertyChangedNotifications =
new Collection<PropertyChangedEventArgs>();

public FooObserver(ObservableFoo foo)
{
((INotifyCollectionChanged)foo.Strings).CollectionChanged += this.ObservableStringsOnCollectionChanged;
((INotifyPropertyChanged)foo.Strings).PropertyChanged += this.OnPropertyChanged;
foo.PropertyChanged += this.OnPropertyChanged;
}

public ReadOnlyCollection<NotifyCollectionChangedEventArgs> CollectionChangedNotifications
{
get
{
return new ReadOnlyCollection<NotifyCollectionChangedEventArgs>(this.collectionChangedNotifications);
}
}

public ReadOnlyCollection<PropertyChangedEventArgs> PropertyChangedNotifications
{
get
{
return new ReadOnlyCollection<PropertyChangedEventArgs>(this.propertyChangedNotifications);
}
}

private void ObservableStringsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
this.collectionChangedNotifications.Add(args);
}

private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
this.propertyChangedNotifications.Add(args);
}
}

What Now?

Frankly, I hate to ship another assembly, thankfully NuGet will make this less painful. If this works as well as it appears to, I suppose I will start shipping data binding friendly versions of some of the types found in Microsoft.Activities.Extensions. Am I on the right track? After all, I could be missing something obvious. I could just make the base classes data binding friendly and make everyone reference System.Windows. The performance penalty is not that great, but then when there is a good solution why not use it?

Happy Coding!

Ron Jacobs
blog: http://blogs.msdn.com/rjacobs
Twitter: @ronljacobs


Viewing all articles
Browse latest Browse all 35736

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>