Content by Category
.NET 1.x
.NET 2.0
.NET 3.0
.NET 3.5
.NET 4.0
.NET 4.5
.NET Assemblies
.NET Framework
.NET Getting Started
Accessibility
ADO.NET
Advertorials
Agile Development
AJAX
Amazon Web Services
Analysis Services
Android
Architecture
Arduino
ASP .NET Web API
ASP.NET
ASP.NET MVC
ASP.NET WebForms
Azure
B2B (Business Integration)
BDD
Big Data
Bing
BizTalk
Book Excerpts
Build and Deploy
Business Intelligence
C#
C++
ClickOnce
Cloud Computing
Code Contracts
CODE Framework Info - non Technical
CODE on the Road!
COM+
Community
Conferences
Continuous Integration
Crystal Reports
CSLA.NET
CSS
Data
Debugger
Design Patterns
Development Process
Display Technologies
Distributed Computing
Document Database
DotNetNuke
DSL
Dynamic Languages
Dynamic Programming
Editorials
Enterprise Services ("COM+")
Entity Framework
Events
Expression Blend
F#
Fox to Fox
Frameworks
Functional Programming
Git
Graphics
HTML 5
Internet Explorer 8.0
Interviews
IOS
iPhone
Iron Ruby
Java
Java Script
JavaScript
jQuery
JSON
Lightswitch
LINQ
Linux
LUA
Mac OS X
MDX
Messaging
Metro
Microsoft Application Blocks
Microsoft Business Rules Framework
Microsoft Dynamics
Microsoft Expression
Microsoft Office
Mobile Development
Mobile PC
Mono
MsBuild
MVVM
MySQL
Network
NHibernate
node.js
NOSQL
Nuget
Object Oriented Development
Objective C
Odata
OLAP
Open Source
Opinion
Opinions
Oracle
ORM
Other Languages
Parallel Programming
Patterns
PHP
Podcasts
Post Mortem
PowerPoint
Print/Output
Prism
Product News
Product Reviews
Project Management
Prolog
Python
Q&A
Rails
Rake
Razor
Reporting Services
REST
RIA Services
Ruby
Ruby on Rails
Scheme
Search
Security
Services
SharePoint
SignalR
Silverlight
SOA
Social Networks
Software & Law
Software Business
Source Control
Speech-Enabled Applications
SQL Server
SQL Server 2000
SQL Server 2005
SQL Server 2008
SQL Server 2012
SQL Server CE/AnyWhere/Mobile/Compact
SSIS
Subversion
Sync Framework
Tablet PC
TDD
Team System
Techniques
Testing and Quality Control
TFS
Tips
TypeScript
UI Design
UML
User Groups
VB Script
VB.NET
Version Control
VFP and .NET
VFP and SQL Server
Virtual Earth
Vista
Visual Basic
Visual Basic 6 (and older)
Visual FoxPro
Visual Studio .NET
Visual Studio 11
Visual Studio 2005
Visual Studio 2008
Visual Studio 2010
Visual Studio 2011
Visual Studio 2012
Visual Studio Tools for Office
VSX
WCF
Web Development (general)
Web Services
WebMatrix
WF
Whitepapers
Windows 7
Windows 8
Windows Azure
Windows Live
Windows Phone 7
Windows Phone SDK
Windows Server
Windows Vista
WinForms
WinRT
Workflow
WPF
XAML
Xiine Documentation
XML
XNA
XSLT



Learn Now


LearnNow
 


Hacker Halted


Reader rating:
Click here to read 17 comments about this article.
Article source: CoDe (2009 Jul/Aug)

INotifyPropertyChanged Is Obsolete

The INotifyPropertyChanged interface, while easy to implement, is hard to consume. Registering for notification events requires extra bookkeeping code that obfuscates business logic. Update Controls is an open source library that replaces event-based data binding with automatic discovery and updating. Powerful yet difficult patterns

become simple. WPF data binding requires that a bindable class implement the INotifyPropertyChanged interface. This interface is fairly easy to implement as it requires only a single event: PropertyChanged. This event is to be fired every time one of the properties is changed.

The problem with this interface is in the code that consumes it. The consumer of the interface usually needs to update a property. The value of that property depends upon properties of other objects, and so the dependent object registers for the PropertyChanged event. When this event is fired, the dependent object calculates the value of the dependent property, stores it, and fires another PropertyChanged event upstream. Listing 1 shows an example.

Much of the code using INotifyPropertyChanged is concerned with registering for the PropertyChanged event, reregistering when the subject changes, and firing it at the appropriate time. The actual business logic is lost in a sea of bookkeeping.

"
Much of the code using INotifyPropertyChanged is concerned with registering for the PropertyChanged event, reregistering when the subject changes, and firing it at the appropriate time. The actual business logic is lost in a sea of bookkeeping.
"

What would this code look like if you could take INotifyPropertyChanged out of the picture? What if data binding could work directly against a property and update whenever it changed? In that ideal world, the bookkeeping code goes away and you are left with just the business logic, as shown in Listing 2.

This ideal code really works-if you use Update Controls. Update Controls is an open source software library that does not require you to implement the INotifyPropertyChanged interface or fire PropertyChanged events. You just need to write your business logic. The Update Controls library figures out when your properties change and automatically updates the view. You can download the library from Codeplex (http://www.codeplex.com/updatecontrols) or UpdateControls.NET (http://updatecontrols.net/).

"
The Update Controls library figures out when your properties change and automatically updates the view.
"

The Update Controls library

To use Update Controls, add two references to your C# or Visual Basic WPF project: UpdateControls.dll and UpdateControls.XAML.dll. Within your XAML files, add a namespace reference to UpdateControls.XAML.

<Window ...
    xmlns:u="clr-namespace:UpdateControls.XAML;
assembly=UpdateControls.XAML"
    ... >

Now use “{u:Update}” everywhere that you would otherwise use “{Binding}”. Your WPF view will be updated whenever the property changes. For example:

<TextBlock Text="{u:Update MailMerge}"/>

MailMerge is an example of a dependent property because it depends upon other properties for its value. CurrentCustomer and Name, on the other hand, are independent properties since they can change independently. Independent properties support both read and write, but dependent properties are read-only. As you’ve seen, writing a dependent property is simple; just put the business logic into the get method. But writing an independent property takes one additional step. You need to create an Independent sentry object that keeps track of every get and set. Listing 3 shows the CurrentCustomer property as an example.

The Independent sentry tells Update Controls that MailMerge’s getter has called CurrentCustomer’s getter. It also makes MailMerge out-of-date when CurrentCustomer’s setter is invoked. Creating these sentries is all you need to do, but you need to do it for every independent property.

To help you write this extra code, Update Controls installs a Visual Studio add-in that shows up in the Tools menu as “Generate Independent Properties”. It is mapped, by default, to Ctrl+D, G (as well as Ctrl+D, Ctrl+G, just in case). Select any private field or set of private fields in a C# or Visual Basic class, hit Ctrl+D, G, and the add-in will generate the Independent sentry and property.

Whereas INotifyPropertyChanged forces you to write bookkeeping code for intermediate logic, Update Controls makes it trivial. Because of this, Update Controls simplifies patterns that are much more cumbersome using INotifyPropertyChanged.

The Presentation Model Pattern

In 2004, Martin Fowler documented the Presentation Model pattern (http://martinfowler.com/eaaDev/PresentationModel.html). In 2006, John Gossman of the Expression team gave the name Model-View-ViewModel to a realization of this pattern in WPF (http://blogs.msdn.com/johngossman/archive/2006/02/27/540304.aspx). In either case, the goal is separation of concerns.

The pattern separates the data model from the view by interjecting an intermediate model created specifically for presentation. This intermediate presentation model transforms the data model into a form that the view can easily consume. The presentation model is an adapter between the data model and the view. See Figure 1 for a diagram of the resulting structure.

Click for a larger version of this image.

Figure 1: Structure diagram of the Presentation Model pattern. The view has direct access to the presentation model, and indirect access to the data mode.

Why is this important? The data model is concerned with storing data. It should be a faithful representation of the problem domain. It should be useful not only to the UI, but also to business logic. It should not have any presentation logic-you should delegate presentation logic to the presentation model.

The CRMPresentation class in Listing 2 is an example of a presentation model. It is an adapter between the Customer data model and the view. But the view also needs direct access to the data model for its other behavior. Since a WPF component can have only one DataContext, you’ll add a pass-through property to the presentation model.

public CustomerSelector CustomerSelector
{
    get return _customerSelector; }
}

Or in VB:

Public ReadOnly Property CustomerSelector() _
    As CustomerSelector
    Get
        Return _customerSelector
    End Get
End Property

Now the view has access to the data model. The view binds to the current Customer for Name and to the presentation model for MailMerge. It uses the pass-through property to get from the presentation model back to the data model, like this:

<StackPanel>
    <TextBox
        Text="{u:Update
        CustomerSelector.CurrentCustomer.Name}"/>
    <TextBlock
        Text="{u:Update MailMerge}"/>
</StackPanel>

The Navigation Model pattern

WPF makes it really easy to bind one control to another. You might bind the scaling transform of an image to a zoom slider, for example. Or you might bind the selection in a list to the data context of a form so that you can edit the selected item.

Binding controls together in this way tightly couples them. It makes it difficult to decompose the UI into separate user controls for easier maintenance. And it makes it hard to make two windows work together when they are not part of the same visual tree.

The solution is to move this UI selection state out of the controls and into a shared object-what I call a navigation model. All of the controls can then bind to the navigation model rather than to each other. This model doesn’t store application data; that’s the job of the data model. The navigation model stores the user’s point-of-view from which they observe the data model. As the user selects items in a list, they are navigating through application data. A structure diagram appears in Figure 2.

Click for a larger version of this image.

Figure 2: Structure diagram of the Navigation Model pattern. The navigation model holds transient state, while the data model holds persistent state.

The biggest difference between the navigation model and the data model is that the data model is persistent whereas the navigation model is transient. Application data is stored in a database and shared among many users. Navigation data exists only in memory. It sticks around only as long as the user’s session, and goes away when they close the window.

The CustomerSelector class in Listing 3 is a navigation model. It stores the currently selected customer. Three controls bind to the CustomerSelector: customer name, mail merge text, and customer list. The user controls the selected customer with the list and the other two controls pick it up from there.

<ListBox
    ItemsSource="{u:Update CRMData.Customers}"
    SelectedItem="{u:Update
        CustomerSelector.CurrentCustomer}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <ListBoxItem
                Content="{u:Update Name}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

By sharing a navigation model, all controls can be linked without referring to each other.

One final benefit of the navigation model pattern is that it becomes easy to programmatically navigate on the user’s behalf. To select a new customer when they are added to the CRM system, just change the CurrentCustomer property in the navigation model. The list, the name control, and the mail merge all respond to this one change.

public void NewCustomer()
{
    _customerSelector.CurrentCustomer =
        _crmData.NewCustomer();
}

Or in VB:

Public Sub NewCustomer()
    _customerSelector.CurrentCustomer = _
        _crmData.NewCustomer()
End Sub

Advantages of Asynchronous Updates

In these examples you’ve seen how Update Controls takes care of notification on your behalf, eliminating a significant amount of bookkeeping code. You never have to write event registration code again. It simplifies patterns that are otherwise difficult to implement.

But there are advantages that you don’t see. PropertyChanged events are fired synchronously. A PropertyChanged event is going to be handled immediately; consumers aren’t going to wait until all changes are complete. If one user action causes multiple properties to change, and one consumer is dependent upon all changes, multiple PropertyChanged events will be fired. This causes a cascade of redundant updates.

Have you ever seen a list box with so many items that the scroll bar is a thin horizontal line? If you select all of the items and press the delete button, you might see an hourglass appear. The thin line of the scrollbar will grow, slowly at first, then faster, until it fills the height. Finally, the list is empty and the hourglass disappears.

What’s happened is that a loop somewhere in the code is calling a method to delete each item. That method fires an event to the list box informing it that the list has changed. The list box removes the item, repaints itself, and updates the scroll bar. Only then does the method return to the loop so it can be called again.

Update Controls, on the other hand, works asynchronously. It waits until all changes are completed before updating the view. Even if the view depends upon all of those changes, it is updated only once. The scroll bar of a list box using u:Update is not repainted until all of the items are deleted.

Another advantage of asynchronous updates is thread safety. A synchronous event is handled on the thread on which it is fired. Since the UI is the consumer of PropertyChanged events, all PropertyChanged events must be fired on the UI thread. If a background thread changes properties visible to the UI, you must take special care to move that event across threads.

Since Update Controls works asynchronously, changes can occur on any thread. You must, of course, use locks to protect your data from simultaneous access, but there is no additional code needed to make updates thread safe. Changes made by a background thread automatically cause the foreground thread to update the UI.

Download the library to see all of these advantages for yourself. Once you start using Update Controls on a regular basis, I think you’ll agree that INotifyPropertyChanged is obsolete.

Michael L Perry

&

By: Michael L Perry

Michael is a software generalist, producing mobile, desktop, and enterprise solutions. He codes in both C# and Java, and applies the lessons learned from each to the other.

By day, Michael is a solutions architect at AmerisourceBergen Specialty Group where he delivers high quality enterprise software in .NET to support the pharmaceutical industry. By night, he writes mobile applications in Java for the Andriod platform. He blogs his discoveries along the way at Adventures In Software (http://adventuresinsoftware.com/blog).

mperry@adventuresinsoftware.com


Object-Oriented Excel

In a spreadsheet, you can enter a formula in B2 that refers to data in A1. When A1 changes, B2 updates. You don’t have to tell A1 to notify B2; it just happens.

Update Controls works on the same principle. A dependent property runs code to determine its value. The property depends upon everything that that code touches. There is no need to register for notification because the dependency is inferred.



Listing 1 (C#): Mail merge using INotifyPropertyChanged
public class CRMPresentation : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private CustomerSelector _customerSelector;
    private Customer _currentCustomer;
    private string _mailMerge;

    public CRMPresentation(CustomerSelector customerSelector)
    {
        _customerSelector = customerSelector;
        UpdateCurrentCustomer();

        // Register for changes to the selected customer.
        _customerSelector.PropertyChanged +=
            new PropertyChangedEventHandler(
                CustomerSelector_PropertyChanged);
    }

    public string MailMerge
    {
        get return _mailMerge; }
    }

    private void CustomerSelector_PropertyChanged(
        object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "CurrentCustomer")
        {
            UpdateCurrentCustomer();
        }
    }

    void CurrentCustomer_PropertyChanged(
        object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Name")
        {
            UpdateMailMerge();
        }
    }

    private void UpdateCurrentCustomer()
    {
        // Unregister from the old customer.
        if (_currentCustomer != null)
            _currentCustomer.PropertyChanged -=
                new PropertyChangedEventHandler(
                    CurrentCustomer_PropertyChanged);

        // Make the switch.
        _currentCustomer = _customerSelector.CurrentCustomer;
        UpdateMailMerge();

        // Register with the new customer.
        if (_currentCustomer != null)
            _currentCustomer.PropertyChanged +=
                new PropertyChangedEventHandler(
                    CurrentCustomer_PropertyChanged);
    }

    private void UpdateMailMerge()
    {
        // Calculate the new mail merge.
        Customer recipient = _currentCustomer;
        if (recipient == null)
        {
            _mailMerge = string.Empty;
        }
        else
        {
            string name = recipient.Name;

            _mailMerge = string.Format(
                "Congratulations {0}, you are a winner!",
                name);
        }

        // Notify others that the mail merge has changed.
        FirePropertyChanged("MailMerge");
    }

    private void FirePropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged.Invoke(this,
                new PropertyChangedEventArgs(name));
    }
}


Listing 1 (VB): Mail merge using INotifyPropertyChanged
Public Class CRMPresentation
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler _
        Implements INotifyPropertyChanged.PropertyChanged

    Private _customerSelector As CustomerSelector
    Private _currentCustomer As Customer
    Private _mailMerge As String

    Public Sub New(ByVal customerSelector As CustomerSelector)
        _customerSelector = customerSelector
        UpdateCurrentCustomer()

        ' Register for changes to the selected customer.
        AddHandler _customerSelector.PropertyChanged, _
            AddressOf Me.CustomerSelector_PropertyChanged
    End Sub

    Public ReadOnly Property MailMerge() As String
        Get
            Return _mailMerge
        End Get
    End Property

    Private Sub CustomerSelector_PropertyChanged( _
        ByVal sender As Object, _
        ByVal As PropertyChangedEventArgs)
        If e.PropertyName = "CurrentCustomer" Then
            UpdateCurrentCustomer()
        End If
    End Sub

    Private Sub CurrentCustomer_PropertyChanged( _
        ByVal sender As Object, _
        ByVal As PropertyChangedEventArgs)
        If e.PropertyName = "Name" Then
            UpdateMailMerge()
        End If
    End Sub

    Private Sub UpdateCurrentCustomer()
        ' Unregister from the old customer.
        If _currentCustomer IsNot Nothing Then
            RemoveHandler _currentCustomer.PropertyChanged, _
                AddressOf CurrentCustomer_PropertyChanged
        End If

        ' Make the switch.
        _currentCustomer = _customerSelector.CurrentCustomer
        UpdateMailMerge()

        ' Register with the new customer.
        If _currentCustomer IsNot Nothing Then
            AddHandler _currentCustomer.PropertyChanged, _
                AddressOf CurrentCustomer_PropertyChanged
        End If
    End Sub

    Private Sub UpdateMailMerge()
        ' Calculate the new mail merge.
        Dim recipient As Customer
        recipient = _currentCustomer
        If recipient Is Nothing Then
            _mailMerge = String.Empty
        Else
            _mailMerge = String.Format( _
                "Congratulations {0}, you are a winner!", _
                recipient.Name)
        End If

        ' Notify others that mail merge has changed.
        FirePropertyChanged("MailMerge")
    End Sub

    Private Sub FirePropertyChanged(ByVal name As String)
        RaiseEvent PropertyChanged(Me, _
            New PropertyChangedEventArgs(name))
    End Sub
End Class


Listing 2 (C#): Mail merge using Update Controls
public class CRMPresentation
{
    private CustomerSelector _customerSelector;

    public CRMPresentation(CustomerSelector customerSelector)
    {
        _customerSelector = customerSelector;
    }

    public string MailMerge
    {
        get
        {
            Customer recipient =
                _customerSelector.CurrentCustomer;
            if (recipient == null)
            {
                return string.Empty;
            }
            else
            {
                string name = recipient.Name;

                return string.Format(
                    "Congratulations {0}, you are a winner!",
                    name);
            }
        }
    }
}


Listing 2 (VB): Mail merge using Update Controls
Public Class CRMPresentation
    Private _customerSelector As CustomerSelector

    Public Sub New(ByVal customerSelector As CustomerSelector)
        _customerSelector = customerSelector
    End Sub

    Public ReadOnly Property MailMerge() As String
        Get
            ' Calculate the new mail merge.
            Dim recipient As Customer
            recipient = _customerSelector.CurrentCustomer
            If recipient Is Nothing Then
                Return String.Empty
            Else
                Return String.Format( _
                    "Congratulations {0}, you are a winner!", _
                    recipient.Name)
            End If
        End Get
    End Property
End Class


Listing 3 (C#): A class with an independent property
public class CustomerSelector
{
    private Customer _currentCustomer;
    private Independent _indCurrentCustomer = new Independent();

    public Customer CurrentCustomer
    {
        get
        {
            _indCurrentCustomer.OnGet();
            return _currentCustomer;
        }
        set
        {
            _indCurrentCustomer.OnSet();
            _currentCustomer = value;
        }
    }
}


Listing 3 (VB): A class with an independent property
Public Class CustomerSelector
    Private _currentCustomer As Customer
    Private _indCurrentCustomer As New UpdateControls.Independent

    Public Property CurrentCustomer() As Customer
        Get
            _indCurrentCustomer.OnGet()
            Return _currentCustomer
        End Get
        Set(ByVal value As Customer)
            _indCurrentCustomer.OnSet()
            _currentCustomer = value
        End Set
    End Property
End Class


How would you rate the quality of this article?
1 2 3 4 5
Poor      Outstanding

Tell us why you rated the content this way. (optional)

Average rating:
2.7 out of 5

46 people have rated this article.

rssbus

      LearnNow

 

Xojo