在下面的简单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
不会导致此行为。
答案 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)
您附属的财产有几个假设:
LostKeyboardFocus
和LostFocus
事件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
,并且问题已解决。
与默认绑定行为相比,这将导致更多的绑定更新,但是在这种情况下,这不是问题。