You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin MVVM模式下检测回车键(或任意按键)的实现方法

问题解答:MVVM模式下检测Entry的回车键/按键事件

嘿,作为Xamarin新手,你提的这个问题真的很典型,咱们一步步来拆解解决:首先明确给密码Entry添加命令是完全符合MVVM模式的正确方案,另外你提到的Completed事件也可以很好地和ViewModel关联,下面给你两种实用的实现方式,结合你的现有代码修改:


第一步:完善ViewModel的命令基础

你的ViewModel目前缺少ICommand的实现,咱们先添加一个通用的RelayCommand类(这是MVVM里常用的命令实现),然后给LoginViewModel添加对应的命令:

1. 添加RelayCommand类

using System;
using System.Windows.Input;

namespace newApp
{
    public class RelayCommand : ICommand
    {
        private readonly Action _execute;
        private readonly Func<bool> _canExecute;

        public event EventHandler CanExecuteChanged;

        public RelayCommand(Action execute, Func<bool> canExecute = null)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter) => _canExecute == null || _canExecute();

        public void Execute(object parameter) => _execute();

        public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

2. 更新LoginViewModel

给ViewModel添加登录命令和密码框回车对应的命令,同时完善构造函数:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;

namespace newApp
{
    class LoginViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        string username, password;
        bool bol;

        void OnPropertyChanged([CallerMemberName] string name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public string Password
        {
            get { return password; }
            set { password = value; OnPropertyChanged(); }
        }

        public string Username
        {
            get { return username; }
            set { username = value; OnPropertyChanged(); OnPropertyChanged(nameof(DisplayMessage)); }
        }

        public string DisplayMessage
        {
            get { return !string.IsNullOrEmpty(username) ? $"This is your {Username}" : ""; }
        }

        // 登录命令(按钮和回车都可以调用)
        public ICommand LogInCommand { get; }
        // 密码框Completed事件对应的命令
        public ICommand PasswordCompletedCommand { get; }

        public LoginViewModel()
        {
            // 绑定命令到Login方法
            LogInCommand = new RelayCommand(Login);
            PasswordCompletedCommand = new RelayCommand(Login);
        }

        void Login()
        {
            // 这里写你的登录逻辑
            if (string.IsNullOrEmpty(username))
            {
                bol = false;
                // 可以更新提示信息
                OnPropertyChanged(nameof(DisplayMessage));
            }
            else
            {
                // 登录成功的逻辑,比如导航到主页等
            }
        }
    }
}

方法一:后台代码绑定Completed事件(新手友好)

这种方式不需要额外写行为类,直接在页面后台把Entry的Completed事件绑定到ViewModel的命令:

修改XAML(给密码Entry加名字)

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:newApp"
             x:Class="newApp.MainPage">
    <ContentPage.Content>
        <StackLayout Padding="30" Spacing="10" VerticalOptions="Center">
            <Image Source="logo.png" />
            <Entry Text="{Binding Username}" Placeholder="Username" />
            <!-- 给Entry加x:Name -->
            <Entry x:Name="PasswordEntry" Text="{Binding Password}" IsPassword="True" Placeholder="Password" />
            <Label Text="{Binding DisplayMessage}"/>
            <Button Text="Log In" Command="{Binding LogInCommand}" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

修改后台代码(绑定事件)

using Xamarin.Forms;

namespace newApp
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            BindingContext = new LoginViewModel();
            
            // 把PasswordEntry的Completed事件绑定到ViewModel的命令
            PasswordEntry.Completed += (sender, e) =>
            {
                if (BindingContext is LoginViewModel vm)
                {
                    vm.PasswordCompletedCommand.Execute(null);
                }
            };
        }
    }
}

方法二:使用EventToCommandBehavior(纯MVVM风格)

这种方式不需要在后台写逻辑,完全通过XAML绑定,更符合MVVM的“视图和逻辑分离”原则:

1. 添加EventToCommandBehavior类

using System;
using System.Reflection;
using Xamarin.Forms;

namespace newApp
{
    public class EventToCommandBehavior : Behavior<View>
    {
        public static readonly BindableProperty EventNameProperty =
            BindableProperty.Create(nameof(EventName), typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);

        public static readonly BindableProperty CommandProperty =
            BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(EventToCommandBehavior), null);

        public static readonly BindableProperty CommandParameterProperty =
            BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(EventToCommandBehavior), null);

        private Delegate _eventHandler;
        private EventInfo _eventInfo;

        public string EventName
        {
            get => (string)GetValue(EventNameProperty);
            set => SetValue(EventNameProperty, value);
        }

        public ICommand Command
        {
            get => (ICommand)GetValue(CommandProperty);
            set => SetValue(CommandProperty, value);
        }

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

        protected override void OnAttachedTo(View bindable)
        {
            base.OnAttachedTo(bindable);
            RegisterEvent(EventName);
        }

        protected override void OnDetachingFrom(View bindable)
        {
            DeregisterEvent(EventName);
            base.OnDetachingFrom(bindable);
        }

        private void RegisterEvent(string eventName)
        {
            if (string.IsNullOrWhiteSpace(eventName))
                return;

            _eventInfo = AssociatedObject.GetType().GetRuntimeEvent(eventName);
            if (_eventInfo == null)
                throw new ArgumentException($"Event {eventName} not found on {AssociatedObject.GetType().Name}");

            MethodInfo methodInfo = typeof(EventToCommandBehavior).GetRuntimeMethod(nameof(OnEvent), new[] { typeof(object), typeof(EventArgs) });
            _eventHandler = methodInfo.CreateDelegate(_eventInfo.EventHandlerType, this);
            _eventInfo.AddEventHandler(AssociatedObject, _eventHandler);
        }

        private void DeregisterEvent(string eventName)
        {
            if (_eventHandler == null || _eventInfo == null)
                return;

            _eventInfo.RemoveEventHandler(AssociatedObject, _eventHandler);
            _eventHandler = null;
            _eventInfo = null;
        }

        private void OnEvent(object sender, EventArgs e)
        {
            if (Command == null)
                return;

            object parameter = CommandParameter ?? e;
            if (Command.CanExecute(parameter))
                Command.Execute(parameter);
        }

        private static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var behavior = (EventToCommandBehavior)bindable;
            if (behavior.AssociatedObject == null)
                return;

            string oldEventName = (string)oldValue;
            string newEventName = (string)newValue;

            behavior.DeregisterEvent(oldEventName);
            behavior.RegisterEvent(newEventName);
        }
    }
}

2. 修改XAML(添加Behavior)

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:newApp"
             x:Class="newApp.MainPage">
    <ContentPage.Content>
        <StackLayout Padding="30" Spacing="10" VerticalOptions="Center">
            <Image Source="logo.png" />
            <Entry Text="{Binding Username}" Placeholder="Username" />
            <!-- 添加Behavior绑定Completed事件到命令 -->
            <Entry Text="{Binding Password}" IsPassword="True" Placeholder="Password">
                <Entry.Behaviors>
                    <local:EventToCommandBehavior EventName="Completed" Command="{Binding PasswordCompletedCommand}" />
                </Entry.Behaviors>
            </Entry>
            <Label Text="{Binding DisplayMessage}"/>
            <Button Text="Log In" Command="{Binding LogInCommand}" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

额外说明

  • 如果需要检测任意按键(不止回车键),可以用Entry的TextChanged事件结合按键判断,或者自定义Renderer,但对于登录场景,Completed事件(回车/完成按钮)已经足够。
  • 两种方法都能实现需求,方法一适合快速上手,方法二更贴近MVVM的设计理念,你可以根据自己的习惯选择。

内容的提问来源于stack exchange,提问作者Haani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:50:51