In the following simple WPF application a TextBox
is set to update a property when the focus is lost from that control, like so
<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; }
}
However when I run this application, enter some text in the text box and then click on the "Test" button my breakpoint on the MyString
property is not raised, also any event handler for the FocusLost
event is not raised either. These events are only raised when focus is lost from the control via some other means (e.g. the window is closed).
This is causing problems for us as in reality the "Test" button contains logic which relies on the MyString
property being updated.
How can I ensure that the FocusLost
event is correctly raised and that the binding is updated as I click on the "Test" button? It looks like the problem is somehow caused by the use of the ToolBar
, as replacing the ToolBar
with a standard button does not result in this behaviour.
Your attached property makes a couple of assumptions:
LostKeyboardFocus
and LostFocus
eventsLostFocus
event (they could have UpdateSourceTrigger.Explicit)Instead, you could enumerate the bindings on the element and directly call 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();
}
}
}
Also, instead of handling each TextBox
individually, you could handle the container and use OldFocus to get the actual element that lost keyboard focus.
In this case the text box doesn't actually loose logical focus and so the event is never raised - essentially I actually want the LostKeyboardFocus
event, and not the LostFocus
event to trigger the update.
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));
}
This just means that whenever LostKeyboardFocus
is raised for that control, it goes ahead and raises an additional LostFocus
event causing the binding to update. Its used like so
<TextBox Text="{Binding Test}" LostKeyboardFocus="UIElement_OnLostKeyboardFocus" local:MainWindow.BindOnLostKeyboardFocus="True" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With