Explicitly update binding sources
In WPF data binding scenarios, the binding source is usually updated implicitly – if you, for example, edit the text of a bound TextBox
control, the underlying binding source is updated a soon as the control loses focus.
This is something you might want to prevent sometimes – a common scenario is a model dialog that provides a Cancel button to abort changes. The basic idea is that the underlying data remains unchanged until you commit all your changes at once if the dialog’s OK button is clicked.
The solution to prevent automatic updates of the binding source is to set the UpdateSourceTrigger property
to Explicit
, which prevents automatic updates of the underlying data item:
<TextBox Text="{Binding Path=Name, UpdateSourceTrigger=Explicit}" />
However, this feature comes with a catch: You’ll have to trigger updates on all your controls manually. Beatriz Costa posted a generic solution, but I found it a bit tedious to explicitly update every single control on my dialog. As a result, I came up with a solution that recursively processes a visual tree from a given starting point and updates all controls that provide one or several bindings of your choice:
/// <summary> /// Recursively processes a given dependency object and all its /// children, and updates sources of all objects that use a /// binding expression on a given property. /// </summary> /// <param name="obj">The dependency object that marks a starting /// point. This could be a dialog window or a panel control that /// hosts bound controls.</param> /// <param name="properties">The properties to be updated if /// <paramref name="obj"/> or one of its childs provide it along /// with a binding expression.</param> public static void UpdateBindingSources(DependencyObject obj, params DependencyProperty[] properties) { foreach (DependencyProperty depProperty in properties) { //check whether the submitted object provides a bound property //that matches the property parameters BindingExpression be = BindingOperations.GetBindingExpression(obj, depProperty); if (be != null) be.UpdateSource(); } int count = VisualTreeHelper.GetChildrenCount(obj); for(int i=0; i<count; i++) { //process child items recursively DependencyObject childObject = VisualTreeHelper.GetChild(obj, i); UpdateBindingSources(childObject, properties); } }
Using the above method is quite simple: Just set a starting point (window, grid, whatever) along with an arbitrary number of dependency properties you’d like to have processed. As an example: The snipped below commits all bound Text
properties of a dialog’s TextBox
controls as well bound items of XamComboEditor
dropdown controls:
//update TextBox and ComboBox controls of the dialog DependencyProperty dpText = TextBox.TextProperty; DependencyProperty dpSelectedItem = XamComboEditor.SelectedItemProperty; UpdateBindingSources(this, dpText, dpSelectedItem);
I’ve updated your code so you don’t need to pass DependencyProperty[] properties.
Good post! I was having this exact issue and your solution seems to work just fine for me.
Awesome Job!! I used your code to do a general refresh of bindings at program startup – in order to display the results of validation w/o requiring the user to ‘invalidate’ each entry (and w/o requiring me to list out each control and do a Binding UpdateSource()… thanks again.
Hi. I tried using the solution and though it does work on controls like TextBox and such , it is not updating the bindings that i have on a ListView items.
Splendid! thanks Stefan also!
Thank you very much for this post, and also to Stefan Olson. Such a “bulk” functionality saves me of much tedious work. It makes an excellent bridge for getting into WPF – keeping me from the “F” (Framework overhead) in it, so I may still use the oldschool imperative “Fetch input / Show output” approach.
For this, I found it convenient to also have the values transport work the opposite direction.
public static class DependencyObjectExtension
{
///
/// Gets Values from View and stores them in the POCO data object
///
///
public static void UpdateSource(this DependencyObject obj)
{
obj.UpdateBinding(true);
}
///
/// Gets Values from the POCO object and shows them in the View’s controls
///
///
public static void UpdateTarget(this DependencyObject obj)
{
obj.UpdateBinding(false);
}
public static void UpdateBinding(this DependencyObject obj, bool toSource=true)
{
IEnumerable properties =obj.EnumerateDependencyProperties();
foreach (DependencyProperty depProperty in properties)
{
//check whether the submitted object provides a bound property
//that matches the property parameters
BindingExpression be =
BindingOperations.GetBindingExpression(obj, depProperty);
if (be != null)
if (toSource)
be.UpdateSource();
else
be.UpdateTarget();
}
int count = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < count; i++)
{
//process child items recursively
DependencyObject childObject = VisualTreeHelper.GetChild(obj, i);
UpdateBinding(childObject,toSource);
}
}
public static IEnumerable EnumerateDependencyProperties(this DependencyObject element)
{
LocalValueEnumerator lve = element.GetLocalValueEnumerator();
while (lve.MoveNext())
{
LocalValueEntry entry = lve.Current;
if (BindingOperations.IsDataBound(element, entry.Property))
{
yield return entry.Property;
}
}
}
}