Click here to Skip to main content
13,354,132 members (57,642 online)
Click here to Skip to main content
Add your own
alternative version


7 bookmarked
Posted 7 Feb 2013

Data-binding Among Complex Expressions in C#

, 8 Feb 2013
Rate this:
Please Sign up or sign in to vote.
Data-binding among complex expressions in C#

What could be more preferable than simply stating X = Y in one place, then having the rest of the work transparently handled on your behalf?

Expression trees were one of the many language features introduced with C# 3.0 way back in 2007. Integration with the compiler allows us to nonchalantly ask it to emit data that represents a line of code instead of going about its usual business of readying the code for execution only. This leads to a convenient convergence of the benefits of reflection and those of compiler-checked code. An example that is further described here is that we no longer must use a string literal to meta-identify a property; instead, we can use a lambda expression like o => o.SomeProperty.

It took me much of this month of January 2013 to apply expression trees in a way that goes far beyond that simple property example. The result demonstrates not only the power of code as data, but also the applicability of the concept of data binding to coding in general instead of in the user-interface alone.

The project arose from the need to manage numerous interdependent properties on view-model and model classes. A property must be updated whenever one of the properties it depends on changes. Done on an individual basis, this would lead to a lot of event handlers. My undeviating aim to keep application code as succinct and declarative as possible led me to seek an alternative. What could be more preferable than simply stating X = Y in one place, then having the rest of the work transparently handled on your behalf? Nothing, of course. So I create the following public interface and then implemented the guts to make it work:

/// <summary> 
/// Keeps a target's properties updated with values determined by corresponding expressions. 
/// Properties referenced within a value expression are monitored such that property-change 
/// notifications cause the target properties to be updated. 
/// </summary> 
/// <typeparam name="TTarget">The type of object on which properties 
/// will be updated</typeparam> 
public class PropertiesUpdater<TTarget>     
    /// <summary>     
    /// Constructs a PropertiesUpdater instance for properties of the specified target.     
    /// </summary>     
    /// <param name="target">The object on which properties will be updated</param>     
    public PropertiesUpdater( TTarget target )     
    /// <summary>     
    /// Sets the specified property of target to the result of the specified value expression 
    /// whenever the value of a property referenced within the value expression changes.     
    /// </summary>     
    /// <typeparam name="TProperty">The type of the property on target to be set</typeparam>     
    /// <param name="targetPropertyExp">The property on target to be set</param>     
    /// <param name="valueExp">The expression to be monitored for changes 
    /// and evaluated as the target property's value</param>     
    /// <param name="setInitialValue">Should the target property 
    /// be immediately set to the evaluated value expression?</param>     
    /// <returns>The instance on which this method was invoked, 
    /// to allow multiple invocations to be chained together</returns>     
    public PropertiesUpdater<TTarget> AddProperty<TProperty>(     
        Expression<Func<TTarget, TProperty>> targetPropertyExp,     
        Expression<Func<TProperty>> valueExp,     
        bool setInitialValue = false )     

I will present some usage examples to show the sort of advantage that PropertiesUpdater provides. The restaurant POS system I am now working on includes a Table domain object that represents the sort of table you sit at. Table’s OpenOrder property is bound in the user-interface and so its private setter raises a property-changed event for notifying the UI of changes. Table’s constructor contains the following code for determining OpenOrder’s value:

new PropertiesUpdater<Table>( this )     
        t => t.OpenOrder,     
        () => DineInParts.Select( p => p.Owner ).SingleOrDefault( o => o != null && o.IsOpen )     

PropertiesUpdater recognizes most LINQ-to-Objects methods and can interpret the lambdas passed to them. After calling AddProperty in the above example, each of the following causes the target Table.OpenOrder property to be updated on this:

  1. changes to the Table.DineInParts collection property’s value on this,
  2. add/removes of the collection instance’s elements, and
  3. changes to values of the Owner and IsOpen properties referenced via collection elements.

Under the covers, PropertiesUpdater relies on the referenced objects implementing INotifyPropertyChanged and the referenced collections implementing INotifyCollectionChanged.

Next, I will present two usages taken from the constructor of the order detail screen’s view-model in the restaurant POS system:

// Base the phone number and name on their sources in the domain model. 
// Initialize these without causing the contact to be overwritten. 
new PropertiesUpdater<OrderDetailsScreenPM>( this )     
        pm => pm.PhoneNumber,     
        () => domainItem.Contact is VerifiedCustomer ? 
              ((VerifiedCustomer)domainItem.Contact).PhoneNumber : null,     
        setInitialValue: true     
        pm => pm.Name,     
        () => domainItem.Contact != null ? domainItem.Contact.Name : null,     
        setInitialValue: true     
// Store a contact in the domain model that has the user-provided phone number and name. 
// new PropertiesUpdater<Order>( domainItem )     
        dm => dm.Contact,     
        () => PhoneNumber != null     
            // If a phone# is provided then lookup a verified contact; if not found then create one.     
            ? (DataAccess.Instance.FindCustomer( PhoneNumber ) ?? new VerifiedCustomer 
                 { PhoneNumber = PhoneNumber, Name = Name })     
            // If only a name is provided then create an unverified contact.     
            : Name != null ? new Customer { Name = Name } : null     

The first thing you may notice about this example is that the "one line" permitted for the value expression is actually spanning multiple lines. The "one line" limit just means the expression must not contain a semicolon. The expression can in fact be however complex is needed.

In the value expressions for the first instantiated PropertiesUpdater, notice the presence of property paths that navigate across more than one property. These are domainItem.Contact.PhoneNumber and domainItem.Contact.Name. What if Contact's value changes? The former value of Contact is no longer of interest (in particular, PhoneNumber's and Name’s values on the former contact), therefore the PropertiesUpdater stops observing the former contact. This is a similar situation as when an item is removed from a collection in which case the removed element is no longer observed. In the opposing case, PropertiesUpdater will begin observing a newly introduced value.

The Wikipedia article on Reactive programming provides a canonical example that is in fact PropertyUpdater’s primary purpose. So, I decided to call the namespace for holding PropertiesUpdater and its supporting classes "Qnomad.Reactive", at least until someone who knows better tells me that’s an inappropriate classification. As for Microsoft’s Reactive Extensions (Rx), I didn’t see its connection to what PropertiesUpdater provides until I came across an interesting article that fills in the gap: Data binding in code using Reactive Extensions. When I first saw the title, I worried my month-long work on PropertiesUpdater may have been for naught. The single usage example that the article builds up to, however, falls far short of what PropertiesUpdater can accomplish. The explained functionality reminds me of the basic building block with which PropertiesUpdater observes referenced objects, which resembles Josh Smith’s PropertyObserver. Using Rx for this purpose, however, doesn’t seem necessary or even useful. This leaves me wondering how "reactive" Rx really is; although, I suppose it may just be reactive in a different sense. As with other CS concepts that become buzzwords, "reactive" is likely to go through a period of overuse before its actual meaning gets whittled down in the minds of the masses. Instead of waiting for that to happen, though, I would love to hear some clarification sooner.

Realizing the significant time expense of implementing PropertiesUpdater about a week into it, I checked if something similar already exists. I came across the WhenAny method of the ReactiveUI project, but it pales in comparison to what I ended up accomplishing with PropertiesUpdater. It wasn’t until after completing PropertiesUpdater and describing it in the above paragraphs that I finally found two projects, Bindable LINQ and Obtics, that appear to accomplish a similar feat. My initial impression, however, is that PropertiesUpdater can more easily accomplish the same with its mere two public methods and comparatively tiny implementation. Anyway, I’ll further explore these two alternatives later, if there is occasion.


This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


About the Author

Adrian Alexander
United States United States
Adrian loves facilitating suave user experiences via the latest and greatest GUI technologies such as Windows 8 Metro-style apps as well as WPF. More generally, he finds joy in architecting software that is easy to comprehend and maintain. He does so by applying design patterns at the top-level, and by incessantly refactoring code at lower levels. He's always interested in hearing about opportunities for full or part-time development work. He resides in Pennsylvania but can potentially travel anywhere in the country. (Writing about himself in the third-person is Adrian's new hobby.)

You may also be interested in...


Comments and Discussions

QuestionWhere is the source code? Pin
theperm15-Mar-13 12:05
membertheperm15-Mar-13 12:05 
AnswerRe: Where is the source code? Pin
Adrian Alexander16-Mar-13 9:50
memberAdrian Alexander16-Mar-13 9:50 
GeneralRe: Where is the source code? Pin
theperm17-Mar-13 7:45
membertheperm17-Mar-13 7:45 
QuestionLike it Pin
Sacha Barber7-Mar-13 20:44
mvpSacha Barber7-Mar-13 20:44 
AnswerRe: Like it Pin
Adrian Alexander16-Mar-13 9:34
memberAdrian Alexander16-Mar-13 9:34 
QuestionNice! Pin
Dmitri Raiko3-Mar-13 23:58
memberDmitri Raiko3-Mar-13 23:58 
AnswerRe: Nice! Pin
Adrian Alexander5-Mar-13 17:35
memberAdrian Alexander5-Mar-13 17:35 
AnswerRe: Nice! Pin
Dmitri Raiko5-Mar-13 22:39
memberDmitri Raiko5-Mar-13 22:39 
GeneralRe: Nice! Pin
Adrian Alexander6-Mar-13 3:22
memberAdrian Alexander6-Mar-13 3:22 
GeneralRe: Nice! Pin
Dmitri Raiko6-Mar-13 4:21
memberDmitri Raiko6-Mar-13 4:21 
GeneralRe: Nice! Pin
Adrian Alexander6-Mar-13 4:27
memberAdrian Alexander6-Mar-13 4:27 
GeneralRe: Nice! Pin
Adrian Alexander6-Mar-13 4:34
memberAdrian Alexander6-Mar-13 4:34 
GeneralRe: Nice! Pin
Dmitri Raiko6-Mar-13 12:18
memberDmitri Raiko6-Mar-13 12:18 
GeneralRe: Nice! Pin
Adrian Alexander6-Mar-13 13:08
memberAdrian Alexander6-Mar-13 13:08 
GeneralThanks Pin
Vincent BOUZON25-Feb-13 7:44
memberVincent BOUZON25-Feb-13 7:44 
GeneralRe: Thanks Pin
Adrian Alexander25-Feb-13 13:44
memberAdrian Alexander25-Feb-13 13:44 
QuestionI don't quite get it. Pin
Qwertie20-Feb-13 9:21
memberQwertie20-Feb-13 9:21 
AnswerRe: I don't quite get it. Pin
Adrian Alexander20-Feb-13 11:16
memberAdrian Alexander20-Feb-13 11:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.180111.1 | Last Updated 8 Feb 2013
Article Copyright 2013 by Adrian Alexander
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid