Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF-MVVM: Setting UI control focus from ViewModel

Tags:

c#

.net

focus

mvvm

wpf

What is a good practice of setting control focus in MVVM architecture.

The way I envision it, is with a property on the ViewModel that would trigger a focus change when needed. And than have the UI controls bind/listen to that property so that if it changes, appropriate focus will be set.

I see it as a ViewModel thing, because i want to set focus appropriate after a certain action was performed by the ViewModel, such as loading certain data.

What's the best practice?

like image 635
Sonic Soul Avatar asked Mar 17 '11 14:03

Sonic Soul


People also ask

How do I set focus in WPF MVVM?

How to use this? this. SetFocus(()=>ViewModelProperty); or this. SetFocus("Property");

How do you focus a control in WPF?

When setting initial focus at application startup, the element to receive focus must be in the visual tree of the initial window loaded by the application, and the element must have Focusable and IsVisible set to true . The recommended place to set initial focus is in the Loaded event handler.

Can ViewModel have reference view?

In "pure" MVVM, the ViewModel shouldn't really reference the View. It's often convenient, however, to provide some form of interface in the View whereby the ViewModel can interact with it.

Can a ViewModel have multiple views?

It's possible that having two separate views is what you want, but it's just something to consider. Show activity on this post. I am using the prism framework and was also looking for a solution of using one viewmodel for many (child) views.


2 Answers

Use the IsFocused Attached Property as suggested in the Answer here: Set focus on textbox in WPF from view model (C#)

Then you can simply bind to a property in your viewmodel.

like image 185
Snarfblatt Avatar answered Sep 20 '22 15:09

Snarfblatt


If you are using Caliburn.Micro, here is a service that I created to set Focus to any Control in the view inherited from Screen.

Note: This will only work if you are using Caliburn.Micro for your MVVM framework.

public static class FocusManager {     public static bool SetFocus(this IViewAware screen ,Expression<Func<object>> propertyExpression)     {         return SetFocus(screen ,propertyExpression.GetMemberInfo().Name);     }      public static bool SetFocus(this IViewAware screen ,string property)     {         Contract.Requires(property != null ,"Property cannot be null.");         var view = screen.GetView() as UserControl;         if ( view != null )         {             var control = FindChild(view ,property);             bool focus = control != null && control.Focus();             return focus;         }         return false;     }      private static FrameworkElement FindChild(UIElement parent ,string childName)     {         // Confirm parent and childName are valid.          if ( parent == null || string.IsNullOrWhiteSpace(childName) ) return null;          FrameworkElement foundChild = null;          int childrenCount = VisualTreeHelper.GetChildrenCount(parent);          for ( int i = 0; i < childrenCount; i++ )         {             FrameworkElement child = VisualTreeHelper.GetChild(parent ,i) as FrameworkElement;             if ( child != null )             {                  BindingExpression bindingExpression = GetBindingExpression(child);                 if ( child.Name == childName )                 {                     foundChild = child;                     break;                 }                 if ( bindingExpression != null )                 {                     if ( bindingExpression.ResolvedSourcePropertyName == childName )                     {                         foundChild = child;                         break;                     }                 }                 foundChild = FindChild(child ,childName);                 if ( foundChild != null )                 {                     if ( foundChild.Name == childName )                         break;                     BindingExpression foundChildBindingExpression = GetBindingExpression(foundChild);                     if ( foundChildBindingExpression != null &&                         foundChildBindingExpression.ResolvedSourcePropertyName == childName )                         break;                 }              }         }          return foundChild;     }      private static BindingExpression GetBindingExpression(FrameworkElement control)     {         if ( control == null ) return null;          BindingExpression bindingExpression = null;         var convention = ConventionManager.GetElementConvention(control.GetType());         if ( convention != null )         {             var bindablePro = convention.GetBindableProperty(control);             if ( bindablePro != null )             {                 bindingExpression = control.GetBindingExpression(bindablePro);             }         }         return bindingExpression;     } } 

How to use this?

From your ViewModel inherited from Caliburn.Micro.Screen or Caliburn.Micro.ViewAware

this.SetFocus(()=>ViewModelProperty); or this.SetFocus("Property");

How it works?

This method will try to search for an element in the Visual Tree of View and focus will be set to any matching control. If no such control found, then it will make use of the BindingConventions used by Caliburn.Micro.

For ex.,

It will look for the Propery in the BindingExpression of the control. For TextBox, it will look whether this property is binded to Text property then the focus will be set.

like image 40
Kishore Kumar Avatar answered Sep 19 '22 15:09

Kishore Kumar