This came up on Twitter today and since I have a solution that's been working well for me I opted to share. When making a series of data changes that will update Bindings, How can you keep a WPF/Silverlight UI from reflecting any of these changes until you're all done? In other words, we need the ability to treat a series of Property changes as a single unit of work.
While this works better with the design patterns from my series "Great Features for MVVM Friendly Objects" I will define a more general mechanism here.
Step 1 - Define a mechanism for not firing PropertyChanged
The first step is to give your data objects a means of not firing updates. We'll create a simple base class
public class SampleDataObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Don't fire updates!
/// </summary>
public bool SuspendChangeNotification { get; set; }
protected void OnPropertyChanged(string propName)
{
if (null != PropertyChanged && !SuspendChangeNotification)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Step 2 - Define Reasonable Programming Semantics for Context Boundaries
Whenever I'm solving a problem I always think about the Developer Experience. Is this going to be so annoying or hard to use that people will seek reasons to avoid using it? Since we want to define a context with a clear beginning and end I like to see if the problem can be expressed with using() semantics. It turns out that this one can be. We'll create an object that does the work we need to do and implements IDisposable so it can be used in a using() {} block:
public class SuspendChangeNotifyContext: IDisposable
{
public SuspendChangeNotifyContext(ISupportInitialize target) : this(new List<ISupportInitialize>{target})
{
}
public SuspendChangeNotifyContext(IEnumerable<ISupportInitialize> targets)
{
_targets = targets;
foreach (var t in _targets)
{
t.BeginInit();
}
}
IEnumerable<ISupportInitialize> _targets;
public void Dispose()
{
foreach (var t in _targets)
{
t.EndInit();
}
}
}
This means that our SampleDataObject needs to implement ISupportInitialize, which causes us to implement methods for BeginInit() and EndInit(). I chose this because this interface is already in the Silverlight libraries. You could certainly make your own interface that looks directly at some other property or methods
Read more: Damon Payne
0 comments:
Post a Comment