Проблемы с реализацией функции в C# Avalonia MVVM
Разрабатываю "Тренажер английских слов" на C# Avalonia 11.0-preview4.
Но столкнулся с проблемой, так как не знаю, как реализовать нужный функционал.
В общем, есть два листбокса, оба выводят сущности класса Word.
Но Левый выводит атрибут EnglishVersion, А Правый RussianVersion
Левый выводит 10 слов по порядку, а Правый 10 слов в рандомном порядке
Подразумевается, что пользователь будет сначала выбирать Английское слово в Левом списке, а потом выбирать правильный по его мнению перевод из Правого списка
Решил удалять слова из Левого списка после выбора. + Хотелось бы, чтобы слова из Правого меняли цвет на
зеленый/красный в зависимости от правильности ответа соответственно.
Но столкнулся с непосредственными проблемами:
- Не получается обратиться к определенному элементу списка и его свойству для изменения цвета
- При выборе правильного ответа кидает эксепшн "System.NullReferenceException: "Object reference not set to an instance of an object." value было null."
- Нужно чтобы слова из Левого и Правого списка соответствовали друг другу. Сейчас, т.к в Правый список они выводятся рандомно, то для некоторых слов слева может не оказаться правильного перевода справа :(
Ниже представлю код некоторых классов для понимания структуры программы:
Word.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace EnglishTrainer.Models;
public partial class Word
{
public int WordId { get; set; }
public int UserId { get; set; }
public string EnglishVersion { get; set; } = null!;
public string RussianVersion { get; set; } = null!;
public virtual User User { get; set; } = null!;
[NotMapped] public bool IsAnswered { get; set; } = false;
[NotMapped] public bool IsCorrect { get; set; } = false;
}
MainWindowViewModel.cs:
using Avalonia.Controls;
using EnglishTrainer.Models;
using EnglishTrainer.Sourses;
using EnglishTrainer.Views;
using MessageBox.Avalonia.Enums;
using MessageBox.Avalonia;
using ReactiveUI;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
namespace EnglishTrainer.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
#region [Private Fields]
#region [Constants]
private const int NotSelectedItem = -1;
#endregion
private MainWindow _mainWindow;
private ObservableCollection<Word> _englishWords;
private ObservableCollection<Word> _russianWords;
private Word _selectedEnglishWord;
private Word _selectedRussianWord;
#endregion
public MainWindowViewModel(MainWindow window)
{
_mainWindow = window;
var WordsList = DbContextProvider.GetContext().Words.Take(10).Where(x => x.UserId == AuthWindowVM.CurrentUser.UserId).ToList();
Random random = new Random();
RussianWords = new ObservableCollection<Word>(WordsList.OrderBy(x => random.Next()).ToList());
EnglishWords = new ObservableCollection<Word>(WordsList);
CancelCommand = ReactiveCommand.Create<Window>(CancelCommandImpl);
}
#region [Properties]
public ObservableCollection<Word> EnglishWords
{
get
{
return _englishWords;
}
set
{
this.RaiseAndSetIfChanged(ref _englishWords, value);
}
}
public ObservableCollection<Word> RussianWords
{
get
{
return _russianWords;
}
set
{
this.RaiseAndSetIfChanged(ref _russianWords, value);
}
}
public Word SelectedEnglishWord
{
get
{
return _selectedEnglishWord;
}
set
{
if (value.IsAnswered == true)
{
return;
}
this.RaiseAndSetIfChanged(ref _selectedEnglishWord, value);
}
}
public Word SelectedRussianWord
{
get
{
return _selectedRussianWord;
}
set
{
if (value.IsAnswered == true)
{
return;
}
this.RaiseAndSetIfChanged(ref _selectedRussianWord, value);
try
{
if (_selectedRussianWord != null)
{
if (_selectedRussianWord.WordId == _selectedEnglishWord.WordId)
{
MessageBoxManager.GetMessageBoxStandardWindow($"Ошибка", $"Правильно", ButtonEnum.Ok, Icon.Warning).Show();
_selectedRussianWord.IsAnswered = true;
_selectedEnglishWord.IsAnswered = true;
_selectedRussianWord.IsCorrect = true;
_selectedEnglishWord.IsCorrect = true;
EnglishWords.Remove(_selectedEnglishWord);
_mainWindow.Listbox_EnglishWords.SelectedItem = NotSelectedItem;
_mainWindow.Listbox_RussianWords.SelectedItem = NotSelectedItem;
}
else
{
MessageBoxManager.GetMessageBoxStandardWindow($"Ошибка", $"Неправильно", ButtonEnum.Ok, Icon.Warning).Show();
_selectedRussianWord.IsAnswered = true;
_selectedEnglishWord.IsAnswered = true;
}
}
else
{
throw new Exception();
}
}
catch
{
MessageBoxManager.GetMessageBoxStandardWindow($"Ошибка", $"Null", ButtonEnum.Ok, Icon.Warning).Show();
}
}
}
#region [Commands Declaration]
public ReactiveCommand<Window, Unit> CancelCommand { get; }
#endregion
#endregion
#region [Methods]
private async void CancelCommandImpl(Window window)
{
var menuWindow = new MenuWindow();
menuWindow.Show();
window.Close();
}
#endregion
}
}
MainWindow.axaml:
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:EnglishTrainer.ViewModels"
mc:Ignorable="d" Icon="/Assets/avalonia-logo.ico"
x:Class="EnglishTrainer.Views.MainWindow"
x:Name="Main"
d:DesignWidth="800" d:DesignHeight="400"
Width="800" Height="400"
MaxWidth="800" MaxHeight="400"
MinWidth="800" MinHeight="400"
Title="Main"
WindowStartupLocation="CenterScreen"
Background="#a8e4a0">
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<Window.Styles>
<Style Selector="Button">
<Setter Property="Background" Value="#228b22"/>
<Setter Property="CornerRadius" Value="10"/>
<Setter Property="CommandParameter" Value="{Binding ElementName=Main}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="ArialNova"/>
<Setter Property="IsEnabled" Value="True"/>
<Setter Property="IsVisible" Value="True"/>
</Style>
</Window.Styles>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="5,5,5,0" Grid.Row="0" Grid.Column="0">
<ListBox
Background="#a8e4a0" x:Name="Listbox_EnglishWords"
SelectedItem="{Binding SelectedEnglishWord}"
Items="{Binding EnglishWords}" Height="350"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock
Text="{Binding EnglishVersion}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextWrapping="Wrap"
FontSize="16" FontWeight="DemiBold"
Height="14"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
<StackPanel Margin="5,5,5,0" Grid.Row="0" Grid.Column="1">
<ListBox
Background="#a8e4a0" x:Name="Listbox_RussianWords"
SelectedItem="{Binding SelectedRussianWord}"
Items="{Binding RussianWords}" Height="350"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock
Text="{Binding RussianVersion}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextWrapping="Wrap"
FontSize="16" FontWeight="DemiBold"
Height="14"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
<StackPanel Margin="5,0,5,5" Grid.Row="1" Grid.Column="0">
<Button
Content="Дальше" x:Name="Btn_Continue"
Command="{Binding ContinueCommand}"
HorizontalAlignment="Right"
FontSize="16"
Width="75" Height="35"
Margin="0,5,0,0"/>
</StackPanel>
<StackPanel Margin="5,0,5,5" Grid.Row="1" Grid.Column="1">
<Button
Content="Назад" x:Name="Btn_Cancel"
Command="{Binding CancelCommand}"
HorizontalAlignment="Left"
FontSize="16"
Width="75" Height="35"
Margin="0,5,0,0"/>
</StackPanel>
</Grid>
</Window>