Как корректно определить состояние команд и передавать его?
У меня есть три команды, в которых определено условие, при котором они могут выполняться. В итоге у меня три кнопки одновременно в состоянии 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 шт):
Вам не нужны команды в дочернем окне для показа результата выбора пользователем. Заведите энумератор.
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}}"/>