Control input focus from ViewModel objects

One of the pain points in MVVM development is controlling input focus from ViewModel objects.  A common scenario is when a validation error occurs, and focus needs to move to the control that is bound to the property in error.  This makes it easier for the user to immediately fix the validation problem.

A while ago, Dr. WPF shared a solution to the WPF Disciples that involves hijacking the VM’s IDataErrorInfo implementation and doing some hacky magic that results in focus being sent to the correct control.  While a brilliant solution, I preferred finding a more formal, less hacky solution.  This blog post presents the implementation that I’ve thrown together, with help from the WPF Disciples over the past few days.

In a ViewModel object, you must implement my IFocusMover interface.

public event EventHandler MoveFocus;

void RaiseMoveFocus(string focusedProperty)
{
var handler = this.MoveFocus;
if (handler != null)
{
var args = new MoveFocusEventArgs(focusedProperty);
handler(this, args);
}
}

The MoveFocus event should be raised when the VM object determines that input focus needs to be sent to the control bound to the ‘focused property.’  For example, if the FirstName property of an object is deemed invalid, you would call the RaiseMoveFocus method, passing “FirstName” as the focused property argument.  In the demo application, this results in the TextBox whose Text is bound to the FirstName property to get input focus.

Now let’s shift our attention to the View to see how a control is configured to be able to receive focus.  The only thing you must do is use a custom Binding object that I made, called FocusBinding.  That class relies on Philipp Sumi’s BindingDecoratorBase class, which allows you to create a custom Binding that can override the all-important ProvideValue method (inherited from MarkupExtension).

public override object ProvideValue(IServiceProvider provider)
{
DependencyObject elem;
DependencyProperty prop;
if (base.TryGetTargetItems(provider, out elem, out prop))
{ FocusController.SetFocusableProperty(elem, prop); }

return base.ProvideValue(provider);
}

You use the FocusBinding in XAML like this:

<TextBox

Text=”{jas:FocusBinding Path=FirstName, ValidatesOnDataErrors=True}”

/>

The ValidatesOnDataErrors property setting is not required, but is used in the demo app to enable support for validation via the VM’s IDataErrorInfo implementation.  All controls bound to VM properties that can be “focusable” use the FocusBinding, instead of a normal Binding.  When the VM raises its MoveFocus event, the control whose FocusBinding’s Path references the ‘focused property’ will be given input focus.

You can download the source code here.  NOTE: Rename the file extension from .DOC to .ZIP and then decompress it.  Feedback is welcome!

About these ads

19 Responses to Control input focus from ViewModel objects

  1. Dr. WPF says:

    This is fantastic, Josh! Well done! :-D

    (Just one more example of why SL should give us markup extensions too!)

  2. Josh Smith says:

    Agreed, Doc. Looks like we might have to wait a while for SL to catch up with WPF in that respect.

  3. Anagram4Wander says:

    From a longer term perspective, views ideally could be managed via the interaction model – which would work in both SL and WPF.. i.e. using behaviors, triggers and actions.

  4. Josh Smith says:

    Good point, Anagram4Wander. Philpp Sumi is working on a behavior-based approach, which leverages the code presented here, “as we speak.”

    Josh

  5. [...] Control input focus from ViewModel objects « Josh Smith on WPF [...]

  6. ross says:

    This is very nice. After reading the title I thought, ‘oh no!, separated presentation out the window’.

    However looking at it in more detail I can see the implementation is free of coupling.

    It does seem to be pushing the boundary of what I philosophically think a VM should do. Perhaps if / when I implement this idea I might change the words MoveFocus to something that doesn’t imply what the UI should do.

    Of course it is just semantics, not a criticism.

  7. Ben says:

    Great post Josh, I have been looking for a tidy solution. This assumes though, that you can set the DataContext inside the constructor. In my model I cannot do this. How can I wire in a DataContextChanged event to recheck the focus bindings?

    Thanks

  8. Josh Smith says:

    Good catch, Ben. I’ve updated the source code so that you can set the DataContext after InitializeComponent is called in the View’s ctor.

    Josh

  9. Anvaka says:

    Hi Josh,

    I have some concerns about your approach. Using custom binding decorators makes me little nerves. You can have MultiBinidng or Binding that simplifies debugging instead of simple Binding – wouldn’t it break focus?

    I think using bindings makes code far less obvious. You expect it to provide a value not to do focus tricks under the hood.

    Instead, I’m using attached behaviors… Some time ago I answered to this question at Stack Overflow: http://stackoverflow.com/questions/1356045/set-focus-on-textbox-in-wpf-from-view-model-c-wpf/1356781#1356781

    Solution isn’t perfect too, but we can improve it :).

    Cheers, Anvaka.

  10. [...] this posting is not about controlling input focus. Josh did all the legwork there, and you should check out the article on his blog. Everything that goes beyond the Behavior classes is Josh’s work, not mine  – I merely [...]

  11. [...] Control input focus from ViewModel objects (Josh Smith) [...]

  12. [...] Control input focus from ViewModel objects (Josh Smith) [...]

  13. [...] Control input focus from ViewModel objects [...]

  14. Søren says:

    Tanks Josh! I have tried to solve this problem in many ways. Letting the ViewModel handling the Focus control is by fare most, the ‘right way’ to do it.

  15. [...] I was reading an article on Josh Smith’s blog about how to set the focus from inside the View Model of a MVVM pattern using a custom MarkupExtension class.  While this article was very [...]

  16. LukePet says:

    I Josh, I test your solution and it works great.

    One question. I would set FocusBinding from code and I can’t use “SetBinding” because FocusBinding is not read like standard Binding.

    Which is the right way?

  17. Josh Smith says:

    LukePet,

    If you are setting the binding in code, you could just call FocusController.SetFocusableProperty(elem, prop); in code, too.

    Josh

  18. LukePet says:

    you are right, thanks!

  19. Johan O says:

    I am working everyday with these kinds of problems since im one of the programmers on a big MVVM project. There are always lot’s of new problems concernings things like this when you need to do something in the view that is not directly supported with simple databinding. I always always solve these problems, and I always do it with the same tool, Behaviors! I think behaviors are easier to program and easier to use. We never need to use extensions anymore. I would solve this one like this:

    Use a trigger that triggers on an event of the associated objects datacontext. It could be an event that is known through an interface (like in the example), or it could be a trigger where you can name the event yourself (one of those exists in the blend 3 samples and maybe even blend 4 now?).

    I would then write an action that sets focus to the associated object when the trigger triggers.

    Done! And now very easy to reuse :)

    /Johan

Follow

Get every new post delivered to your Inbox.

Join 274 other followers

%d bloggers like this: