WPF-MVVM:从ViewModel设置UI控件焦点

Son*_*oul 46 .net c# wpf focus mvvm

在MVVM架构中设置控制焦点的好习惯是什么.

我设想它的方式是使用ViewModel上的属性,它会在需要时触发焦点更改.并且让UI控件绑定/侦听该属性,以便在它更改时,将设置适当的焦点.

我将其视为ViewModel,因为我希望在ViewModel执行某个操作后设置焦点,例如加载某些数据.

什么是最佳做法?

Sna*_*att 31

使用此处的答案中建议的IsFocused附加属性:从视图模型(C#)设置WPF中的文本框焦点

然后,您只需绑定到viewmodel中的属性即可.

  • 那根本不是什么.UI元素具有附加属性,该属性绑定到ViewModel中的bool属性.附加属性的作用是将焦点设置为具有附加属性的控件.那就是MVVM.如果不清楚我建议你使用Anvaka提出的答案. (6认同)

Kis*_*mar 14

如果您使用的是Caliburn.Micro,这是我创建的一项服务,用于将Focus设置为从Screen继承的视图中的任何Control.

注意:这仅适用于您的MVVM框架使用Caliburn.Micro.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

怎么用?

从您的ViewModel继承自Caliburn.Micro.Screen或Caliburn.Micro.ViewAware

this.SetFocus(()=>ViewModelProperty); 要么 this.SetFocus("Property");

这个怎么运作?

此方法将尝试在Visual Tree of View中搜索元素,焦点将设置为任何匹配的控件.如果没有找到这样的控件,那么它将使用Caliburn.Micro使用的BindingConventions.

例如,

它将在控件的BindingExpression中查找Propery.对于TextBox,它将查看此属性是否绑定到Text属性,然后将设置焦点.