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

MAUI:如何通过MVVM将4位验证码绑定至ViewModel

MAUI验证码自定义控件绑定与状态传递实现方案

核心思路

不需要使用隐藏<Entry>,而是通过给自定义控件**添加可绑定属性(BindableProperty)**来实现验证码拼接和状态传递,既满足内部输入逻辑,又能让外部页面/ViewModel轻松获取PinEntry和IsComplete值。


步骤1:给自定义控件添加可绑定属性

在PinCodeView.xaml.cs中定义两个可绑定属性,用于外部绑定:

public partial class PinCodeView : ContentView
{
    public PinCodeView()
    {
        InitializeComponent();
    }

    // 完整验证码的可绑定属性,支持双向绑定
    public static readonly BindableProperty PinEntryProperty =
        BindableProperty.Create(nameof(PinEntry), typeof(string), typeof(PinCodeView), string.Empty, BindingMode.TwoWay, propertyChanged: OnPinEntryChanged);

    public string PinEntry
    {
        get => (string)GetValue(PinEntryProperty);
        set => SetValue(PinEntryProperty, value);
    }

    // 输入完成状态的可绑定属性
    public static readonly BindableProperty IsCompleteProperty =
        BindableProperty.Create(nameof(IsComplete), typeof(bool), typeof(PinCodeView), false);

    public bool IsComplete
    {
        get => (bool)GetValue(IsCompleteProperty);
        set => SetValue(IsCompleteProperty, value);
    }

    // 可选:当外部手动设置PinEntry时,同步到四个输入框
    private static void OnPinEntryChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is PinCodeView pinView && newValue is string pin)
        {
            pinView.Pin1.Text = pin.Length >= 1 ? pin[0].ToString() : string.Empty;
            pinView.Pin2.Text = pin.Length >= 2 ? pin[1].ToString() : string.Empty;
            pinView.Pin3.Text = pin.Length >= 3 ? pin[2].ToString() : string.Empty;
            pinView.Pin4.Text = pin.Length >= 4 ? pin[3].ToString() : string.Empty;
            pinView.UpdateIsComplete();
        }
    }
}

步骤2:完善输入框的TextChanged和焦点切换逻辑

更新每个输入框的TextChanged事件,实现输入后自动跳转焦点、删除后自动回退焦点,同时实时拼接验证码并更新PinEntry和IsComplete:

// Pin1的TextChanged
private void Pin1_TextChanged(object sender, TextChangedEventArgs e)
{
    if (!string.IsNullOrEmpty(e.NewTextValue) && e.NewTextValue.Length == 1)
    {
        Pin2.Focus();
    }
    UpdatePinEntryAndStatus();
}

// Pin2的TextChanged
private void Pin2_TextChanged(object sender, TextChangedEventArgs e)
{
    if (!string.IsNullOrEmpty(e.NewTextValue) && e.NewTextValue.Length == 1)
    {
        Pin3.Focus();
    }
    else if (string.IsNullOrEmpty(e.NewTextValue) && !string.IsNullOrEmpty(e.OldTextValue))
    {
        Pin1.Focus();
        Pin1.Text = string.Empty;
    }
    UpdatePinEntryAndStatus();
}

// Pin3的TextChanged
private void Pin3_TextChanged(object sender, TextChangedEventArgs e)
{
    if (!string.IsNullOrEmpty(e.NewTextValue) && e.NewTextValue.Length == 1)
    {
        Pin4.Focus();
    }
    else if (string.IsNullOrEmpty(e.NewTextValue) && !string.IsNullOrEmpty(e.OldTextValue))
    {
        Pin2.Focus();
        Pin2.Text = string.Empty;
    }
    UpdatePinEntryAndStatus();
}

// Pin4的TextChanged
private void Pin4_TextChanged(object sender, TextChangedEventArgs e)
{
    if (string.IsNullOrEmpty(e.NewTextValue) && !string.IsNullOrEmpty(e.OldTextValue))
    {
        Pin3.Focus();
        Pin3.Text = string.Empty;
    }
    UpdatePinEntryAndStatus();
}

// 核心方法:拼接验证码并更新状态
private void UpdatePinEntryAndStatus()
{
    var pin = $"{Pin1.Text}{Pin2.Text}{Pin3.Text}{Pin4.Text}";
    PinEntry = pin.Trim();
    UpdateIsComplete();
}

// 更新输入完成状态
private void UpdateIsComplete()
{
    IsComplete = !string.IsNullOrEmpty(Pin1.Text) 
                && !string.IsNullOrEmpty(Pin2.Text) 
                && !string.IsNullOrEmpty(Pin3.Text) 
                && !string.IsNullOrEmpty(Pin4.Text);
}

步骤3:外部页面绑定ViewModel属性

在使用PinCodeView的页面XAML中,直接绑定到ViewModel的PinEntry和IsComplete属性:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:Pincode.Controls"
             x:Class="Pincode.Views.PinInputPage"
             BindingContext="{StaticResource PinCodeViewModel}">

    <StackLayout>
        <!-- 绑定自定义控件的属性到ViewModel -->
        <controls:PinCodeView 
            PinEntry="{Binding PinEntry, Mode=TwoWay}"
            IsComplete="{Binding IsComplete, Mode=OneWayToSource}"/>

        <!-- 示例:根据IsComplete启用验证按钮 -->
        <Button 
            Text="验证验证码"
            IsEnabled="{Binding IsComplete}"
            Command="{Binding VerifyPinCommand}"/>
    </StackLayout>
</ContentPage>

步骤4:ViewModel验证逻辑(可选)

在PinCodeViewModel中添加验证命令,利用绑定后的PinEntry进行验证:

public partial class PinCodeViewModel : ObservableObject
{
    [ObservableProperty]
    string _pinEntry = string.Empty;

    [ObservableProperty]
    bool _isComplete = false;

    [RelayCommand]
    void VerifyPin()
    {
        // 这里使用PinEntry做验证逻辑
        if (PinEntry == "1234")
        {
            // 验证成功逻辑
        }
        else
        {
            // 验证失败逻辑
        }
    }
}

关键说明

  1. 无需隐藏Entry:通过自定义控件的可绑定属性,直接将内部四个输入框的文本拼接后对外暴露,比隐藏Entry更简洁且易维护。
  2. 双向绑定支持:PinEntry属性设置为双向绑定,既可以从内部输入更新外部ViewModel,也可以从外部设置值同步到内部输入框(比如重置验证码)。
  3. 状态自动同步:每次输入/删除都会触发UpdatePinEntryAndStatus方法,实时更新PinEntry和IsComplete,外部页面无需手动处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 00:41:00