Команды 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.

https://learn.microsoft.com/en-us/dotnet/api/microsoft.toolkit.mvvm.input.relaycommand.notifycanexecutechanged?view=win-comm-toolkit-dotnet-7.1

Если ни один из атрибутов не подходит, вы можете просто сделать:

        CommandManager.InvalidateRequerySuggested()

Или вы можете перебирать команды relay в модели представления и вызывать для них notifycanexecutechanged.

Также обратите внимание, что исходный код для mvvmlight доступен.

Я использовал его в одном проекте .net6.

Другие вопросы по тегам