1

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);
    }
}
Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
Coder1095
  • 828
  • 10
  • 25

3 Answers3

3

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.

Klaus78
  • 11,648
  • 5
  • 32
  • 28
  • This works and makes sense. I have done some testing and can confirm that in case 2, I am trying to set focus before the TextBox is visible. Queing the focus change on the dispatcher means that it is called later on (i.e. after the dispatcher thread has finished rendering the TextBox). – Coder1095 Dec 13 '12 at 13:06
2

Probably because the IsVisibleChanged event is raised on another thread (not on the UI thread).

laszlokiss88
  • 4,001
  • 3
  • 20
  • 26
1

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.

slugster
  • 49,403
  • 14
  • 95
  • 145
  • Thank you for taking the time to help. In this case I believe the event is being raised on the UI thread however the information here is still very valuable. – Coder1095 Dec 13 '12 at 13:09