Browse by Tags

All Tags » inotifypropertychanged   (RSS)

The biggest problem with data-binding is the requirement to implement the INotifyPropertyChange interface. There are dozens of solutions out there that try to simplify the process with techniques ranging from parsing lambda expressions and walking the stack frame to using IL weaving to modify classes at compile time. The most popular approach is to derive from a base class and call a base method to handle the event.

The frustration often comes from mapping data objects that don't implement the interface to view models that do. Wouldn't it be nice to have a simple, straightforward way to manage this without duplicating properties and writing tedious mapping code? It turns out there is.

For this particular problem, I started with the solution. Given a model, say, a ContactModel, I wanted to be able to do this:

public PropertyNotifier<ContactModel> Contact { get; set; } 
public void SetContact(ContactModel contact) 
{
   Contact = new PropertyNotifier(contact); 
}

In other words, a nice type would wrap the object and expose it with full property change glory, and little effort on my part.

So, where to start? To begin with I created a simple base class that allows for property change notification. For now I'm going to ignore some of the interesting ways to actually call the notification.

public abstract class BaseNotify : INotifyPropertyChanged 
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChange(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

This class is really all you need to have your own MVVM framework. Next is some heavy lifting. Because the solution uses dynamic types and heavy reflection, it will not work on Windows Phone 7. It will, however, work with Silverlight 4, and there is perhaps an even more elegant solution to be derived from this work in Silverlight 5 by adding ICustomTypeProvider to the mix.

How can this create a bindable object in Silverlight 4 or 5? First, create the shell of the view model. It should create the proxy class with property change notification. It should allow you to pass in a template and have that template mirrored by the proxy. Ideally, it should be easy to get the template back out (i.e. yank out the original model to send on its way after it has been modified). Here's the start:

public class PropertyNotifier<TTemplate> : BaseNotify where TTemplate : class
{
   public TTemplate Instance { get; set; }
   public INotifyPropertyChanged NotifyInstance { get; set; }
}    

Simple enough. Not sure if the notifier instance really deserves a public setter... but it is there for now. Now comes the fun part!

The type must be created on the fly, so it needs a dynamic assembly and module to host the type. There is no sense in creating a new one for each type, so these can be static properties that live on the notifier. There should also be a type dictionary to map the source type to the proxy type (to avoid recreating the proxy type) and a mutex to avoid collisions with the dictionary (thread safety).

private static readonly ModuleBuilder _builder;
private static readonly Dictionary<Type, Type> _types = new Dictionary<Type, Type>();
private static readonly object _mutex = new object();        

static PropertyNotifier()
{
    var assemblyName = new AssemblyName("PropertyNotifier");
    var currentDomain = AppDomain.CurrentDomain;
    var builder = currentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);

    _builder = builder.DefineDynamicModule("PropertyChangeModels");
}

If you are afraid of collisions you can give the assembly a creative name like a GUID or append random text and strings if you like. This makes it nice and readable in the debugger. The assembly is created in the current domain and the module defined to host dynamic types.

Without understanding the details of how the type is actually built, you can still wire in the constructor and put in a placeholder, like this:

public PropertyNotifier()
{
    Monitor.Enter(_mutex);
    try
    {
        if (!_types.ContainsKey(typeof (TTemplate)))
        {
            _types.Add(typeof(TTemplate), _BuildType()); 
        }                                
    }
    finally
    {
        Monitor.Exit(_mutex);
    }

    var type = _types[typeof (TTemplate)];
            
    NotifyInstance = (INotifyPropertyChanged)Activator.CreateInstance(type);                    
}

public PropertyNotifier(TTemplate instance) : this()
{
    Instance = instance;
}

If the type has not been created, it is built. An overloaded constructor will take in an instance and then set it.

Next, assuming the type is built (we'll get into the gory details later), a few methods will help with mapping properties. First, define a delegate for the getter and setter. Then, define a dictionary of dictionaries. The key to the outer dictionary will be the type, and the inner dictionary will map the property name to the getter or setter method.

private delegate void Setter(object target, object value);

private delegate object Getter(object target);

private static readonly Dictionary<Type, Dictionary<string,Setter>> _setterCache = new Dictionary<Type, Dictionary<string,Setter>>();
private static readonly Dictionary<Type, Dictionary<string,Getter>> _getterCache = new Dictionary<Type, Dictionary<string, Getter>>();

The helper methods will inspect the type for the property information and use reflection to grab the getter or setter. They will then store these in the cache for future look ups:

private static object _GetValue(object target, string property)
{
    Monitor.Enter(_mutex);
    try
    {
        if (!_getterCache[target.GetType()].ContainsKey(property))
        {
            var method = target.GetType().GetProperty(property).GetGetMethod();
            _getterCache[target.GetType()].Add(property, obj => method.Invoke(obj, new object[] {}));
        }
    }
    finally
    {
        Monitor.Exit(_mutex);
    }

    return _getterCache[target.GetType()][property](target);                
}

private static void _SetValue(object target, string property, object value)
{
    Monitor.Enter(_mutex);
    try
    {
        if (!_setterCache[target.GetType()].ContainsKey(property))
        {
            var method = target.GetType().GetProperty(property).GetSetMethod();
            _setterCache[target.GetType()].Add(property, (obj,val) => method.Invoke(obj, new[] { val }));
        }
    }
    finally
    {
        Monitor.Exit(_mutex);
    }

    _setterCache[target.GetType()][property](target, value);
}

You can call the first with an object and the property name to get the value. Call the second with the object, the property name, and the property value to set it. Subsequent calls will not require inspection of the properties as the methods will be cached to call directly (and if you really want to speed it up, look into dynamic methods).

So the proxy still hasn't been built yet, but that's more complicated. First, get the simple stuff out of the way. When the instance is passed in, automatically wire the properties to the proxy. When the proxy is created, hook into the property change notificaton to automatically push changes back to the original instance:

private TTemplate _instance;

// original object
public TTemplate Instance
{
    get { return _instance; }
    set
    {                
        _instance = value;
        NotifyInstance = (INotifyPropertyChanged)Activator.CreateInstance(_types[typeof (TTemplate)]);

        foreach(var p in typeof(TTemplate).GetProperties())
        {
            var sourceValue = _GetValue(value, p.Name);
            _SetValue(NotifyInstance, p.Name, sourceValue);
        }

        RaisePropertyChange("Instance");
    }
}

// proxy object
private INotifyPropertyChanged _notifyInstance;

public INotifyPropertyChanged NotifyInstance
{
    get { return _notifyInstance; }
    set
    {
        if (_notifyInstance != null)
        {
            _notifyInstance.PropertyChanged -= _NotifyInstancePropertyChanged;
        }

        _notifyInstance = value;
        _notifyInstance.PropertyChanged += _NotifyInstancePropertyChanged;

        RaisePropertyChange("NotifyInstance");                
    }
}

void _NotifyInstancePropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (Instance == null)
    {
        return;
    }           

    if (_setterCache[typeof (TTemplate)].ContainsKey(e.PropertyName))
    {
        _SetValue(Instance, e.PropertyName, _GetValue(NotifyInstance, e.PropertyName));
    }
}

OK, all of the proxy and marshalling is in place. Now it's time to build the type! First step is to define the type name and set the parent so it derives from the BaseNotify object:

private static Type _BuildType()
{
    var typeBuilder =
        _builder.DefineType(string.Format("{0}Notifier", typeof (TTemplate).Name), TypeAttributes.Class | TypeAttributes.Public);

    typeBuilder.SetParent(typeof(BaseNotify));
}

Next, grab a handle to the property change method from the base class and set up a dictionary to cache the getters and setters on the template type:

var propertyChange = typeof(BaseNotify).GetMethod("RaisePropertyChange", new[] { typeof(string)});

_getterCache.Add(typeof(TTemplate), new Dictionary<string, Getter>());
_setterCache.Add(typeof(TTemplate), new Dictionary<string, Setter>());
                        

Now comes the fun part, looping through the properties and caching the getters/setters (this is from the template):

foreach(var p in typeof(TTemplate).GetProperties())
            {
                var getterInfo = p.GetGetMethod();
                _getterCache[typeof(TTemplate)].Add(p.Name, obj=>getterInfo.Invoke(obj, new object[]{}));

                var setterInfo = p.GetSetMethod();
                _setterCache[typeof(TTemplate)].Add(p.Name, (obj,value)=>setterInfo.Invoke(obj, new[]{value}));
}

Each property has a private backing field, so create the field on the proxy type:

var field = typeBuilder.DefineField(string.Format("_{0}", p.Name), p.PropertyType, FieldAttributes.Private);                

Next, define the property.

var property = typeBuilder.DefineProperty(p.Name, PropertyAttributes.HasDefault, p.PropertyType,null);

The property needs a getter. This is where the code is a little more interesting becaues it requires emitting IL code. Fortunately, you can build a sample class and use ILDASM.EXE to disassemble it and learn what the proper op codes are. Here is the getter method:

var getter = typeBuilder.DefineMethod(string.Format("get_{0}", p.Name),
    MethodAttributes.Public |
    MethodAttributes.SpecialName | 
    MethodAttributes.HideBySig,
    p.PropertyType, Type.EmptyTypes);
var getterCode = getter.GetILGenerator();

getterCode.Emit(OpCodes.Ldarg_0);
getterCode.Emit(OpCodes.Ldfld, field);
getterCode.Emit(OpCodes.Ret);

Next is the setter method. The setter method has some extra code that loads the property name and then calls the property change method. That is why the handle to the method was captured earlier.

var setter = typeBuilder.DefineMethod(string.Format("set_{0}", p.Name),  
    MethodAttributes.Public |
    MethodAttributes.SpecialName |
    MethodAttributes.HideBySig, null,
    new[] { p.PropertyType });

var setterCode = setter.GetILGenerator();

setterCode.Emit(OpCodes.Ldarg_0);
setterCode.Emit(OpCodes.Ldarg_1);
setterCode.Emit(OpCodes.Stfld, field);


// property change
// put the property name on the stack
setterCode.Emit(OpCodes.Nop);
setterCode.Emit(OpCodes.Ldarg_0);
setterCode.Emit(OpCodes.Ldstr, p.Name);
setterCode.Emit(OpCodes.Call, propertyChange);
setterCode.Emit(OpCodes.Nop);                

setterCode.Emit(OpCodes.Ret);

Now that the methods have been generated, they must be attached to the property:

property.SetGetMethod(getter);
property.SetSetMethod(setter);

That's the hard part! The easy part is to define a default constructor (calls down to the base) and create the actual type. Remember, this is the method called in the constructor so the type is returned and stored in the dictionary, then the activator is used to create the instance. Also, go ahead and set up the getter and setter cache:

typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);            
            
var type = typeBuilder.CreateType();
            
_getterCache.Add(type,new Dictionary<string, Getter>());
_setterCache.Add(type,new Dictionary<string, Setter>());
            
return type;

Believe it or not, that's what it takes to build a proxy, assuming the base class contains simple properties and no complex nested types or structures. Here's a simple template to test the proxy with:

public class ContactTemplate
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }        
}

Here's a view model that is based on the template. It uses the property notifier to wrap the properties with property change notification. It also creates a default template in the constructor just to give you some information to work with when the application runs:

public class ContactViewModel : PropertyNotifier<ContactTemplate>
{       
    public ContactViewModel()
    {
        var template = new ContactTemplate
                            {
                                Id = 1,
                                FirstName = "Jeremy",
                                LastName = "Likness"
                            };
        Instance = template;
    }       
}

Now some XAML to bind it all together:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.DataContext>
        <ViewModels:ContactViewModel/>
    </Grid.DataContext>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="First Name: "/>
    <TextBlock Text="Last Name: " Grid.Row="1"/>
    <TextBlock Text="Edit First Name: " Grid.Row="2"/>
    <TextBlock Text="{Binding NotifyInstance.FirstName}" Grid.Column="1"/>
    <TextBlock Text="{Binding NotifyInstance.LastName}" Grid.Row="1" Grid.Column="1"/>
    <TextBox Text="{Binding NotifyInstance.FirstName,Mode=TwoWay}" Grid.Row="2" TextChanged="TextBox_TextChanged" Grid.Column="1" Width="200"/>
</Grid>

When you run the application, you'll find the property change works just fine. Now, with this helper class, anytime you need to take a simple data object and implement property change, you can just wrap it in the property notifier and bind to the InstanceNotifier property. This works perfectly well in Silverlight 4.

Of course, I won't leave you hanging. Here's the source code to play with!

Jeremy Likness

This will be a shorter bit for an interesting topic. I've noticed a few posts and discussion around raising the property changed notification. This continues to be the sore spot for MVVM. It creates quite a bit of ceremony for what is a fairly common piece of functionality that shouldn't be so hard to create.

Jounce supports all of the common ways of doing this, but there is a third that I learned from a colleague of mine (Keith Rome) that I added and think bears some attention.

The most common approach is to use the "magic string" technique (which many developers automatically cringe at) and do something like this:

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged("FirstName");
    }
}

The problem there of course is that it is too easy to type the property wrong. Coders who typically "cut and paste" might forget to update this and then they'll find themselves raising the wrong property.

The next iteration types the property a bit and gets rid of magic strings. It does this by allowing a lambda expression to be passed in. The expression tree is parsed and the property name is extracted. Some extra validation can even make sure it's inside a property - but there is still no guarantee you aren't raising the wrong property, you just know you are raising some property.

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged(()=>FirstName);
    }
}

So the third method is a little more safe. Not only does it avoid magic strings, but I can get out of the ceremony of passing a weird lambda expression, and I can guarantee it will raise the notification for the correct property. Before I talk about how this works, let me show you the code:

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged();
    }
}

This will throw an exception if it's not in a setter. And it will always capture the current property. I still have to make a call, but no magic strings and we've reduced the ceremony somewhat. How does it work?

The trick is the context of execution. All of these methods have a little overhead (the string method actually has the best performance) but in most applications if the performance hit is in your setters, you've probably got worse issues farther up the stack.

Speaking of the stack, that's exactly what we use. We simply walk the stack frames up from the setter to find the name of the setter method itself. The convention for properties is set_propName so we look up where we are at and grab the propName.

Here's the code:

public virtual void RaisePropertyChanged()
{
    var frames = new System.Diagnostics.StackTrace();
    for (var i = 0; i < frames.FrameCount; i++)
    {
        var frame = frames.GetFrame(i).GetMethod() as MethodInfo;
        if (frame != null)
            if (frame.IsSpecialName && frame.Name.StartsWith("set_"))
            {
                RaisePropertyChanged(frame.Name.Substring(4));
                return;
            }
    }
    throw new InvalidOperationException("NotifyPropertyChanged() can only by invoked within a property setter.");
}

Notice once we extract the property name, we just pass it along to the method that takes a happy string.

There you have it - another way to do the deed with a little more overhead but a lot less ceremony.

Jeremy Likness

Silverlight 4 provides some very powerful data form capabilities out of the box. You will no longer have to throw an exception just to send a validation error or even worry about hard-coding labels. With support for data annotations, IDataErrorInfo and more, you now have plenty "out of the box" to work with.

Let's explore the surface of some of these powerful new features!

IDataErrorInfo

With support for IDataErrorInfo as well as INotifyDataErrorInfo, you no longer have to raise exceptions just to validate your classes. Fire up VS 2010 and create a new Silverlight 4 application. First, we'll create a "helper" class to derive our entities from in order to perform validation and notify property changes. The class looks like this:


public abstract class BaseEntity : INotifyPropertyChanged, IDataErrorInfo
{
    private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();

    protected void RemoveErrors(string prop)
    {
        if (_errors.ContainsKey(prop))
        {
            _errors.Remove(prop);
        }
    }

    protected void AddError(string prop, string error)
    {
        if (_errors.ContainsKey(prop))
        {
            _errors[prop].Add(error);
        }
        else
        {
            _errors.Add(prop, new List<string> { error });
        }
    }

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

    public event PropertyChangedEventHandler PropertyChanged;

    public virtual string Error
    {
        get { return string.Empty; }
    }

    public virtual string this[string columnName]
    {
        get 
        {
            System.Text.StringBuilder retVal = new System.Text.StringBuilder();

            if (_errors.ContainsKey(columnName))
            {
                bool first = true;
                foreach (string error in _errors[columnName])
                {
                    retVal.Append(error);
                    if (first)
                    {
                        first = false;
                    }
                    else
                    {
                        retVal.Append(Environment.NewLine);
                    }
                }
            }

            return retVal.ToString();
        }
    }
}

There are a few things going on here. First, we implement INotifyPropertyChanged in to facilitate binding. The protected OnPropertyChanged method allows derived classes to hook into this event.

Next, we implement IDataErrorInfo. This forces us to provide a single error property as well as an extension method that references errors for a given property. John Papa has an excellent article about building a handler for this at Enabling Validation in Silverlight 4 with IDataErrorInfo. In my case, I keep a dictionary referenced by the property name that holds a list in case there are multiple errors I want to show. When the errors are requested, I fold them into a single string using Environment.NewLine (OK, so the beta just came out the other day ... can't say I've fully tested that part).

Data Annotations

Now you can build a PersonInfo class based on this base class. To keep things simple for a contrived example, I chose just a first name, last name, and age. Here we'll need to add a reference to System.ComponentModel.DataAnnotations. You must install the latest Silverlight Toolkit for this. If you need to browse to it manually, it's located under the toolkit installation directory (usually c:\Program Files\Microsoft SDKs\Silverlight) under v4.0\Libraries\Client. Now we can derive the class and annotate it, like this:


public class PersonInfo : BaseEntity 
{
    private string _firstName, _lastName;

    private int _age;

    [Display(Name="First Name")]
    [Required]
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (string.IsNullOrEmpty(value.Trim()))
            {
                AddError("FirstName", "First name is required.");
            }
            else
            {
                RemoveErrors("FirstName");
                if (!value.Equals(_firstName))
                {
                    _firstName = value;
                    OnPropertyChanged("FirstName");
                }
            }
        }
    }

    [Display(Name="Last Name")]
    [Required]
    public string LastName
    {
        get { return _lastName; }
        set
        {
            if (string.IsNullOrEmpty(value.Trim()))
            {
                AddError("LastName", "Last name is required.");
            }
            else
            {
                RemoveErrors("LastName");
                if (!value.Equals(_lastName))
                {
                    _lastName = value;
                    OnPropertyChanged("LastName");
                }
            }
        }
    }

    [Display(Name="Age (Years)")]
    [Required]
    public int Age
    {
        get { return _age; }
        set
        {
            if (value  130)
            {
                AddError("Age", "Age must be a valid integer between 18 and 130.");
            }
            else
            {
                RemoveErrors("Age");
                if (!value.Equals(_age))
                {
                    _age = value;
                    OnPropertyChanged("Age");
                }
            }
        }
    }
}

You'll probably pull out validations into a handler / rule set but basically what I'm doing is validating the value, setting an error or clearing all errors if it passes, then calling the property changed event if the value truly changes. Note the use of annotations to specify required properties as well as "friendly names" for the properties.

Now that our class is prepped, we can get to work on the XAML.

Implicit Styles

With support for implicit styles, we can set a style based on a target type and style it that way - allowing for themes to be set externally. We'll take advantage of that to set the width for our TextBox controls. I also add a reference to the PersonInfo class to use in data binding, so I won't need to touch the code-behind for MainPage.xaml:


<UserControl.Resources>
   <local:PersonInfo x:Key="Person"/>
   <Style TargetType="TextBox">
      <Setter Property="Width" Value="200"/>
   </Style>
</UserControl.Resources>

Data Input Helpers

Next, add a reference to System.Windows.Controls.Data.Input (found in the toolkit as well, same folder as the data annotations). We'll reference it at the top of our XAML (along with the local reference we need for the PersonInfo class:


xmlns:local="clr-namespace:DataForm"
    xmlns:dataInput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input"

Great, now let's get to work! I'm going to show you the full snippet of XAML and then explain the pieces:


    <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource Person}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>           
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <dataInput:Label Target="{Binding ElementName=tbFirstName}"
                         Grid.Row="0" Grid.Column="0"/>
        <StackPanel Orientation="Horizontal" 
                    Grid.Row="0" Grid.Column="1">
            <TextBox x:Name="tbFirstName" 
                 Text="{Binding Path=FirstName, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, FallbackValue=''}"/>
            <dataInput:DescriptionViewer Description="Please enter your first name."
                                         Target="{Binding ElementName=tbFirstName}"/>
        </StackPanel>
        <dataInput:Label Target="{Binding ElementName=tbLastName}"
                         Grid.Row="1" Grid.Column="0"/>
        <StackPanel Orientation="Horizontal" 
                    Grid.Row="1" Grid.Column="1">
            <TextBox x:Name="tbLastName" 
                 Text="{Binding Path=LastName, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, FallbackValue=''}" />
            <dataInput:DescriptionViewer Description="Please enter your last name."
                                         Target="{Binding ElementName=tbLastName}"/>
        </StackPanel>
        <dataInput:Label Target="{Binding ElementName=tbAge}"
                         Grid.Row="2" Grid.Column="0"/>
        <StackPanel Orientation="Horizontal" 
                    Grid.Row="2" Grid.Column="1">
            <TextBox x:Name="tbAge" 
                     Text="{Binding Path=Age, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, FallbackValue='0'}"/>
            <dataInput:DescriptionViewer Description="Please enter a valid age between 18 and 130."
                                         Target="{Binding ElementName=tbAge}"/>
        </StackPanel>
        <dataInput:ValidationSummary Grid.Row="3" Grid.Column="0"
                                     Grid.ColumnSpan="2"/>        
    </Grid>

Some of these helper classes are made available via the toolkit for Silverlight 3 as well.

Labels

The label class lets us bind to an element like a textbox. It will pull the name of the databound property from the annotations and display it. If a validation error is thrown, it will turn red to further reinforce the error. If the field is tagged as required, the label will automatically bold itself to indicate a required field.

Learn more about the Label class

New Bindings

If you take a look at the textbox contorls, you'll see we've added new binding parameters. Specifically, ValidatesOnDataErrors (as opposed to exceptions) and FallbackValue (a value to use when databinding fails).

Learn more about bindings

Description Viewer

The description viewer control provides an easy way to show tips and hints and is similar to the ToolTip service. It will show an informational icon that, when the cursor hovers over, will provide a hint or description.

Learn more about the description viewer

Validation Summary

Finally, we add a validation summary control. The default behavior is to list all errors. When the user clicks on an error, it gives focus to the UI element that caused the error.

Learn more about the validation summary class

Putting it all Together

When you tie this all together and run it (without any additional code behind), this is an example of what you'll see:

Silverlight 4 Beta Data Forms

  • Note the labels automatically pulled from the DisplayAttribute on the entity class
  • The labels are bold due to the RequiredAttribute annotation
  • The labels on properties with errors are colored red
  • I clicked on the information glyph next to the first name text box and was the hint to enter my first name
  • I clicked on the red triangle in the upper right corner of the text box for age and was shown the error that my age is not in the valid range
  • The ValidationSummary control has summarized my errors (if I click on one, I'll be taken to the control with the issue)

As you can see, that's quite a bit of functionality right out of the box, and it allows a nice, clean separation of things like attributes and descriptions from the user interface that presents them.

Jeremy Likness