简体   繁体   English

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

[英]WPF-MVVM: Setting UI control focus from ViewModel

What is a good practice of setting control focus in MVVM architecture.在 MVVM 架构中设置控制焦点的好做法是什么。

The way I envision it, is with a property on the ViewModel that would trigger a focus change when needed.我设想的方式是在 ViewModel 上使用一个属性,该属性会在需要时触发焦点更改。 And than have the UI controls bind/listen to that property so that if it changes, appropriate focus will be set.然后让 UI 控件绑定/侦听该属性,以便在它发生更改时设置适当的焦点。

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.我将其视为 ViewModel 的事情,因为我想在 ViewModel 执行某个操作(例如加载某些数据)后适当地设置焦点。

What's the best practice?最佳做法是什么?

Use the IsFocused Attached Property as suggested in the Answer here: Set focus on textbox in WPF from view model (C#)按照此处的答案中的建议使用 IsFocused 附加属性: Set focus on textbox in WPF from view model (C#)

Then you can simply bind to a property in your viewmodel.然后您可以简单地绑定到您的视图模型中的属性。

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.如果您使用 Caliburn.Micro,这里是我创建的一个服务,用于将 Focus 设置为从 Screen 继承的视图中的任何控件。

Note: This will only work if you are using Caliburn.Micro for your MVVM framework.注意:这仅在您为 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;
    }
}

How to use this?这个怎么用?

From your ViewModel inherited from Caliburn.Micro.Screen or Caliburn.Micro.ViewAware从您从 Caliburn.Micro.Screen 或 Caliburn.Micro.ViewAware 继承的 ViewModel

this.SetFocus(()=>ViewModelProperty); or this.SetFocus("Property");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.如果未找到此类控件,则它将使用 Caliburn.Micro 使用的 BindingConventions。

For ex.,例如,

It will look for the Propery in the BindingExpression of the control.它将在控件的 BindingExpression 中查找属性。 For TextBox, it will look whether this property is binded to Text property then the focus will be set.对于 TextBox,它会查看此属性是否绑定到 Text 属性,然后设置焦点。

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

You could introduce an interface for the View so that the ViewModel can tell the View to set the Focus. 您可以为View引入一个接口,以便ViewModel可以告诉View设置Focus。 The BookLibrary sample application of the WPF Application Framework (WAF) shows how to do it. WPF应用程序框架(WAF)的BookLibrary示例应用程序显示了如何执行此操作。 Please have a look at the BookListViewModel. 请看一下BookListViewModel。

The question has been asked a couple of times, unfortunately the answers only apply to WPF. 这个问题已被问过几次,不幸的是答案只适用于WPF。 So for silver light using MVVM, you can also bind with any property for detail please visit the following link 因此对于使用MVVM的银灯,您还可以绑定任何属性以获取详细信息,请访问以下链接

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM