WPF Databinding in Verbindung mit einem Defaultbutton
Vor kurzem ist ein Kollege von mir auf ein kleines Problem mit Databinding gestoßen. Und zwar in Verbindung mit einem Button dessen “IsDefault” Eigenschaft auf True gesetzt ist. Es wurde die Bindingquelle nicht aktualisiert.
Zur Verdeutlichung soll folgendes UI dienen:
Die TextBox für den Username ist über Databinding an die “Username” Eigenschaft des ViewModels angebunden. Bei dem Ok Button ist “IsDefault” auf True gesetzt. Mit Hilfe einiger Traces kann man im Ausgabefenster des Visual Studio sehen, was vom Benutzer eingegeben wurde.
...
public string Username
{
get { return _userName; }
set
{
_userName = value;
Trace.WriteLine(string.Format("Username {0} entered.", _userName));
}
}
...
private void OnOkCommand()
{
Trace.WriteLine("Ok button pressed.");
Trace.WriteLine(string.Format("Current username {0}", _userName));
}
...
Bei einer TextBox die per Databinding angebunden ist, wird die Quelle per Default bei Verlust des Fokus aktualisiert. D.h. die Eigenschaft “UpdateSourceTrigger” des Bindings hat den Wert “LostFocus”. Scheinbar wird beim Auslösen eines Defaultbuttons der Fokus nicht auf diesen Button gesetzt. Es wird dann zwar das Command ausgeführt welches mit dem Button verknüpft ist, aber die Quelle der TextBox wurde nicht aktualisiert.
Es gibt mehrere Möglichkeiten das Problem zu lösen.
- Durch Setzen der “UpdateSourceTrigger” Eigenschaft am Binding auf “PropertyChanged”. Dadurch wird die Quelle bei jeder Veränderung aktualisiert.
- Durch manuelles Setzen des Fokus im Click-Eventhandler des Buttons
- Durch manuelles Aktualisieren des Bindings im Click-Eventhandler des Buttons
Bei Variante zwei muss der entsprechende Code in die Code-Behind Datei der View verlagert werden, da ein direkter Zugriff auf das Button Control notwendig ist:
private void OnOkButtonClick(object sender, RoutedEventArgs e)
{
FocusManager.SetFocusedElement(this, (Button)sender);
}
Den Code für die dritte Lösung könnte man (technisch) auch ins ViewModel packen. Gefällt mir aber nicht, da ich im ViewModel keine Kenntnis von UI Controls haben möchte. Deshalb kommt auch hier die Code-Behind Datei zu Zuge:
private void OnOkButtonClick(object sender, RoutedEventArgs e)
{
TextBox focusedTextBox = Keyboard.FocusedElement as TextBox;
if( null != focusedTextBox )
{
BindingExpression bindingExpression = focusedTextBox.GetBindingExpression(TextBox.TextProperty);
if (null != bindingExpression)
{
bindingExpression.UpdateSource();
}
}
}
Bisher gefällt mir die zweite Variante am Besten. Mir sind auch noch keine Nachteile oder Probleme aufgefallen. Den “richtigen” Code, der beim Auslösen des Defaultbuttons ausgeführt werden soll, kann man im ViewModel lassen. Das Command welches mit dem Button verknüpft ist, wird erst nach dem Click Event ausgeführt.
Weiss zufällig jemand, ob es diesen Effekt mit dem Defaultbutton und LostFocus auch unter WinForms gibt?






Hallo Bernd,
das ist ein schöner Blogpost!
Ich habe schon mal das gleiche Problem gehabt, allerdings mit HTML Web-Forms.
Ich habe das auch mit beiden Varianten ausprobiert.
Die erste Variante (Focus woanders setzen), hat nicht funktioniert, da der Submit schneller war, als die Formatierungsfunktionen und die Aktualisierung der UI.
Dann habe ich länger gesucht, bis ich doch die zweite Variante ausprobiert habe. Das wollte ich zuerst nicht, weil ich da an 2 Stellen ändern müsste, falls sich die Formatierungsfunktionsaufrufe ändern sollten. Bei ‘onSubmit’ habe ich dann die Formatierungsfunktionen aufgerufen, bevor das dann abgesendet wird. Dann hat es wenigstens funktioniert.
Das ist natürlich bei WPF wesentlich sauberer gelöst, aber es ist trotzdem schade, dass er vorher nicht zumindest den Focus auf das Formular setzt, oder Ähnliches.
Danke Robert. Ich finde das Verhalten auch sehr unschön. Könnte man schon fast als Bug ansehen.