Thursday, October 30, 2008

WPF and Focus

So, Control focus in WPF just frustrates me sometimes. It should be simple. Control.Focus(). But things are never as they seem. Take this code for example. It simply hides a panel asking for an ID and the presents a panel asking for a Pin. With the myPinPad variable is an instance of a custom control (inherited from UserControl) with nice buttons for a pin pad and a text field to type optionally type in a pin.



private void ShowPinPad ()
{
IDBadgePanel.Visibility = Visibility.Collapsed;
PinPanel.Visibility = Visibility.Visible;
myPinPad.Focus();
}


The PinPad custom control contains an override of the OnGotFocus method as shown below. The PWD variable is a PasswordBox control that will display the pin. Now, when the PinPad control gets focus, we want the PasswordBox to get focus so a person can just start typing.



protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
PWD.Focus();
}


Anyway, this doesn't work the first time around! Why not? Well, after some investigation it is revealed that while PWD.IsInitialized is true, PWD.IsVisible is false. Of course, call ShowPinPad again later on and it works. This time IsVisible is set to true.

So, what to do? Well, I think the reason it isn't visible is that despite the status of being Initialized, it really isn't yet. Since we just made the panel containing the control visible, perhaps the render engine hasn't had time to make the children (and hence the PWD control) visible yet.

One trick is to wait a bit before sending the focus. And by wait, I mean let all the other initialization stuff finish first on the thread. This could be done with a DispatchTimer, but an even easier trick is the following:



protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
this.Dispatcher.BeginInvoke((Action)delegate { PWD.Focus(); },
System.Windows.Threading.DispatcherPriority.Background);
}


With this trick, we create an anonymous delegate and schedule it for execution at the lowest possible priority. So, hopefully everything else will get initialized first. I've tried this with my app and it works.

No comments:

Post a Comment