Silverlight/WPF debugging: Determining the focused element
When developing complex WPF or Silverlight applications, you may stumble upon the problem of not knowing where your keyboard focus is. This is also known as the “But where am I typing?!” problem, and occurs more often than not on composite applications.
Silverlight
You can determine which element has keyboard focus within Silverlight via the FocusManager.FocusedElement property. Unfortunately, this property can’t be directly bound to, nor does it provide notification of its value changes. When debugging, I use a timer to regularly get its value and set it to a property that the UI can bind to. The implementation uses the proxy object pattern :
<local:DebugFocusedElementProxy x:Name="debugFocusedElement"/>
<TextBlock TextWrapping="Wrap" Text="{Binding FocusedElementDescription,ElementName=debugFocusedElement}"/>
The proxy implementation is as follows
private string _FocusedElementDescription;
public string FocusedElementDescription
{
get { return _FocusedElementDescription; }
set
{
_FocusedElementDescription = value;
OnNotifyPropertyChanged("FocusedElementDescription");
}
}
public DebugFocusedElementProxy()
{
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(100);
timer.Tick += (o, ea) =>
{
var fe = FocusManager.GetFocusedElement();
if (fe == null)
this.FocusedElementDescription = "None";
else
{
var element = fe as FrameworkElement;
if (string.IsNullOrEmpty(element.Name))
this.FocusedElementDescription = fe.GetType().Name;
else
this.FocusedElementDescription = element.Name;
}
};
timer.Start();
}
WPF
WPF’s focus system is more complicated, as it supports multiple focus scopes. This means that elements can lose keyboard focus while maintaining logical focus. This is mainly to allow support for menus and toolbars which get keyboard focus and give them back to the previously focused element afterwards (think “push/pop”). For this reason, the WPF FocusManager is slightly more complex as it supports multiple focus scopes and getting the focused element requires providing the scope. Fortunately, as per how the Windows UI (and other popular OSes) works, keyboard focus can only be given to one element at a time, and WPF’s Keyboard.FocusedElement (which does not exist in Silverlight) will provide you with the element currently holding keyboard focus. You can use the previously described proxy object pattern to query this value.
<local:DebugFocusedElementProxy x:Name="debugFocusedElement"/>
<TextBlock TextWrapping="Wrap" Text="{Binding FocusedElementDescription, ElementName=debugFocusedElement}"/>
<Rectangle Height="{Binding ActualHeight, ElementName=debugFocusedElement}"
Width="{Binding ActualWidth, ElementName=debugFocusedElement}" MaxHeight="120">
<Rectangle.Fill>
<VisualBrush Visual="{Binding FocusedElement, ElementName=debugFocusedElement}" Stretch="Uniform"/>
</Rectangle.Fill>
</Rectangle>
A neat tweak is to leverage WPF’s VisualBrush to further improve your debugging experience.
A different, timer-free but less complete way, to determine the focused element in WPF is to bind to the FocusManager.FocusedElement attached property (hence the difference from the Silverlight version !) of a focus scope holding control such as the application Window itself. Since FocusManager.FocusedElement is an attached property, you get value updates out of the box without the need for a timer. The binding looks like this :
<TextBlock Text="{Binding (FocusManager.FocusedElement), Converter={StaticResource GetTypeConv}, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
The idea is just to go up the visual tree to the focus scope holder (FindAncestor your way to the Window), and bind to its FocusManager.FocusedElement property.
Sample project
As usual, sample projects are attached to this post. On an interesting note, try opening the XAML files with Expression Blend !
Thanks to my friend and colleague João for motivating me into posting this trick!
Comments
- Anonymous
September 28, 2010
I truly love you man! Thanx for the great tip!!!