Home > C#, Dependencies, Open Source, Uncategorized > From Lambda Dependencies to Lambda Bindings

From Lambda Dependencies to Lambda Bindings

April 3rd, 2009

Lambda-based binding for the business layer or your View Model :)

I’ve had this on the shelf for quite a while, but Kent Boogaart’s article on POCOs vs. DependencyObjects finally got me to cleaning things up a little. Kent is coming up with a similar approach that looks very promising, so you should keep your eyes on his blog, too.

Lambda Bindings are built on top of the Lambda Dependencies project I published a while ago. The original Lambda Dependencies allow you to observe object graphs for changes using simple LINQ expressions. Lambda Bindings leverage this pattern by not just publishing a change event but synchronizing target properties or fields automatically.

This provides you with a generic binding framework that can be used wherever you want to synchronize objects. Let’s have a first example:

 

public void TestBinding(Student student)
{
  string cityName = "";

  //synchronize the cityName field with the City property of the school's address
  var binding = LambdaBinding.BindOneWay(
      () => student.School.Address.City,
      () => cityName);

  //change the property on the School object
  student.School.Address.City = "Sin City";

  //the binding expression updated the local variable
  Assert.AreEqual("Sin City", cityName);
}

What’s happening in the snippet above is that I created a binding between a nested property of a referenced object and a local field. As soon as the binding source (the City property of a school’s address) is changed, the local cityName field is being updated as well.

 

However, the Lambda Dependencies not only cover the source properties but the whole object graph. Accordingly, exchanging the whole School (or the Student instance) also triggers an update. In the snippet below, the cityName variable is being updated twice:

 

[Test]
public void Updating_Intermediary_Object_Should_Update_Target(Student student)
{
  string cityName = "";

  //synchronize the cityName field with the City property of the school's address
  var binding = LambdaBinding.BindOneWay(
      () => student.School.Address.City,
      () => cityName);


  //change bound City property -> triggers update of the local variable
  student.School.Address.City = "Paris";
  Assert.AreEqual("Paris", cityName);

  //create a completely different school instance
  School englishSchool = new School();
  englishSchool.Address = new Address {City = "London"};

  //assign the new school to the student
  student.School = englishSchool;

  //setting the School property also triggered the binding
  Assert.AreEqual("London", cityName);
}

 

Value Conversion

You can do simple value conversion by just submitting a converter to the binding expression. This allows you to intercept the binding pipeline or bind objects of different types together. If you’re coming from WPF, this feels natural anyway, but the solution here does not require you to implement a value converter – a simple Func<TSource, TTarget> is sufficient.

Here’s a simple sample that performs a conversion of a boolean flag to into a corresponding Visibility enum value:

 

[Test]
public void Boolean_Should_Be_Converted_To_Visibility()
{
  //create a hidden window
  Window window = new Window { Visibility = Visibiliy.Collapsed };
  
  //create a view model
  MyViewModel viewModel = new MyViewModel { IsVisible = false };
  
  //create binding that casts the Visibility into a boolean
  LambdaBinding.BindOneWay(
      () => viewModel.IsVisible,
      () => window.Visibility,
      b => b == true ? Visibility.Visible : Visibility.Collapsed;
  
  //a change in the ViewModel shows/hides the window
  viewModel.IsVisible = true;
  
  Assert.AreEqual(Visibility.Visible, window.Visibility);  
}

 

Two-Way-Binding

Two way binding works too, of course:

[Test]
public void Updates_Should_Work_Both_Ways()
{
  //create two-way binding
  var binding = LambdaBinding.BindTwoWay(
    () => FirstStudent.Name,
    () => SecondStudent.Name);

  //change property on source
  FirstStudent.Name = "Peter";
  Assert.AreEqual("Peter", SecondStudent.Name);

  //change property on target
  SecondStudent.Name = "Parker";
  Assert.AreEqual("Parker", FirstStudent.Name);
}

In case you need to perform type conversion, you need to supply two converter functions for forward / reverse conversion:

//bind a boolean property to a control's Visibility property
var binding = LambdaBinding.BindTwoWay(
    () => ModelItem.IsEnabled,
    () => MyControl.IsVisible,
    b => b == true ? Visibility.Visible : Visibiliy.Collapsed
    v => v == Visibility.Visible ? true : false);

 

Default Values

In case the object graph is being broken (e.g. because the School was set to null), the target node will be automatically set to its default value (null for an object, 0 for an int etc.). However, you can also specify a default value of your own:

//a local field to be updated
private string schoolCity;

[Test]
public void Breaking_The_Chain_Should_Assign_Default_Value_To_Target_If_Specified()
{
  var binding = LambdaBinding.BindOneWay(
      () => Student.School.Address.City,
      () => schoolCity,
      "[No City]");

  //break the source chain
  Student.School = null;

  //the default value was assigned to the target
  Assert.AreEqual("[No City]", schoolCity);
}

(btw: the above snippet also shows you that you can easily bind to a field rather than a property).

 

Weak References

The underlying Lambda Dependencies only use weak references so you’re not at risk of creating memory leaks. However, LambdaBinding implements IDisposable, so the proper way to clean things up would be to dispose your binding.

Things to Consider

Remember that that the underlying Lambda Dependencies rely on the INotifyPropertyChanged interface, so don’t expect source binding to fields (or properties that do not fire a PropertyChanged event) to magically update your targets.

 

Download: lambda-dependencies.zip

 

kick it on DotNetKicks.com


  1. stooboo
    April 13th, 2009 at 23:02 | #1

    WOW !!!… i’m passing this on to friends
    Thankyou !!
    This should help with implementing Presentation Model in win forms…
    Stu

  2. KShaban
    April 14th, 2009 at 03:28 | #2

    This is perfect. A lot of framworks like BindableLinq and ContinousLinq deal with collections, but simple property binding was really neglected.

    Thanks!

  3. April 14th, 2009 at 07:47 | #3

    Stu,
    Hope it’ll help! However, keep in mind, that WinForms controls do *not* implement INotifyPropertyChanged. Accordingly, you can easily update your UI based on events in your Presentation Model, but the dependency framework won’t get notified about changes on the UI due to missing events.
    Cheers,
    Philipp

  4. stooboo
    April 14th, 2009 at 21:25 | #4

    Philipp
    Good point ! ;-)
    I should have read the ‘Things to consider’ b4 I started typing !
    Thankyou anyway – it’s still going to be useful !
    Stu

  5. KShaban
    May 7th, 2009 at 01:03 | #5

    Phillipp
    Will this work if one-side of the binding is a DependencyProperty or do both sides need to implement INotifyPropertyChanged.

    Thanks for the great work!
    Kavan

  6. KShaban
    May 7th, 2009 at 01:06 | #6

    @KShaban
    Should of looked at the sample better, of course it does.

    Thanks!!

  7. jbland
    December 29th, 2009 at 18:03 | #7

    Have you looked at Reactive Linq ?

  8. Gerhard
    September 3rd, 2010 at 19:33 | #8

    Phillip,
    I just try to make a unit test in VB, and I found out, that two way binding works, if both ends were properties of objects, but fails, if the ‘source’ is a property and the ‘destination’ is a field. By the way, to define two way bindings, it may be better to name the parameters different than ‘source’ and ‘destination’, isn’t it?

    With best regards

    Gerhard

  9. Gerhard
    September 3rd, 2010 at 19:44 | #9

    Phillip,
    by the way, it also fails, when ‘Source’ is the field and ‘destination’ is the property …….

    Sorry.

    With best regards

    Gerhard

  10. Gerhard
    September 4th, 2010 at 05:09 | #10

    Phillip,

    whats about binding between two collections? I think about a scenario, where I get back a collection of Students from my DAL and want to bind a collection(of StudentGUI) to that resultlist? StudentGUI can ba an object which is optimized for GUI usage and Student is a pure DAL object or BO object.

    Thanks

    Gerhard

  11. October 30th, 2010 at 09:33 | #11

    Gerhard,
    The bindings rely on INotifyPropertyChanged, so if that event isn’t fired, nothing happens as the binding engine doesn’t catch the update.

    Regarding collection binding – this goes more into the realm of Bindable / Reactive / Continuous Linq. There’s a set of project on CodePlex that cover that scenario :)

  12. Sergei Vedischev
    December 27th, 2010 at 16:07 | #12

    Thank You Philipp!
    BindOneWay works nice, but in two way binding i’ve got a stack overflow exception. Should I fire the PropertyChanged in my view in some special manner to avoid this event cycle?

  13. Sergei Vedischev
    December 27th, 2010 at 16:22 | #13

    @Sergei Vedischev
    oops! sorry, my attention was under xmas press, all magic was in setters:
    if (value == secondStudent) return;
    See unit tests on every fail :)

  14. Jan
    January 13th, 2011 at 14:38 | #14

    Philipp,

    First of all: “Merci vielmol” or thank you very much for this great article!
    As you said the code is relying on INotifyPropertyChanged. I’m not a big fan of having this interface on domain objects. Are you thinking of having an extension to the dependency builder where also other pub/sub mechanisms could be supported (e.g. Property “City” has its CityChanged event or similar).

    Thanks
    Jan

  15. January 13th, 2011 at 14:56 | #15

    Hoi Jan :)

    I’ll stick with INPC – after all, it’s the common mechanism for property changes in the .NET framework (and accordingly resides in System.ComponentModel). Of course, a City -> CityChanged event strategy would be possible as well, but this were just another convention, with a more complicated API (10 properties -> 10 different events), so you probably wouldn’t gain too much, would you?

    Cheers,
    Philipp

  1. No trackbacks yet.