Ввод данных в текстовое поле с фокусом в приложении Avalonia MVVM

Вопрос или проблема

Я пишу приложение для сенсорного экрана, используя .NET 8, Avalonia UI и Community Toolkit MVVM.

В моем представлении есть три поля ввода TextBox. Я создал UserControl, который представляет собой числовую клавиатуру, которая будет использоваться в представлении для генерации ввода в одно из полей TextBox, которое в данный момент активно. Поля TextBox должны становиться активными при нажатии.

Мой вопрос состоит в том, как мне структурировать все это, чтобы следовать паттерну MVVM?

*Содержимое TextBox связано с свойствами в модели представления.

  • Команды кнопок числовой клавиатуры связаны с RelayCommand в модели представления.
  • Событие GotFocus должно обрабатываться в коде позади, верно?

Мои попытки:

  • Я использовал один подход, при котором активное поле ввода передается в поле в модели представления. Модель представления затем редактирует это поле и может сохранять его активным. Это явно нарушает MVVM, так как TextBox является элементом представления..
  • Я также использовал другой похожий подход с полем для активного TextBox в коде позади представления. Здесь команды кнопок числовой клавиатуры также были связаны с функциями в коде позади представления.

Чтобы сохранить паттерн MVVM, свяжите TextBox с свойствами в модели представления и обрабатывайте GotFocus в коде позади, чтобы отслеживать текущее активное поле TextBox. Храните идентификатор активного TextBox в модели представления, и связывайте команды кнопок числовой клавиатуры с RelayCommands, которые обновляют соответствующее свойство в зависимости от текущей фокусировки. Это позволяет сохранять бизнес-логику в модели представления, позволяя обрабатывать фокусировку, специфичную для представления, в коде позади.

Ответ или решение

Для разработки приложения на .NET 8 с использованием Avalonia UI и паттерна MVVM, которое содержит несколько TextBox и пользовательскую панель ввода (нумпад), важно соблюсти принципы MVVM, избегая нарушения их с помощью прямых манипуляций с элементами представления из модели представления. Вот как можно организовать ваше приложение:

Шаг 1: Структура ViewModel

Создайте ViewModel, который будет содержать свойства для каждого из TextBox, а также свойство для отслеживания того, какой TextBox в данный момент фокусирован.

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

public class MainViewModel : INotifyPropertyChanged
{
    private string _textBox1;
    private string _textBox2;
    private string _textBox3;
    private string _focusedTextBoxIdentifier;

    public string TextBox1
    {
        get => _textBox1;
        set => SetProperty(ref _textBox1, value);
    }

    public string TextBox2
    {
        get => _textBox2;
        set => SetProperty(ref _textBox2, value);
    }

    public string TextBox3
    {
        get => _textBox3;
        set => SetProperty(ref _textBox3, value);
    }

    public string FocusedTextBoxIdentifier
    {
        get => _focusedTextBoxIdentifier;
        set => SetProperty(ref _focusedTextBoxIdentifier, value);
    }

    public ICommand NumpadCommand => new RelayCommand<string>(OnNumpadButtonPressed);

    private void OnNumpadButtonPressed(string number)
    {
        if (FocusedTextBoxIdentifier == "TextBox1")
        {
            TextBox1 += number;
        }
        else if (FocusedTextBoxIdentifier == "TextBox2")
        {
            TextBox2 += number;
        }
        else if (FocusedTextBoxIdentifier == "TextBox3")
        {
            TextBox3 += number;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(storage, value))
            return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

Шаг 2: Рабочая среда (View)

В вашем XAML-файле создайте три TextBox и пользовательский контрол для нумпада. Обработайте событие GotFocus для TextBox, чтобы запомнить, какой из них в данный момент фокусирован.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             DataContext="{Binding MainViewModel, Source={StaticResource Locator}}">
    <StackPanel>
        <TextBox Text="{Binding TextBox1}"
                 GotFocus="OnTextBoxGotFocus"
                 Tag="TextBox1"/>
        <TextBox Text="{Binding TextBox2}"
                 GotFocus="OnTextBoxGotFocus"
                 Tag="TextBox2"/>
        <TextBox Text="{Binding TextBox3}"
                 GotFocus="OnTextBoxGotFocus"
                 Tag="TextBox3"/>

        <Numpad Command="{Binding NumpadCommand}"/>
    </StackPanel>
</UserControl>

Шаг 3: Обработчик событий в code-behind

В вашем файле code-behind напишите обработчик для события GotFocus, который будет обновлять идентификатор фокусированного TextBox в ViewModel.

using Avalonia.Controls;

public partial class YourUserControl : UserControl
{
    public YourUserControl()
    {
        InitializeComponent();
    }

    private void OnTextBoxGotFocus(object sender, GotFocusEventArgs e)
    {
        if (sender is TextBox textBox && textBox.Tag is string tag)
        {
            var viewModel = (MainViewModel)DataContext;
            viewModel.FocusedTextBoxIdentifier = tag;
        }
    }
}

Заключение

Эта структура позволяет вам сохранить логику в модели представления, избегая привязки пользовательского интерфейса напрямую к ViewModel, что соответствует принципам MVVM. Обработка фокуса выполняется на уровне представления (code-behind), добавляя простоту и чистоту к вашему коду.

Оцените материал
Добавить комментарий

Капча загружается...