C# WPF MVVM 实战 – 5- 用绑定,通过 VM 设置 View 的控件焦点_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > C# WPF MVVM 实战 – 5- 用绑定,通过 VM 设置 View 的控件焦点

C# WPF MVVM 实战 – 5- 用绑定,通过 VM 设置 View 的控件焦点

 2013/8/31 12:09:23  Lepton  博客园  我要评论(0)
  • 摘要:本文介绍在MVVM中,如何用ViewModel控制焦点。这焦点设置个东西嘛,有些争论。就是到底要不要用ViewModel来控制视图的键盘输入焦点。这里不讨论,假设你就是要通过VM,设置输入焦点在哪里。MSDN有解释关于Focus的,还有FocusManager,点击这里打开。不知道的话建议你先看看,只求结果的可以直接看下面代码,抄就是了。这次,初级的解释全部略过,直接说做法,看不懂的请留言。做法很多,大概两种比较符合MVVM模式:1
  • 标签:C# view 控件

本文介绍在 MVVM 中,如何用 ViewModel 控制焦点。

这焦点设置个东西嘛,有些争论。就是到底要不要用 ViewModel 来控制视图的键盘输入焦点。这里不讨论,假设你就是要通过 VM,设置输入焦点在哪里。

MSDN 有解释关于 Focus 的,还有 FocusManager,点击这里打开。不知道的话建议你先看看,只求结果的可以直接看下面代码,抄就是了。这次,初级的解释全部略过,直接说做法,看不懂的请留言。做法很多,大概两种比较符合 MVVM 模式:

1. DataTrigger 设置 FocusedElement

Style 中写 Trigger,DataTrigger,值变化触发设置 FocusManager 的 FocusedElement。

XAML:

break-word;overflow: auto;"><Grid>
    <Grid.Style>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=ScanFtBarCodeNow}" Value="True">
                    <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=FtNoTextBox}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>
    <TextBox name="FtNoTextBox" />
</Grid>

注意:放置 Style 的位置要注意。一层又一层 Grid 的话,FocusManager 单单设置 FocusedElement 不足够。原因在 MSDN 有说明,不重复。

2. Attached Property

使用 Attached Property 为控件加属性,值变化触发 CallBack,然后 UIElement.Focus()。所谓值变化,比如,bool 变为 True 。

CODE:

public static class FocusBehavior {
    public static bool GetIsFocused(DependencyObject obj) {
        return (bool)obj.GetValue(IsFocusedProperty);
    }
    public static void SetIsFocused(DependencyObject obj, bool value) {
        obj.SetValue(IsFocusedProperty, value);
    }
    public static readonly DependencyProperty IsFocusedProperty =
            DependencyProperty.RegisterAttached(
            "IsFocused", 
            typeof(bool),
            typeof(FocusBehavior),
            new UIPropertyMetadata(false, (s, e) => {
                var sender = (UIElement)s;
                if ((bool)e.NewValue) {
                    sender.Focus();
                    Keyboard.Focus(sender);
                }
                })
            );
}

XAML:

<TextBox local:FocusBehavior.IsFocused="{Binding Path=ScanBarCodeNow}" />

这做法虽然简单,但实际绑定的并非 UIElement.IsFocused 这只读属性,看CallBack 代码就知道,两个Focus() 都只是方法。界面操作焦点离开此控件时候,DependencyProperty 的值不会变化。后果是,设置一次 true 后,属性值 true,焦点离开它依然是 true,你在设置它 true,会被认定为值无变化,CallBack 没有执行,Focus() 不会运行。Workaround:先设 false 再设 true。

好傻吧。改一下,把 GotFocus 和 LostFocus 事件接上此 Attached Property,触发变更 DependencyProperty 的值,使它变成双向绑定。

using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;

namespace Lepton_Practical_MVVM_5 {
    public static class FocusBehavior {
        private static Dictionary<UIElement, RoutedEventHandler> handlers =
            new Dictionary<UIElement, RoutedEventHandler>();
        public static bool? GetIsFocused(DependencyObject obj) {
            return (bool?)obj.GetValue(IsFocusedProperty);
        }
        public static void SetIsFocused(DependencyObject obj, bool? value) {
            obj.SetValue(IsFocusedProperty, value);
        }
        public static readonly DependencyProperty IsFocusedProperty =
                DependencyProperty.RegisterAttached(
                "IsFocused",
                typeof(bool?),
                typeof(FocusBehavior),
                new UIPropertyMetadata() {
                    DefaultValue = null,
                    PropertyChangedCallback =
                        (s, e) => {
                            UIElement sender = (UIElement)s;
                            RoutedEventHandler x;
                            if (!handlers.TryGetValue(sender, out x)) {
                                Attach(sender);
                            }
                            if ((bool)e.NewValue) {
                                sender.Focus();
                                Keyboard.Focus(sender);
                            }
                        }
                });
        private static void Attach(UIElement sender) {
            RoutedEventHandler handler = (s, e) => {
                UIElement ui = (UIElement)s;
                if (e.RoutedEvent == UIElement.GotFocusEvent) {
                    ui.SetValue(IsFocusedProperty, true);
                }
                if (e.RoutedEvent == UIElement.LostFocusEvent) {
                    ui.SetValue(IsFocusedProperty, false);
                }
            };
            sender.GotFocus += handler;
            sender.LostFocus += handler;
            handlers.Add(sender, handler);
        }
    }
}

Dictionary 为了记下已连接的 Handler ,以免重复加入。

这做法是只有加句柄没有移除,你有情况需要移除的话,自己改吧。

发表评论
用户名: 匿名