9

I have a WPF Window that contains a TextBox. I have implemented a Command that executes on Crtl-S that saves the contents of the window. My problem is that if the textbox is the active control, and I have newly edited text in the textbox, the latest changes in the textbox are not commited. I need to tab out of the textbox to get the changes.

In WinForms, I would typically call EndEdit on the form, and all pending changes would get commited. Another alternative is using onPropertyChange binding rather than onValidation, but I would rather not do this.

What is the WPF equivalent to EndEdit, or what is the pattern to use in this type of scenario?

Thanks,

Thies
  • 4,101
  • 2
  • 31
  • 37

6 Answers6

6

Based on Pwninstein answer, I have now implemented an EndEdit in my common class for WPF Views / Windows that will look for bindings and force an update on them, code below;

Code below;

private void EndEdit(DependencyObject parent)
{
    LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();
    while (localValues.MoveNext())
    {
        LocalValueEntry entry = localValues.Current;
        if (BindingOperations.IsDataBound(parent, entry.Property))
        {
            BindingExpression binding = BindingOperations.GetBindingExpression(parent, entry.Property);
            if (binding != null)
            {
                binding.UpdateSource();
            }
        }
    }            

    for(int i=0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(parent, i);
        this.EndEdit(child);
    }
}

protected void EndEdit()
{
    this.EndEdit(this);
}

In my Save command, I now just call the EndEdit method, and I don't have to worry about other programmers selection of binding method.

Mark Avenius
  • 13,679
  • 6
  • 42
  • 50
Thies
  • 4,101
  • 2
  • 31
  • 37
  • Edited my previous answer to include my comments. Thanks! Glad that helped :) – Mark Carpenter May 20 '09 at 19:52
  • I upvoted this. Works great, but causes a problem with virtualization turned on. Changing ItemSource cause scrollbars to stop working. See: [http://stackoverflow.com/questions/8107618/datagrid-scroll-bar-stops-working](http://stackoverflow.com/questions/8107618/datagrid-scroll-bar-stops-working) – CrazyIvan1974 Feb 04 '16 at 15:45
  • Something like this prevents the scrollbar problem: var updateSourceTrigger = binding.ParentBinding.UpdateSourceTrigger; if (updateSourceTrigger == UpdateSourceTrigger.Default) { var metadata = entry.Property.GetMetadata(element) as FrameworkPropertyMetadata; if (metadata != null) updateSourceTrigger = metadata.DefaultUpdateSourceTrigger; } if (updateSourceTrigger != UpdateSourceTrigger.PropertyChanged) binding.UpdateSource(); – Mark Cranness Aug 16 '18 at 07:45
5

To avoid the issue of needing to tab away, you could simply change the UpdateSourceTrigger property of your controls' binding. Try the following:

<TextBox.Text>
    <Binding Path="MyProperty" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>

This tells WPF to update the backing object whenever the Text property is changed. This way, you don't need to worry about tabbing away. Hope this helps!

EDIT:

The accepted answer for the following SO question provides a way to automatically run validation rules for a page. You could modify it to call UpdateSource() on all BindingExpression objects instead.

Link

Community
  • 1
  • 1
Mark Carpenter
  • 17,445
  • 22
  • 96
  • 149
  • I would preferably not have to add the updateSourceTrigger to all the textboxes in my form, one call to make sure all pending changes are 'commited' is what I am looking for. Your suggestion is like the 'OnPropertyChange' binding. It is doable, but also causes more events than I would like to see. I am looking for a generic WPF pattern to solving this type of problem. Thank you for answering. – Thies May 20 '09 at 19:10
5

You can force specific bindings to update using code like the following:

var bindingExpression = txtInput.GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();

Doing this more generally is difficult because there is no generic way to get all bindings, nor would you necessarily want them all to updated.

David Nelson
  • 3,666
  • 1
  • 21
  • 24
  • That would probably work too. The accepted answer for the following SO question provides a way to automatically run validation rules for a page. I think you could modify it to call UpdateSource() on all BindingExpression objects instead. http://stackoverflow.com/questions/127477/detecting-wpf-validation-errors – Mark Carpenter May 20 '09 at 18:12
  • I am looking for a generic solution that does not have to make me change the binding on all forms. Something that commits all on-going edits. Thank you for answering. – Thies May 20 '09 at 19:13
  • This is exactly what I was looking for because I have some Attachable Properties will commands on them that require the text to be up to date. – Sellorio Sep 26 '14 at 02:59
3

I disagree w/ArielBH. The issue here is the interplay between keyboard and logical focus, and unless you have changed all your Data Binding update triggers to PropertyChanged, you may miss some source data updates in certain scenarios (e.g. Toolbar button clicks). For instance, the default update trigger for TextBox.Text is LostFocus and clicking on a toolbar button does not blur the active TextBox focus.

If you have some mechanism to register the controls, then you could explicitly force the data binding to update the source in the same place you'd be calling EndEdit in a WinForms app. It's not neat or elegant, but it gets the job done.

If somebody has come up with a better solution, I'd be all ears as well.

micahtan
  • 18,530
  • 1
  • 38
  • 33
  • 1
    See the link I posted in my comment to commongenius' answer for an example of how one could go about forcing bindings to update. Granted, the example shown doesn't do exactly that, but could be easily modified to do just what you're suggesting. :) – Mark Carpenter May 20 '09 at 18:37
1

I believe you are supposed to declare a binding group and then reference that binding group in code. I put mine on the root Window element so it gets the bindings of all the controls on the Window.

<Window.BindingGroup>
    <BindingGroup />
</Window.BindingGroup>

this.BindingGroup.CommitEdit();
dalchri
  • 11
  • 1
1

HI, Well, while using WPF, one needs to adopt to a different mindset.

I would basically bind the TextBox's Text property to one of my properties (Model, ViewModel, Code-Behind, whatever makes you happy). So when you handle CTRL+S you just go to the clr property that is binded and continue happily with all the data you want.

Hope that help you, if you require code examples, leave me a comment. Ariel

ArielBH
  • 1,991
  • 3
  • 22
  • 38