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

发布于 2024-10-22 05:01:39 字数 201 浏览 2 评论 0原文

在 MVVM 架构中设置控制焦点的良好实践是什么?

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

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

最佳做法是什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一杆小烟枪 2024-10-29 05:01:39

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

然后您可以简单地绑定到视图模型中的属性。

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.

清醇 2024-10-29 05:01:39

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

注意:仅当您将 Caliburn.Micro 用于 MVVM 框架时,此方法才有效。

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");

它是如何工作的?

此方法将尝试在可视化视图树中搜索元素,并将焦点设置到任何匹配的控件。如果没有找到这样的控件,那么它将利用 Caliburn.Micro 使用的 BindingConventions。

例如,

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

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.

一笑百媚生 2024-10-29 05:01:39

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

The ViewModel throws an event to the View telling it that the action has been completed, and the View will set the focus.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文