I have a UserControl
that contains a TextBox
. When the user control becomes visible, I give the TextBox
focus. Could somebody clarify why I have to do this using the Dispatcher
?
public MyUserControl()
{
InitializeComponent();
this.IsVisibleChanged += VisibilityChanged;
}
Case 1 (works):
private void VisibilityChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (this.Visibility == Visibility.Visible)
{
this.Dispatcher.BeginInvoke((Action)delegate
{
Keyboard.Focus(this.InputTextBox);
}, DispatcherPriority.Render);
}
}
Case 2 (does not work):
private void VisibilityChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (this.Visibility == Visibility.Visible)
{
Keyboard.Focus(InputTextBox);
}
}
could you call Keyboard.Focus(InputTextBox);
in the event handler for InputTextBox.IsVisibleChanged
instead of this.IsVisibleChanged
?
If this works then I suspect the this.IsVisibleChanged
event is raised before the layout panel has updated the children controls, i.e perhaps InputTextBox
is still not visible when you put focus on it without BeginInvoke
.
Probably because the IsVisibleChanged
event is raised on another thread (not on the UI thread).
The control you are manipulating belongs to the UI thread (because that is where it was created). All controls derive from DispatcherObject, so control.Dispatcher
(or this.Dispatcher
from within the control) will give you the reference to the Dispatcher belonging to the thread the control was created on.
You are then queueing an action on that Dispatcher from the background thread that the event handler is running on. Why are you running on a background thread? It is a control so it is at the mercy of its host, at a guess there is some programmatic logic on a background thread that is causing the visibility to change (maybe via data binding), and consequently the event handler is also invoked on that background thread.
In the interests of stopping you from going wild with the Dispatcher and attempting to use it to perform magical mystical feats that it isn't intended for, make sure you familiarise yourself with the Dispatcher.CurrentDispatcher property and its difference (I've seen some truly awful code due to developers not realising this).
For a good overview of the Dispatcher associated with a UI element check this article: MSDN Advanced WPF: Threading Model. Note that sample code that calls VerifyAccess()
before attempting the action that manipulates the control.