Structuring Objects for Use by Data Entry Forms

To create effective data entry forms, you should do a few things to make your objects friendlier to the user interface controls that will bind to them. RIA Services entities automatically implement these features by default, but if you are binding to objects whose classes are defined on the client, such as a ViewModel when using the MVVM design pattern discussed in Chapter 13, or you're simply not using RIA Services at all, you will need to implement these features manually. Let's take a look at the most important features you should add to your objects for the benefit of the user interface.

Implementing the INotifyPropertyChanged Interface

One of the key interfaces that your objects should implement is the INotifyPropertyChanged interface (found in the System.ComponentModel namespace). This is a simple interface, requiring your class to implement a single event named PropertyChanged. Bindings automatically listen for this event, and when it is raised, any binding associated with that property updates itself accordingly, and hence, the control associated with that binding is updated as well.

images Note Silverlight 5 has introduced the INotifyPropertyChanging interface, which you might like to implement in addition to the INotifyPropertyChanged interface on your classes. However, here we'll focus only on the INotifyPropertyChanged interface.

Basic Implementation (with “Magic Strings”)

Once you've implemented the INotifyPropertyChanged interface on your class, you can notify listeners when a property's value has changed by raising the PropertyChanged event, passing it a PropertyChangedEventArgs object containing the name of the property being updated. The following example demonstrates the code that you would use to notify the user interface that the value of a property named Name has changed:

private string _name;

public string Name
{
    get { return _name; }
    set
    {
        _name = value;

        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Name"));
    }
}

It's important to note that the PropertyChanged event is never raised when you use automatically implemented properties. For example, properties implemented in the following fashion do not raise the PropertyChanged event on the class, even when that class implements the INotifyPropertyChanged interface:

public string Name { get; set; }

Therefore, the user interface will not be aware of any changes to the value of that property that it did not make itself and will not be updated when the underlying property value is changed. Unfortunately, this means that you must implement the getter and setter for the property and maintain the property's value in a member variable. You can then raise the PropertyChanged event in the setter as required.

images Note Some Visual Studio extensions enable you to easily turn an automatically implemented property into a property with a backing store (a member variable that maintains the property's value) and raise the PropertyChanged event for that property. Choosing this option saves you from having to hand-code the getters and setters. Alternatively, try searching the Web for “INotifyPropertyChanged snippets”—you will find a multitude of snippets to help you create your properties accordingly and that you can customize to suit your needs.

The INotifyPropertyChanged interface is very commonly used, and many developers add an OnPropertyChanged (or similarly named) method that raises the PropertyChanged event, rather than actually raising the event in the property setters. This simplifies the property setters, as you do not have to check that the PropertyChanged event is not null, which it will be if nothing is handling the event, before calling it.

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

In the property setter now, you can simply use a single line of code to raise the event:

OnPropertyChanged("Name");

This makes your property setters somewhat neater, although there is still the unfortunate requirement to pass the method the name of the property as a string. This requirement can result in situations where the property name is different from the string. For example, the string may misspell the property name, or the property name may have been refactored but the string was not updated accordingly. These issues can lead to particularly difficult bugs to identify and track down. In the following sections, we'll look at some other alternatives that can avoid these issues. Which approach you choose will come down to personal taste.

Using Reflection Instead of Magic Strings

You can use reflection to get the name of the property in its setter (ignoring the first four characters, set_) and pass that to the OnPropertyChanged method:

OnPropertyChanged(MethodBase.GetCurrentMethod().Name.Substring(4));

images Note For this code to compile, at the top of your file you will need a using directive to the System.Reflection namespace.

Using Reflection to Validate Magic Strings in Debug Mode

Alternatively, you may wish to continue with hard-coding the property name as a string and check in your OnPropertyChanged method that the property exists. The best idea is to perform this check in a separate method and call that method from the OnPropertyChanged method, as you can then decorate it with the Conditional attribute and have it run only when in debug mode:

[Conditional("DEBUG")]
private void EnsurePropertyExists(string propertyName)
{
    PropertyInfo propInfo = this.GetType().GetProperty(propertyName);
    Debug.Assert(propInfo != null, "The property " + propertyName + " does not " +
                                   "exist on this class");
}

images Note For this method to work, you will need a using directive to both the System.Reflection namespace and the System.Diagnostics namespace.

Using Lambda Expressions Instead of Magic Strings

Yet another method is to use a lambda expression to specify the property name (instead of a string) and extract the name of the property from that. This popular method is faster than reflection and is refactoring safe (no magic strings). Start by adding the following using directive to the top of your class:

using System.Linq.Expressions;

Now, define the following method in your class (or a base class):

private string GetPropertyName<T>(Expression<Func<T>> property)
{
    MemberExpression expression = property.Body as MemberExpression;
    return expression.Member.Name;
}

images Note Generally, you would put the preceding method in the object's base class. Alternatively, you might like to define it as an extension method, as described by Jeremy Likeness at http://csharperimage.jeremylikness.com/2010/06/tips-and-tricks-for-inotifypropertychan.html.

You can then call the OnPropertyChanged method from your property's setter using the following code, where Name is the name of the property:

OnPropertyChanged(GetPropertyName(() => Name));

Emiel Jongerius has written up some other alternatives, including another way of implementing this method that also sets the property's value, and how they compare performance-wise, at www.pochet.net/blog/2010/06/25/inotifypropertychanged-implementations-an-overview. Einar Ingebrigtsen also has a blog post with an extended version of this code that provides additional functionality at www.ingebrigtsen.info/post/2008/12/11/INotifyPropertyChanged-revisited.aspx.

Using Notify Property Weaver

Arguably the cleanest and simplest method of implementing property change notifications is using Notify Property Weaver. Notify Property Weaver is a Visual Studio extension created by Simon Cropp that removes the need for you to raise the PropertyChanged event in property setters. Your classes simply need to implement the INotifyPropertyChanged interface, and it handles the rest. It hooks into the compilation process, and “weaves” the required code into the application's IL code.

Benefits of this approach include the following:

  • You get cleaner code. Without the need to raise property changed notifications in the property setters, you can now simply implement automatic properties, vastly reducing the amount of code you need to write.
  • It doesn't require your classes to inherit from a base class.
  • It's refactoring safe.
  • There's no need for attributes on properties, which other IL weavers tend to require. However, you can use them to have more control over what is generated if you wish.

Let's look at how you can use this tool.

  1. Get the Notify Property Weaver extension from the Visual Studio Extension Gallery (Tools images Extension Manager). Do a search for it by its name.
  2. With your Silverlight project as the active project, go to Project images NotifyPropertyWeaver images Configure.
  3. A window will appear, allowing you to configure the IL weaving for the project. If you want to have more control over what is generated, select the IncludeAttributeAssembly check box on the Attribute Assembly tab.
  4. Click OK. Your project will need to reload, but then it's all configured and you're ready to write code. The tool will now be configured as a build task. As long as your class implements the INotifyPropertyChanged interface, the tool will generate property changed notifications for each property in the class.

More information on Notify Property Weaver can be found at the project's web site: http://code.google.com/p/notifypropertyweaver/.

images Note Various other solutions have been created to solve the property change notifications problem that we won't go into here, but you might like to investigate. Oren Eini has another interesting method of handling the INotifyPropertyChanged problem that you may like, where he has created an Observable<T> class. He has blogged about it at http://ayende.com/Blog/archive/2009/08/08/an-easier-way-to-manage-inotifypropertychanged.aspx. Justin Angel has yet another interesting method where he automatically implements INotifyPropertyChanged using Mono.Cecil and PostSharp at justinangel.net/AutomagicallyImplementingINotifyPropertyChanged.

Implementing the IEditableObject Interface

IEditableObject is another useful interface to implement in your objects (also found in the System.ComponentModel namespace). It is designed to enable you to cancel and roll back any changes made to an object (essentially an undo action). Any changes made to the object are tracked using a start/complete-type transaction that can be cancelled. When a transaction is cancelled, any changes made to the object since the beginning of the transaction are rolled back, and the object's original state is reinstated.

Implementing the IEditableObject interface requires your class to implement three methods: BeginEdit, EndEdit, and CancelEdit. The BeginEdit method should be called before any changes are made to the object, generally via a data entry form, and the EndEdit method should be called once those changes have been made and are to be committed.

Implementing the BeginEdit Method

In the BeginEdit method, you will need to write code to capture the state of the object at that time—that is, generally all the property values. The easiest way to implement this is using the MemberwiseClone method on the object to take a shallow copy of the state of the object:

private Product _originalState = null; // Member variable

public void BeginEdit()
{
    _originalState = this.MemberwiseClone() as Product;
}

Implementing the EndEdit Method

You can then discard this state in the EndEdit method, because any changes made to the object since the BeginEdit method was called will have been accepted (committed):

public void EndEdit()
{
    _originalState = null;
}

Implementing the CancelEdit Method

In the CancelEdit method, you need to restore the original state of the object as it was taken when the BeginEdit method was called, copying that state back to the current instance of the object. You can implement this method in one of two ways—either by manually assigning the values from the “backup” object or by using reflection.

images Note The reflection method is more reusable because it takes a more generic approach, but it is slower to execute.

Manually Assigning Property Values

You can have full control over what property values are reinstated by assigning the values back to the object's properties manually, either via the property setters or directly to each member variable backing the corresponding property being reinstated. Note that if you use the property setters, each will execute any code within itself, including the property-changed notifications and validation rules—behavior you may or may not want.

public void CancelEdit()
{
    if (_originalState != null)
    {
        Name = _originalState.Name;
        ProductNumber = _originalState.ProductNumber;
        // Etc...

        _originalState = null;
    }
}

images Note If you choose to bypass the property setters, you may need to revalidate your object after reinstating the property values. (Validation is discussed later in this chapter.)

Assigning Property Values Using Reflection

Alternatively, you can use a generic method that copies the values of all the public properties on the originalState object back to the current instance using reflection:

public void CancelEdit()
{
    if (_originalState != null)
    {
        PropertyInfo[] objectProperties =
            this.GetType().GetProperties(BindingFlags.Public |
                                         BindingFlags.Instance);

        foreach (PropertyInfo propInfo in objectProperties)
        {
            object originalValue = propInfo.GetValue(_originalState, null);
            propInfo.SetValue(this, originalValue, null);
        }

        _originalState = null;
    }
}

images Note The preceding method executes any code in the property setters.

Using the IEditableObject Methods

Both the DataForm and DataGrid controls have built-in support for calling the BeginEdit, EndEdit, and CancelEdit methods on bound objects that implement the IEditableObject interface.

The DataForm control will call the BeginEdit method when changes are first made to the bound object via the bound controls, and commit them by calling the EndEdit method either when the user navigates away from the current object, or when the user explicitly commits the changes by clicking the OK button when the DataForm's AutoCommit property is set to False. As discussed earlier in this chapter, when a DataForm control is bound to an object that implements the IEditableObject interface, a Cancel button will appear in the data entry form. Clicking this Cancel button will call the CancelEdit method on the object to return it to its original state.

The DataGrid also calls the BeginEdit method when changes are first made to a row and commits them by calling the EndEdit method when the user navigates away from the current row. Pressing the Esc key when editing a row will call the CancelEdit method on the object to return it to the state it was in prior to its being edited.

Adding Calculated Properties to Your Classes

Often, you may wish to display a calculated value in your user interface based upon the values of properties on an object. For example, you may have an object entity representing an invoice line and want to display the total amount of that line (quantity × unit price). Alternatively, you may wish to have a property that displays the full name of a person, built by concatenating their first name and surname, separated by a space.

If you have created the class on the client side (such as a ViewModel), you can simply add a new property to the class that performs the calculation. Your user interface controls can then bind to this property and display the calculated value.

public decimal LineTotal
{
    get
    {
        return Quantity * UnitPrice;
    }
}

If you are binding directly to entities exposed from the server via RIA Services, you can add calculated properties to these entities too. You should never modify the code generated by RIA Services, but each of the generated entities is a partial class, so you can simply create a corresponding partial of your own (in the client-side project) that extends the entity and adds the required calculated properties in the same way as was previously demonstrated.

You may be wondering how you can notify the user interface that the LineTotal property's value has changed, when the Quantity or UnitPrice properties are updated. If you shouldn't modify the code generated by RIA Services, where the Quantity or UnitPrice properties are defined, how can you notify the bindings that the value of the calculated property will have also changed? There are a number of options, but the best method is to extend one of the two partial methods that RIA Services has created for each property, which are called when their values are changing. For example, a UnitPrice property on an entity will have two corresponding methods: OnUnitPriceChanging (called before the value has changed) and OnUnitPriceChanged (called after the value has changed). You can extend the Changed method and notify the bindings of the property value change like so:

partial void OnListPriceChanged()
{
    OnPropertyChanged(new PropertyChangedEventArgs("LineTotal"));
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset