WPF-MVVM: Setting UI control focus from ViewModel

后端 未结 5 974
旧时难觅i
旧时难觅i 2020-12-08 09:14

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

5条回答
  •  离开以前
    2020-12-08 09:46

    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> 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.

提交回复
热议问题