毛伊岛自定义条目行为正在阻止 ViewModel 上的 Set 调用

问题描述 投票:0回答:1

我为 Entry 控件创建了一个自定义行为,该行为允许 Entry 控件的集合提供一个单位数字代码输入字段,在输入值时将光标移动到下一个字段(向右),并移动到上一个字段删除值时的字段(左侧)。这大多有效。实际行为中仍然存在一些错误需要解决。我现在试图解决的问题是,作为 ViewModel 中支持字段的字符串数组没有得到设置调用,这意味着我无法引发属性更改通知。

下面列出了自定义行为代码..

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

    public static readonly BindableProperty EntryTextChangedProperty =
        BindableProperty.Create(nameof(EntryTextChanged),
                                typeof(EventHandler<TextChangedEventArgs>),
                                typeof(AutoFocusBehavior),
                                null);

    public event EventHandler<TextChangedEventArgs> EntryTextChanged;

    protected override void OnAttachedTo(Entry entry)
    {
        base.OnAttachedTo(entry);

        entry.Focused += OnEntryFocused;
        entry.TextChanged += OnEntryTextChanged;
    }

    protected override void OnDetachingFrom(Entry entry)
    {
        base.OnDetachingFrom(entry);
        entry.Focused -= OnEntryFocused;
        entry.TextChanged -= OnEntryTextChanged;
    }

    private void OnEntryFocused(object sender, FocusEventArgs e)
    {
        if (sender is Entry entry)
        {
            // Store the previous text if it's not null or empty
            if (!string.IsNullOrEmpty(entry.Text))
            {
                previousText = entry.Text;
            }

            // Select all text when the Entry is focused
            entry.SelectionLength = entry.Text?.Length ?? 0;
        }
    }

    private void OnEntryTextChanged(object sender, TextChangedEventArgs e)
    {
        if (sender is Entry entry)
        {
            if (e.NewTextValue.Length == 0 && e.OldTextValue.Length == 1)
            {
                // If the user deletes a character, move focus to the previous entry
                if (!MoveFocusToPreviousEntry(entry))
                {
                    // If it's the first entry, do not move focus
                    entry.Text = string.Empty;
                }
            }
            else if (e.NewTextValue.Length == 1)
            {
                // If the text length is 1, move focus to the next entry and clear the current entry
                entry.Unfocus();
                MoveFocusToNextEntry(entry);
            }

            EntryTextChanged?.Invoke(sender, e);
        }
    }


    private void MoveFocusToNextEntry(Entry currentEntry)
    {
        // Find the parent stack layout containing all the Entry elements
        if (currentEntry.Parent is StackLayout stackLayout)
        {
            // Get the index of the current Entry
            int currentIndex = stackLayout.Children.IndexOf(currentEntry);

            // Move focus to the next Entry if it exists
            if (currentIndex < stackLayout.Children.Count - 1)
            {
                Entry nextEntry = (Entry)stackLayout.Children[currentIndex + 1];
                nextEntry.Focus();
            }
        }
    }

    private bool MoveFocusToPreviousEntry(Entry currentEntry)
    {
        // Find the parent stack layout containing all the Entry elements
        if (currentEntry.Parent is StackLayout stackLayout)
        {
            // Get the index of the current Entry
            int currentIndex = stackLayout.Children.IndexOf(currentEntry);

            // If it's the first Entry and the text is empty, do not move focus
            if (currentIndex == 0 && string.IsNullOrEmpty(currentEntry.Text))
            {
                return false;
            }

            // Move focus to the previous Entry if it exists
            if (currentIndex > 0)
            {
                Entry previousEntry = (Entry)stackLayout.Children[currentIndex - 1];
                previousEntry.Focus();
                return true;
            }
        }

        return false;
    }
}

此代码绑定到的 XAML 是

<StackLayout x:Name="EntryStack" Orientation="Horizontal" HorizontalOptions="CenterAndExpand" Spacing="1" Grid.Row="1" Margin="0, 10">
  <!-- Code Entry Boxes -->
  <Entry WidthRequest="40" Text="{Binding CodeDigits[0], Mode=TwoWay}" Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center">
    <Entry.Behaviors>
      <b:AutoFocusBehavior EntryTextChanged="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
  </Entry>
  <Entry WidthRequest="40" Text="{Binding CodeDigits[1], Mode=TwoWay}" Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center">
    <Entry.Behaviors>
      <b:AutoFocusBehavior EntryTextChanged="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
  </Entry>
  <Entry WidthRequest="40" Text="{Binding CodeDigits[2], Mode=TwoWay}" Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center">
    <Entry.Behaviors>
      <b:AutoFocusBehavior EntryTextChanged="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
  </Entry>
  <Entry WidthRequest="40" Text="{Binding CodeDigits[3], Mode=TwoWay}" Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center">
    <Entry.Behaviors>
      <b:AutoFocusBehavior EntryTextChanged="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
  </Entry>
  <Entry WidthRequest="40" Text="{Binding CodeDigits[4], Mode=TwoWay}" Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center">
    <Entry.Behaviors>
      <b:AutoFocusBehavior EntryTextChanged="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
  </Entry>
  <Entry WidthRequest="40" Text="{Binding CodeDigits[5], Mode=TwoWay}" Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center">
    <Entry.Behaviors>
      <b:AutoFocusBehavior EntryTextChanged="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
  </Entry>
  <Entry WidthRequest="40" Text="{Binding CodeDigits[6], Mode=TwoWay}" Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center">
    <Entry.Behaviors>
      <b:AutoFocusBehavior EntryTextChanged="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
  </Entry>
  <Entry WidthRequest="40" Text="{Binding CodeDigits[7], Mode=TwoWay}" Keyboard="Numeric" MaxLength="1" HorizontalTextAlignment="Center">
    <Entry.Behaviors>
      <b:AutoFocusBehavior EntryTextChanged="{Binding EntryTextChangedCommand}" />
    </Entry.Behaviors>
  </Entry>
</StackLayout>

视图模型绑定到的属性是...

 public string[] CodeDigits
 {
    get { return _codeDigits; }
    set
    {
        if (_codeDigits != value)
        {
            _codeDigits = value;
            RaisePropertyChanged(nameof(CodeDigits));
            RaisePropertyChanged(nameof(IsVerifyButtonEnabled));
        }
    }
 }

我尝试了许多不同的方法来触发 setter,包括为行为添加 BindableProperty 以及从行为自己的 OnEntryTextChanged 处理程序手动触发 EntryTextChanged 事件。我在这里遗漏了一些基本的东西。感谢任何帮助/指导:-)

maui viewmodel setter behavior
1个回答
0
投票

您的 setter 不会被调用,因为它属于

CodeDigits
属性,而不是其中包含的字符串。

您可以尝试使用

ObservableCollection
而不是
string[]
,但我不确定它会改变什么。

我看到两种方法。

如果您知道需要多少个条目,假设 8 个,您可以使用 8 个

string
属性并为每个属性调用 RaisePropertyChanged。

否则,您必须创建一个更复杂的对象来实现

INotifyPropertyChanged
并具有
string
属性,然后将其中的 8 个对象存储在数组中。

© www.soinside.com 2019 - 2024. All rights reserved.