Команды Microsoft Community Toolkit RelayCommands не работают должным образом
Мы переносим наш проект WPF из библиотеки MVVMLight в библиотеку Microsoft CommunityToolkit.
Мы пошли дальше, следуя общей документации по миграции Microsoft , и соответствующим образом обновили все наши (около 1200) команды.
Когда мы впоследствии получили сборку, мы заметили, что события в проекте не срабатывали должным образом, а команды не работали.
Однако метод SetProperty() не запускает мои RelayCommands.
Ниже мы решили эту проблему только для одной команды нашей пользовательской модели. Существует более 100+ моделей и более 1200 команд.
BaseViewModel.cs
public class BaseViewModel : ObservableObject, IDisposable
{
protected bool _disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public virtual void Dispose(bool disposing)
{
if (_disposed.Equals(false))
{
if (disposing)
{
}
_disposed = true;
}
}
private bool _isSaved;
public bool IsSaved
{
get { return _isSaved; }
set
{
SetProperty(ref _isSaved, value, nameof(_isSaved));
}
}
}
ЛогинВиевМодель
public class LoginViewModel : BaseViewModel
{
public LoginViewModel()
{
User = new UserModel();
User.PropertyChanged += User_PropertyChanged; // WHAT WE NEWLY ADDED
LoginCommand = new RelayCommand<Window>(OnLoginCommandExecuted, CanLoginCommandExecute);
CancelCommand = new RelayCommand<Window>(OnCancelCommandExecuted, CanCancelCommandExecute);
}
private void User_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (LoginCommand is not null) LoginCommand.NotifyCanExecuteChanged(); // We should notify it manually..
}
private UserModel _user;
public UserModel User
{
get { return _user; }
set
{
SetProperty(ref _user, value, nameof(_user));
}
}
public RelayCommand<Window>? LoginCommand { get; private set; }
public async void OnLoginCommandExecuted(Window window)
{
//DO STUFF
}
public bool CanLoginCommandExecute(Window window)
{
//DO STUFF
}
}
Логинвиндов.xaml
<Window
x:Class="ProjectName.LoginWindow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:Name="loginWindow">
<Button x:Name="btnLogin" Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=loginWindow}" />
</Window>
Есть ли способ запустить диспетчер NotifyCanExecuteChanged для всех команд в активных моделях?
Что я могу сделать в этой ситуации? Нужно ли применять следующую реализацию для всех моделей и команд? Это ожидаемая ситуация? Спасибо и счастливого кодирования.
РЕДАКТИРОВАТЬ: я изменил весь проект с аннотациями.
ЛогинВиевМодель
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(LoginCommandExecutedCommand))]
private UserModel _user = new();
[RelayCommand(CanExecute = nameof(CanLoginCommandExecute))]
private async void OnLoginCommandExecuted(Window window)
{
//DO STUFF
}
private bool CanLoginCommandExecute(Window window)
{
// DO STUFF LOGIC
}
Модель пользователя
[ObservableProperty]
private string _username;
partial void OnUsernameChanged(string value)
{
XtraMessageBox.Show("I HAVE CHANGED - USERNAME");
IsDirty = true;
}
[ObservableProperty]
private string _password;
partial void OnPasswordChanged(string value)
{
XtraMessageBox.Show("I HAVE CHANGED - PASSWORD");
IsDirty = true;
}
Он запускает изменения, но уведомляет команды. Причина UserModel не меняется. Свойства UserModels изменены.
То, что мы видим, свойства модели изменены. И модель пользователя LoginViewModel обновлена. Но он не выполняет мои команды. Потому что это только ПОЛУЧЕНИЕ моей UserModel, а не настройка. Автоматически сгенерированный код уведомляет только при установке UserModel. Что я должен делать?
1 ответ
Есть несколько вещей, которые вы можете изменить.
В частичной модели представления класса вы можете сделать:
[ObservableProperty]
[AlsoNotifyChangeFor(nameof(FullName))]
[AlsoNotifyCanExecuteFor(nameof(SaveCommand))]
private bool _isSaved = true;
partial void OnIsSavedChanged(bool newValue)
{
// Called when IsSaved property changes
}
Стандартный код для свойства IsSaved и уведомления об изменении создается в разделяемом классе генератором кода. Это уменьшит риск нарушения уведомлений об изменениях (например, при использовании поля, а не свойства), потому что вам нужно определить только закрытый член.
Вы можете добавить частичные методы onchanged и onchanged, которые будут вызываться при изменении или скором изменении имени соответствующего свойства.
Когда IsSaved изменится, для команды будет поднят атрибут canexecutechanged.
Команда relay имеет явный метод notifycanexecutechanged.
Если ни один из атрибутов не подходит, вы можете просто сделать:
CommandManager.InvalidateRequerySuggested()
Или вы можете перебирать команды relay в модели представления и вызывать для них notifycanexecutechanged.
Также обратите внимание, что исходный код для mvvmlight доступен.
Я использовал его в одном проекте .net6.