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

Maui自定义Entry行为导致ViewModel数组Set调用失效求助

问题分析

你遇到的核心问题是:数组是引用类型,修改数组内的单个元素时,数组本身的引用并没有改变,所以ViewModel中CodeDigits的Set方法永远不会被触发——只有当你把整个数组替换成新的实例时,Set才会执行。另外你的行为中还存在事件与Command绑定不匹配的问题,导致ViewModel无法正确接收文本变更通知。


解决方案

1. 修复行为与ViewModel的Command绑定问题

你的行为中定义的EntryTextChanged是EventHandler<TextChangedEventArgs>类型,但XAML里绑定的是ICommand,两者不兼容,需要把行为中的事件改成Command属性:

public class AutoFocusBehavior : Behavior<Entry>
{
    private string previousText = string.Empty;

    // 替换原有事件为Command属性
    public static readonly BindableProperty TextChangedCommandProperty =
        BindableProperty.Create(nameof(TextChangedCommand), 
                                typeof(ICommand), 
                                typeof(AutoFocusBehavior), 
                                null);

    public ICommand TextChangedCommand
    {
        get => (ICommand)GetValue(TextChangedCommandProperty);
        set => SetValue(TextChangedCommandProperty, value);
    }

    // 原有OnAttachedTo、OnDetachingFrom、OnEntryFocused逻辑不变

    private void OnEntryTextChanged(object sender, TextChangedEventArgs e)
    {
        if (sender is Entry entry)
        {
            if (e.NewTextValue.Length == 0 && e.OldTextValue.Length == 1)
            {
                if (!MoveFocusToPreviousEntry(entry))
                {
                    entry.Text = string.Empty;
                }
            }
            else if (e.NewTextValue.Length == 1)
            {
                entry.Unfocus();
                MoveFocusToNextEntry(entry);
            }

            // 调用ViewModel的Command,传递文本变更参数
            TextChangedCommand?.Execute(e);
        }
    }

    // 原有MoveFocusToNextEntry、MoveFocusToPreviousEntry逻辑不变
}

然后修改XAML中的绑定:

<Entry WidthRequest="40" Text="{Binding CodeDigits[0], Mode=TwoWay}" 
       Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center">
    <Entry.Behaviors>
        <b:AutoFocusBehavior TextChangedCommand="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
</Entry>
<!-- 其他Entry同理修改绑定 -->

2. 解决数组元素变更不触发通知的问题

方案A:改用ObservableCollection代替数组

ObservableCollection会自动监听元素变更,更适合这种需要跟踪单个元素变化的场景:

private ObservableCollection<string> _codeDigits;
public ObservableCollection<string> CodeDigits
{
    get { return _codeDigits; }
    set
    {
        if (_codeDigits != value)
        {
            _codeDigits = value;
            RaisePropertyChanged(nameof(CodeDigits));
            UpdateVerifyButtonState();
        }
    }
}

public YourViewModel()
{
    // 初始化8个空字符串元素
    CodeDigits = new ObservableCollection<string>(Enumerable.Repeat(string.Empty, 8));
    // 监听元素变更事件
    CodeDigits.CollectionChanged += (s, e) => UpdateVerifyButtonState();
}

private void UpdateVerifyButtonState()
{
    RaisePropertyChanged(nameof(IsVerifyButtonEnabled));
}

// ViewModel中的Command
public ICommand EntryTextChangedCommand => new Command<TextChangedEventArgs>(args =>
{
    // 这里可以做额外的文本校验逻辑
});

方案B:保留数组,手动触发元素变更通知

如果不想替换数组,可以在每个Entry的文本变更时,手动触发对应索引的属性通知:

首先给XAML中的每个Entry添加Tag标记索引:

<Entry WidthRequest="40" Text="{Binding CodeDigits[0], Mode=TwoWay}" 
       Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center"
       Tag="0">
    <Entry.Behaviors>
        <b:AutoFocusBehavior TextChangedCommand="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
</Entry>
<!-- 其他Entry依次设置Tag为1~7 -->

然后在ViewModel的Command中处理:

public ICommand EntryTextChangedCommand => new Command<TextChangedEventArgs>(args =>
{
    if (args.OriginalSource is Entry entry && int.TryParse(entry.Tag.ToString(), out int index))
    {
        // 手动触发对应索引的属性通知
        RaisePropertyChanged($"CodeDigits[{index}]");
        // 更新按钮状态
        RaisePropertyChanged(nameof(IsVerifyButtonEnabled));
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:34:50