Как корректно определить состояние команд и передавать его?

У меня есть три команды, в которых определено условие, при котором они могут выполняться. В итоге у меня три кнопки одновременно в состоянии Enabled, либо после нажатия любой из кнопок они находятся в состоянии Disabled, так как мне нужно поле Question класса StudentModel обнулять. Мне нужно, чтобы при нажатии одной из них, остальные кнопки становились Disabled и это состояние кнопок передавать другим кнопкам.

Класс с св-вом, которое мне нужно делать null:

public class StudentModel : INotifyPropertyChanged
{

    private string question;
    public string Question
    {
        get => question;
        set
        {
            if (question == value) return;
            question = value;
            RaisePropertyChanged(nameof(Question));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Команды:

public RelayCommand<StudentModel> yesCmd = null;

public RelayCommand<StudentModel> YesCmd => yesCmd ?? (yesCmd = new RelayCommand<StudentModel>((param) =>
    {
        param.Question = null;
        if (OnTick != param.UpTime)
        {
            OnTick += param.UpTime;
        }
    },
    (stdParam) =>
    {
        return (stdParam != null) ? !string.IsNullOrEmpty(stdParam.Question) : false;
    }));

    public RelayCommand<StudentModel> noCmd = null;
    public RelayCommand<StudentModel> NoCmd => noCmd ?? (noCmd = new RelayCommand<StudentModel>((param) =>
    {
        param.Question = null;
        if (OnTick != param.UpTime)
        {
            OnTick += param.UpTime;
        }
    },
    (stdParam) =>
    {
        return (stdParam != null) ? !string.IsNullOrEmpty(stdParam.Question) : false;
    }));

    public RelayCommand<StudentModel> dontKnowCmd = null;
    public RelayCommand<StudentModel> DontKnowCmd => dontKnowCmd ?? (dontKnowCmd = new RelayCommand<StudentModel>((param) =>
    {
        param.Question = null;
        if (OnTick != param.UpTime)
        {
            OnTick += param.UpTime;
        }
    },
    (param) =>
    {
        return (param != null) ? !string.IsNullOrEmpty(param.Question) : false;
    }));

Класс, где мне нужно добавить команды с состоянием команд описанных выше:

public class QuestionsViewModel : INotifyPropertyChanged
{
    MainWindowViewModel _MainWindowViewModel;

    #region
    private bool On = true;
    #endregion
    public QuestionsViewModel(MainWindowViewModel mainWindowViewModel)
    {
        _MainWindowViewModel = mainWindowViewModel;
    }

    RelayCommand<StudentModel> yesCMD = null;
    RelayCommand<StudentModel> YesCMD => yesCMD ?? (yesCMD = new RelayCommand<StudentModel>(
        (std) =>
        {

        },
        (std) =>
        {
            return _MainWindowViewModel.NoCmd.CanExecute(_MainWindowViewModel.NoCmd);
        }));

    RelayCommand<StudentModel> noCMD = null;
    RelayCommand<StudentModel> NoCMD => noCMD ?? (noCMD = new RelayCommand<StudentModel>(
        (std) =>
        {

        },
        (std) =>
        {
            return _MainWindowViewModel.NoCmd.CanExecute(_MainWindowViewModel.NoCmd);
        }));

    RelayCommand<StudentModel> dontknowCMD = null;
    RelayCommand<StudentModel> DontKnowCMD => dontknowCMD ?? (dontknowCMD = new RelayCommand<StudentModel>(
        (std) =>
        {

        },
        (std) =>
        {
            return _MainWindowViewModel.DontKnowCmd.CanExecute(_MainWindowViewModel.DontKnowCmd);
        }));

    public event PropertyChangedEventHandler PropertyChanged;

    public virtual void RaisePropertyChanged(string property)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }
}

Класс команды:

public class RelayCommand<T> : CommandBase
{
    private readonly Action<T> _execute;
    private readonly Func<T, bool> _canExecute;
    public RelayCommand(Action<T> execute) : this(execute, null) { }
    public RelayCommand(Action<T> execute, Func<T, bool> canExecute)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }
    public override bool CanExecute(object parameter) => _canExecute == null || _canExecute.Invoke((T)parameter);
    public override void Execute(object parameter) { _execute.Invoke((T)parameter); }
}

Базовый класс команд:

public abstract class CommandBase : ICommand
{
    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public abstract bool CanExecute(object parameter);
    public abstract void Execute(object parameter);
}

Ответы (1 шт):

Автор решения: aepot

Вам не нужны команды в дочернем окне для показа результата выбора пользователем. Заведите энумератор.

public enum Answer
{
    Yes,
    No,
    DontKnow
}

Класс для хранения разных вопросов

public class Question
{
    public string Text { get; set; }
    public Answer CorrectAnswer { get; set; }
}

И класс для записи результата

public class AnswerLog
{
    public Question Question { get; set; }
    public Answer Answer { get; set; }
}

Список разных вопросов

public ObservableCollection<Question> Questions;

Тогда создание вопроса будет выглядеть так

Question question = new Question()
{
    Text = "Есть ли в C# энумераторы?",
    CorrectAnswer = Answer.Yes
}
Questions.Add(question);

В команду передавайте то, что содержит текущий вопрос

Тогда проверка на правильность внутри команды будет выглядеть так

// YesCmd

// к примеру
// currentQuestion - текущий вопрос
// user.Answers - ObservableCollection<AnswerLog>
user.Answers.Add(new AnswerLog()
{
    Question = currentQuestion,
    Answer = Answer.Yes
});
if (currentQuestion.CorrectAnswer == Answer.Yes)
{
    MessageBox.Show("Правильно");
}
else
{
    MessageBox.Show("Не правильно");
}

Если вы перестроите логику хранения данных подобным образом, можно будет сразу показать значение правильного ответа, команды для отображения данных не служат, и у вас не получится это сделать с помощью CanExecute, либо это будет костыль размером с вселенную. Команды предназначены для того, чтобы что-то выполнять.

Фейковые отключенные кнопки можно и так, без команд показать, например с помощью DataTrigger.

<Button Content="Yes">
    <Button.Resources>
        <Style TargetType="{x:Type Button}">
            <Setter Property="IsEnabled" Value="False">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Answer}" Value="Yes">
                    <Setter Property="IsEnabled" Value="True"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Resources>
</Button>

А лучше вообще не кнопки, а какую-нибудь картинку с галочкой, или шрифтовую галочку в TextBlock, а через DataTrigger управлять ее свойством Visible.

Или вообще без наворотов текстом через конвертер

public class AnswerConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is Answer answer)
        {
            switch (answer)
            {
                case Answer.Yes:
                    return "Да"; break;
                case Answer.No:
                    return "Нет"; break;
                case Answer.DontKnow:
                    return "Не знаю"; break;                   
            }
        }
        return null;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => null;
}

Подключить конвертер

<Window.Resources>
    <local:AnswerConverter x:Key="AnswerConverter"/>
</Window.Resources>

И использовать где угодно, где надо получить значение энумератора локализованным текстом

<TextBlock Text="{Binding Answer, Converter={StaticResource AnswerConverter}}"/>
→ Ссылка