Whilst developing a LOB Silverlight application, one of our testers pointed out that single character changes were not getting 'saved'. This seemed odd as we were just doing ordinary Databinding, albeit through various presenters and ViewModels. Interestingly I spotted a few articles that seemed to be described the issue we were seeing:
http://silverlight.net/forums/p/35136/105682.aspx
http://forums.lhotka.net/forums/thread/28597.aspx
http://karlshifflett.wordpress.com/2008/04/12/silverlight-2-lostfocus-and-data-binding-bugs/
To investigate the cause I reduced the example down to barebones.
Page.xaml
<UserControl x:Class="SilverlightApplication3.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<StackPanel x:Name="LayoutRoot" Background="White">
<TextBlock Margin="5" Text="{Binding Forename}"/>
<TextBlock Margin="5" Text="{Binding Surname}"/>
<TextBox Margin="5" Text="{Binding Forename, Mode=TwoWay}"/>
<TextBox Margin="5" Text="{Binding Surname, Mode=TwoWay}"/>
</StackPanel>
</UserControl>
namespace SilverlightApplication3
{
public partial class Page : UserControl
{
private Person _model;
public Page()
{
InitializeComponent();
Person person = new Person() { Forename = null, Surname = null };
this.DataContext = person;
person.Forename = "Andy";
person.Surname = "Boyne";
}
public Person Model
{
get { return _model; }
set
{
if(value!=_model)
{
_model = value;
DataContext = _model;
}
}
}
}
public class Person : INotifyPropertyChanged
{
private string _forename;
private string _surname;
public event PropertyChangedEventHandler PropertyChanged;
public string Forename
{
get { return _forename; }
set
{
if(value!=_forename)
{
_forename = value;
RaisePropertyChanged("Forename");
}
}
}
public string Surname
{
get { return _surname; }
set
{
if (value != _surname)
{
_surname = value;
RaisePropertyChanged("Surname");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
If you run the sample, and then enter a single character change into either textbox you will notice the binding fails to update. Enter more characters and the binding seems to work again.
In the example above the DataContext it is set in the constructor – but the problem still exists regardless of where this is set (e.g. it could be set externally via a presenter)
The Workaround
Dispatcher.BeginInvoke(() => this.DataContext = _model);
Setting the DataContext via the Dispatcher now causes single character changes to update the bindings.