单击某些其他控件时不会引发TextBox LostFocus

时间:2013-06-04 11:39:59

标签: wpf focus toolbar lost-focus

在下面的简单WPF应用程序中,TextBox设置为在从该控件丢失焦点时更新属性,如此

<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; }
}

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

这对我们造成了问题,因为实际上&#34;测试&#34;按钮包含依赖于正在更新的MyString属性的逻辑。

我如何确保正确引发FocusLost事件,并且在点击&#34;测试&#34;时更新绑定。按钮?看起来问题是由于ToolBar的使用造成的,因为用标准按钮替换ToolBar不会导致此行为。

4 个答案:

答案 0 :(得分:3)

在这种情况下,文本框实际上没有放松逻辑焦点,因此事件永远不会被引发 - 实际上我实际上希望LostKeyboardFocus事件而不是LostFocus事件触发更新

此问题类似于WPF: Data bound TabControl doesn't commit changes when new tab is selected,并且有一个Microsoft连接项here,其中包含许多可能的解决方案,但我使用附加属性修复此问题。

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" />

答案 1 :(得分:2)

您附属的财产有几个假设:

  • 没有任何内容取决于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();
        }
    }
}

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

答案 2 :(得分: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:

        <TextBox>
            <i:Interaction.Behaviors>
                <behaviors1:TextBoxUpdateOnLostKeyboardFocusBehavior />
            </i:Interaction.Behaviors>              
        </TextBox>

答案 3 :(得分:0)

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

就我而言,我能够更改绑定以使用UpdateSourceTrigger=PropertyChanged,并且问题已解决。

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