Проблемы с реализацией функции в C# Avalonia MVVM

Рейтинг: 0Ответов: 0Опубликовано: 18.04.2023

Разрабатываю "Тренажер английских слов" на C# Avalonia 11.0-preview4.
Но столкнулся с проблемой, так как не знаю, как реализовать нужный функционал.

В общем, есть два листбокса, оба выводят сущности класса Word.
Но Левый выводит атрибут EnglishVersion, А Правый RussianVersion
Левый выводит 10 слов по порядку, а Правый 10 слов в рандомном порядке
Подразумевается, что пользователь будет сначала выбирать Английское слово в Левом списке, а потом выбирать правильный по его мнению перевод из Правого списка
Решил удалять слова из Левого списка после выбора. + Хотелось бы, чтобы слова из Правого меняли цвет на
зеленый/красный в зависимости от правильности ответа соответственно.

Но столкнулся с непосредственными проблемами:

  1. Не получается обратиться к определенному элементу списка и его свойству для изменения цвета
  2. При выборе правильного ответа кидает эксепшн "System.NullReferenceException: "Object reference not set to an instance of an object." value было null."
  3. Нужно чтобы слова из Левого и Правого списка соответствовали друг другу. Сейчас, т.к в Правый список они выводятся рандомно, то для некоторых слов слева может не оказаться правильного перевода справа :(

    Ниже представлю код некоторых классов для понимания структуры программы:
    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>

Ответы

Ответов пока нет.