Friday, February 26, 2010
« Will the real data object stand up? | Main | Running CSLA .NET 3.8.2 under .NET 4 and... »

Continuing on my quest to use the Visual Studio 2010 XAML designer, MVVM and absolutely no code behind, I ran into the common problem of setting focus to a control as a form loads.

This is easily accomplished using code-behind:

firstControl.Focus();

But of course that requires code-behind, which I believe should be unnecessary.

It can also be accomplished by creating an attached property or (with the Blend 3 SDK) trigger action or behavior. Neither of these are great with the VS10 designer though, because the designer has no way to apply attached properties and doesn’t know about the Blend 3 SDK concepts.

So while those solutions are fine, they require manual typing of XAML, which I believe should be unnecessary (as unrealistic as that view might be…).

My solution is to create a FrameworkElement called SetFocus. This shows up in the Toolbox and so can be dragged directly onto the form using the designer. The SetFocus control has no visual aspect, so it doesn’t interfere with the layout or look of the form. Once SetFocus is on the form, the Properties window can be used to set its TargetProperty property, indicating the property that should get focus when the form is loaded.

image

This requires no code-behind and no manual typing of XAML. It keeps the concept of startup focus completely in the view, which (I think) is a good place for it. So my viewmodel doesn’t know or care about this issue. And the same control works in Silverlight and WPF, which should be the goal for any good XAML control.

Here’s the code:

using System;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;

namespace Collective.Controls
{
  /// <summary>
  /// Sets the focus to the TargetControl property as the
  /// form is loaded.
  /// </summary>
  public class SetFocus : FrameworkElement
  {
    /// <summary>
    /// Gets or sets the target UI control.
    /// </summary>
    public static readonly DependencyProperty TargetControlProperty =
      DependencyProperty.Register("TargetControl", typeof(Control),
      typeof(SetFocus), new PropertyMetadata((o, e) =>
      {
        var sf = o as SetFocus;
        if (sf != null && sf.TargetControl != null)
          sf.TargetControl.Focus();
      }));
    /// <summary>
    /// Gets or sets the target UI control.
    /// </summary>
    [Category("Common")]
    public Control TargetControl
    {
      get { return (Control)GetValue(TargetControlProperty); }
      set { SetValue(TargetControlProperty, value); }
    }
  }
}

Friday, February 26, 2010 11:59:47 AM (Central Standard Time, UTC-06:00)
Nice post, Rocky. You can also use the FocusManager.FocusedElement attached property. http://msdn.microsoft.com/en-us/library/system.windows.input.focusmanager.focusedelement.aspx

Josh
Friday, February 26, 2010 12:04:18 PM (Central Standard Time, UTC-06:00)
I think FocusManager is only in WPF?

And in any case, it requires manual typing of XAML - it doesn't work natively with the VS10 designer (in any way I can figure out at least).
Friday, February 26, 2010 12:08:54 PM (Central Standard Time, UTC-06:00)
Yeah, it's WPF only. I only listed it for the sake of completeness. I've never tried using it in the VS10 designer. Thanks for sharing your better approach. :)
Friday, February 26, 2010 12:43:23 PM (Central Standard Time, UTC-06:00)
Very clever but I am leaning towards either a behavior or attached property. Having it derive from FrameworkElement feels "wrong". I always stress over what should derive from what whenever I'm creating components like this. :S
Friday, February 26, 2010 1:00:40 PM (Central Standard Time, UTC-06:00)
I agree that using a trigger/behavior would be better. But since that requires the Blend 3 SDK assembly (not part of the core runtime), and doesn't work with VS10's designer, I don't think it will gain traction for most people in the near future.

If/when the Interactivity assembly becomes part of the core runtime (SL and WPF), and the Visual Studio designer supports trigger/behavior concepts - then it will probably become a mainstream solution.
Friday, February 26, 2010 5:24:21 PM (Central Standard Time, UTC-06:00)
Sometimes you need to set focus to the control inside UserControl that can be shown or hidden. In this case the solution would be to attach handler to Loaded event of this UserControl rather than just setting focus inside property changed callback.
Alex Simkin
Friday, February 26, 2010 6:16:41 PM (Central Standard Time, UTC-06:00)
@Alex, I suppose that's valid if the event is handled inside the usercontrol itself. The thing is, handling something like a Loaded event typically means code-behind, and code-behind should not be required.

I suppose, if such a scenario is really required, you could use a combination of my TriggerAction control (to handle the Loaded event) and SetFocus control (to set the focus). I'd need to add a SetFocus() method to my SetFocus control that could be invoked by TriggerAction, but in theory that would work - while avoiding code-behind and manual XAML typing.
Monday, April 12, 2010 5:39:35 AM (Central Standard Time, UTC-06:00)
Hey Derek, I must agree with you on this one, however, you might be interested to know that if you try an set the focus using the FocusManager in XAML to a dynamically built list (such as listbox) at run time the focus will not be set correctly; you'll be forced to use the code method :-(

For example, this will not work, if the data is loaded dynamically e.g. the ItemsSource is set in a Loaded event handler:

<Window ....
FocusManager.FocusedElement="{Binding ElementName=myList}">
<ListBox x:Name="myList" />
</Window>

You then have to use code, so the question then becomes: which syntax would you use?

myList.Focus();
// OR
FocusManager.SetFocusedElement(this, myList);

So which would *you* choose, and why? ;-)
----------------------
By mcse
donaldjeo
Comments are closed.