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

时间:2011-03-17 14:39:00

标签: c# .net wpf mvvm focus

在MVVM架构中设置控制焦点的良好做法是什么。

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

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

最佳做法是什么?

5 个答案:

答案 0 :(得分:28)

使用此处答案中建议的IsFocused附加属性:Set focus on textbox in WPF from view model (C#)

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

答案 1 :(得分:13)

如果您使用的是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;
    }
}

如何使用?

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

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

它如何运作?

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

例如,

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

答案 2 :(得分:1)

ViewModel向View抛出一个事件,告诉它该动作已经完成,View将设置焦点。

答案 3 :(得分:0)

您可以为View引入一个界面,以便ViewModel可以告诉View设置Focus。 WPF Application Framework (WAF) 的BookLibrary示例应用程序显示了如何执行此操作。请查看BookListViewModel。

答案 4 :(得分:0)

这个问题已被问过几次,不幸的是答案只适用于WPF。因此对于使用MVVM的银灯,您还可以绑定任何属性以获取详细信息,请访问以下链接

http://codenicely.blogspot.com/2012/01/how-to-set-textbox-focus-in-silverlight.html

相关问题