Вопрос или проблема
Я пишу приложение для сенсорного экрана, используя .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), добавляя простоту и чистоту к вашему коду.