12

在以下简单的 WPF 应用程序中,aTextBox设置为在该控件失去焦点时更新属性,如下所示

<DockPanel>
    <ToolBar DockPanel.Dock="Top">
        <Button>Test</Button>
    </ToolBar>
    <TextBox Text="{Binding MyString}" />
</DockPanel>

public MainWindow()
{
    InitializeComponent();
    DataContext = this;
}

public string MyString
{
    get { return _myString; }
    set { _myString = value; }
}

但是,当我运行此应用程序时,在文本框中输入一些文本,然后单击“测试”按钮,我的MyString属性上的断点没有引发,也没有引发事件的任何事件处理程序FocusLost。这些事件仅在通过其他方式(例如关闭窗口)从控件中丢失焦点时引发。

这给我们带来了问题,因为实际上“测试”按钮包含依赖于MyString正在更新的属性的逻辑。

FocusLost当我单击“测试”按钮时,如何确保正确引发事件并更新绑定?看起来问题是由使用 引起的ToolBar,因为用ToolBar标准按钮替换 不会导致这种行为。

4

4 回答 4

5

在这种情况下,文本框实际上并没有失去逻辑焦点,因此永远不会引发事件 - 本质上我实际上想要的是LostKeyboardFocus事件,而不是LostFocus触发更新的事件。

This issue is similar to WPF: Data bound TabControl doesn't commit changes when new tab is selected and there is a Microsoft connect item for it here with a number of potential solutions, however I fixed this using an attached property like so.

public static readonly DependencyProperty BindOnLostKeyboardFocusProperty =
    DependencyProperty.RegisterAttached("BindOnLostKeyboardFocus", typeof(bool), typeof(MainWindow), new PropertyMetadata(default(bool), BindOnLostKeyboardFocusChanged));

private static void BindOnLostKeyboardFocusChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    var control = o as UIElement;
    if (control != null)
    {
        if ((bool) e.NewValue)
        {
            control.AddHandler(LostKeyboardFocusEvent, new RoutedEventHandler(ControlLostKeyboardFocus));
        }
        else
        {
            control.RemoveHandler(LostKeyboardFocusEvent, new RoutedEventHandler(ControlLostKeyboardFocus));
        }
    }
}

private static void ControlLostKeyboardFocus(object sender, RoutedEventArgs e)
{
    var control = (UIElement)sender;
    control.RaiseEvent(new RoutedEventArgs(LostFocusEvent));
}

这只是意味着无论何时LostKeyboardFocus为该控件引发,它都会继续并引发一个LostFocus导致绑定更新的附加事件。它像这样使用

<TextBox Text="{Binding Test}" LostKeyboardFocus="UIElement_OnLostKeyboardFocus" local:MainWindow.BindOnLostKeyboardFocus="True" />
于 2013-06-04T11:58:43.797 回答
3

您的附加属性做了几个假设:

  • 没有什么取决于事件LostKeyboardFocusLostFocus事件之间的区别
  • 它所附加的元素上的绑定实际上会响应LostFocus事件(它们可能有UpdateSourceTrigger.Explicit

相反,您可以枚举元素上的绑定并直接调用UpdateSource

private void CommitBindings(DependencyObject element) {
    var localValueEnumerator = element.GetLocalValueEnumerator();
    while (localValueEnumerator.MoveNext()) {
        var entry = localValueEnumerator.Current;
        if (BindingOperations.IsDataBound(element, entry.Property)) {
            var bindingExpression = (BindingExpressionBase)entry.Value;
            bindingExpression.UpdateSource();
        }
    }
}

此外,TextBox您可以处理容器并使用OldFocus获取失去键盘焦点的实际元素,而不是单独处理每个元素。

于 2013-06-04T12:35:14.820 回答
0

以下行为将解决此问题:

public class TextBoxUpdateOnLostKeyboardFocusBehavior : Behavior<TextBox>
{
    protected override void OnAttached()
    {
        if (AssociatedObject != null)
        {
            base.OnAttached();
            AssociatedObject.LostKeyboardFocus += OnKeyboardLostFocus;
        }
    }

    protected override void OnDetaching()
    {
        if (AssociatedObject != null)
        {
            AssociatedObject.LostKeyboardFocus -= OnKeyboardLostFocus;
            base.OnDetaching();
        }
    }

    private void OnKeyboardLostFocus(object sender, KeyboardFocusChangedEventArgs e)
    {
        var textBox = sender as TextBox;

        if (textBox != null && e.NewFocus == null)
        {
            // Focus on the closest focusable ancestor
            FrameworkElement parent = (FrameworkElement) textBox.Parent;
            while (parent is IInputElement && !((IInputElement) parent).Focusable)
            {
                parent = (FrameworkElement) parent.Parent;
            }

            DependencyObject scope = FocusManager.GetFocusScope(textBox);
            FocusManager.SetFocusedElement(scope, parent);
        }
    }
}

您可以将其附加到您的文本框,如下所示:

        <TextBox>
            <i:Interaction.Behaviors>
                <behaviors1:TextBoxUpdateOnLostKeyboardFocusBehavior />
            </i:Interaction.Behaviors>              
        </TextBox>
于 2015-10-02T11:02:02.563 回答
0

我有一个非常相似的情况,当单击窗口上的主 OK 按钮时,aTextBox中的 a没有更新其绑定源。Toolbar但是,如果您先使用标签,它会起作用。

就我而言,我能够更改要使用的绑定,UpdateSourceTrigger=PropertyChanged问题就解决了。

这将导致比默认绑定行为更多的绑定更新,但在这种情况下,这不是问题。

于 2020-05-05T19:00:21.740 回答