EndEdit equivalent in WPF

2019-04-29 07:40发布

问题:

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,

回答1:

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



回答2:

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.



回答3:

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.



回答4:

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.



回答5:

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



回答6:

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();