基于WPF系统框架设计(7)-TextBox/PasswordBox在ViewModel中支持回车命令 应用场景 设计思想 实现步骤 测试

我现在做一个系统登录功能,要求在PasswordBox上输完密码后回车,能够响应Enter事件,并执行ViewModel中对应的方法。如果登录成功则隐藏当前窗口显示主窗体,登录失败则焦点返回到用户名TextBox中,并全选文字,方便用户再重新输入。

这个在我们制造业自动化流程控制中,做防呆功能是很明显的,因为没有人为去参与。

如果像Winform一样的开发模式,就相对很简单了,现在是要在ViewModel,对一个初学者来说就相对地困难多了,那怎么办呢?

设计思想

自定义一个Command,支持多参数对象数组,把控件,事件传到ViewModel中。

实现步骤

  1. 自定义命令InteractiveCommand类,继承TriggerAction<DependencyObject>
// -----------------------------------------------------------------------
// <copyright file="InteractiveCommand.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace TLAgent.SecurityManager.WPF
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Interactivity;
    using System.Reflection;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class InteractiveCommand : TriggerAction<DependencyObject>
    {
        protected override void Invoke(object parameter)
        {
            if (base.AssociatedObject != null)
            {
                ICommand command = this.ResolveCommand();
                object[] tempObj = { parameter, CommandParameter };
                if ((command != null) && command.CanExecute(tempObj))
                {
                    command.Execute(tempObj);
                }
            }
        }

        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register
            ("CommandParameter", typeof(object), typeof(InteractiveCommand), new PropertyMetadata(null, OnCommandParameterChanged));

        private static void OnCommandParameterChanged
            (DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            InteractiveCommand ic = sender as InteractiveCommand;
            if (ic != null)
            {
                ic.SynchronizeElementState();
            }
        }

        private void SynchronizeElementState()
        {
            ICommand command = Command;
            if (command != null)
            {
                FrameworkElement associatedObject = AssociatedObject as FrameworkElement;
                if (associatedObject != null)
                {
                    associatedObject.IsEnabled = command.CanExecute(CommandParameter);
                }
            }
        }

        private ICommand ResolveCommand()
        {
            ICommand command = null;
            if (this.Command != null)
            {
                return this.Command;
            }
            if (base.AssociatedObject != null)
            {
                foreach (PropertyInfo info in base.AssociatedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    if (typeof(ICommand).IsAssignableFrom(info.PropertyType) && string.Equals(info.Name, this.CommandName, StringComparison.Ordinal))
                    {
                        command = (ICommand)info.GetValue(base.AssociatedObject, null);
                    }
                }
            }
            return command;
        }

        private string commandName;
        public string CommandName
        {
            get
            {
                base.ReadPreamble();
                return this.commandName;
            }
            set
            {
                if (this.CommandName != value)
                {
                    base.WritePreamble();
                    this.commandName = value;
                    base.WritePostscript();
                }
            }
        }

        #region Command
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand), typeof(InteractiveCommand), new UIPropertyMetadata(null));
        #endregion
    }
}

    2.   在XAML中做绑定,把EventName定为”KeyDown“,请参考如下代码:

<PasswordBox x:Name="txtPassword" Height="23" Helper:PasswordBoxHelper.Attach="True" Helper:PasswordBoxHelper.Password="{Binding Path=AuthUser.Password,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="101,92,0,0"  VerticalAlignment="Top" Width="178" TabIndex="2">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="KeyDown">
                    <Helper:InteractiveCommand Command="{Binding EnterLoginCommand}" CommandName="EnterLoginCommand" CommandParameter="{Binding ElementName=txtUserName}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </PasswordBox>

3.   后台的Command声明为DelegateCommand<object[]><object[]><object[]><object[]> _CommandWithEventArgs,并实例化,同时绑定一个带参数object[]的用户验证方法:

public ICommand EnterLoginCommand
        {
            get { return _CommandWithEventArgs ?? (_CommandWithEventArgs = new DelegateCommand<object[]>(CheckUser)); }
        }

绑定的方法为CheckUser,如下

private void CheckUser(object[] objs)
        {
            KeyEventArgs e = objs[0] as KeyEventArgs;
            if (e.Key == Key.Enter)
            {
                object obj = objs[1];
                VerifyUser(obj);
            } 
        }
        private void VerifyUser(object objParam)
        {
            if (AuthUser.UserName != null && AuthUser.Password != null)
            {
                if (AuthUser.UserName.ToUpper().Equals("AGAN") && AuthUser.Password.Equals("123"))
                {
                    IsVisibility = Visibility.Hidden;
                    SplashScreen splashScreen = new SplashScreen("Images/SplashScreen.JPG");
                    splashScreen.Show(true);
                    MainWindow window = new MainWindow();
                    window.ShowDialog();
                }
                else
                {
                    MessageBox.Show(@"用户名或密码错误!");
                    TextBox txtUserName = (TextBox)objParam;
                    txtUserName.Focus();
                    txtUserName.SelectAll();
                }
            }
            else
            {
                MessageBox.Show(@"用户名或密码不能为空!");
                TextBox txtUserName = (TextBox)objParam;//转换为TextBox,即为View层的txtUserName控件
                txtUserName.Focus();//获取焦点
                txtUserName.SelectAll();//全选文本
            }
        }

测试

运行程序后,输入用户密码,显示如图:

基于WPF系统框架设计(7)-TextBox/PasswordBox在ViewModel中支持回车命令
应用场景
设计思想
实现步骤
测试

基于WPF系统框架设计(7)-TextBox/PasswordBox在ViewModel中支持回车命令
应用场景
设计思想
实现步骤
测试