当调用事件操作时,不会触发WPF应用程序侦听方法

本文关键字:WPF 应用程序 方法 事件 调用 操作 | 更新日期: 2023-09-27 18:27:39

我有一个WPF MVVM应用程序。我有一个类叫MainWindowViewModel,还有一个类叫做TileMenuViewModel,它们都是从BaseViewModel派生而来的。基础所做的只是实现INotifyPropertyChangedINotifyDataErrorInfo,同时保留一个错误的Dictionary<string, List<string>>进行验证。

在我的MainWindowViewModel中,我实例化了的一个实例

private TileMenuViewModel _tileMenuViewModel = new TileMenuViewModel();

在我的TileMenuViewModel中,我有一个public event Action NavToCampaings = new delegate {};

现在在MainWindowViewModel构造函数中,我订阅了以下事件:

_tileMenuViewModel.NavToCampaings += OnNavToCampaigns;

然而,当我在某个时刻从TileMenuViewModel调用事件时,就像NavToCampaigns();一样,我的订阅事件(MainWindowViewModelOnNavToCampaigns)不会被调用。当我使用断点进行调试时,我可以看到NavToCampaigns();事件Action被调用,但其值为null。调试器在通过MainWindowViewModel构造时也不会进入TileMenuViewModel,我正在该构造中实例化TileMenuViewModel。然而,我的程序的其余部分按我的意愿工作,我看到平铺菜单也在工作。

我一辈子都搞不清楚自己做错了什么。有什么想法吗?为了简洁起见,这里是我的三个类:

MainWindowViewModel.cs

namespace MyApp.Main
{
    public class MainWindowViewModel : BaseViewModel
    {
        private string _windowsUser = WindowsIdentity.GetCurrent().Name;
        private TileMenuViewModel _tileMenuViewModel = new TileMenuViewModel();
        private CmpCampaignListViewModel _cmpCampaignListViewModel = new CmpCampaignListViewModel();
        private NapTransferCampaignListViewModel _ntCampaignListViewModel = new NapTransferCampaignListViewModel();
        public string WindowsUser { get { return _windowsUser; } }
        public string WindowTitle { get; set; }
        private BaseViewModel _currentViewModel;
        public BaseViewModel CurrentViewModel
        {
            get { return _currentViewModel; }
            set { SetProperty(ref _currentViewModel, value); }
        }
        public MainWindowViewModel()
        {
            WindowTitle = String.Format("MyApp - {0}", WindowsUser);
            NavCommand = new RelayCommand<string>(OnNav);
            AddNewCmpCampaignCommand = new RelayCommand(OnAddNewCmpCampaign);
            CurrentViewModel = _tileMenuViewModel;
            _tileMenuViewModel.NavToCampaings += OnNavToCampaigns;
        }
        public RelayCommand<string> NavCommand { get; private set; }
        public RelayCommand AddNewCmpCampaignCommand { get; private set; }
        public void OnNav(string destination)
        {
            switch (destination)
            {
                case "CmpCampaignList":
                    CurrentViewModel = _cmpCampaignListViewModel;
                    break;
                case "NtCampaignList":
                    CurrentViewModel = _ntCampaignListViewModel;
                    break;
                case "TileMenu":
                    CurrentViewModel = _tileMenuViewModel;
                    break;
                default:
                    CurrentViewModel = _tileMenuViewModel;
                    break;
            }
        }
        public void OnAddNewCmpCampaign()
        {
            var editCampaignWindow = new AddEditCmpCampaignWindow();
            editCampaignWindow.Show();
        }
        private void OnNavToCampaigns()
        {
            CurrentViewModel = _cmpCampaignListViewModel;
            //OnNav("CmpCampaignList");
        }
    }
}

TileMenuViewModel.cs

namespace MyApp.Main
{
    public class TileMenuViewModel : BaseViewModel
    {
        public TileMenuViewModel()
        {
            NavToCampaignsCommand = new RelayCommand(OnNavToCampaigns);
        }
        public RelayCommand NavToCampaignsCommand { get; private set; }
        public event Action NavToCampaings = delegate { };
        private void OnNavToCampaigns()
        {
            NavToCampaings();
        }
    }
}

BaseViewModel.cs

namespace MyApp.Infrastructure
{
    public class BaseViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
    {
        private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>(
            );
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { };
        public bool HasErrors
        {
            get { return _errors.Count > 0; }
        }
        public IEnumerable GetErrors(string propertyName)
        {
            return _errors.ContainsKey(propertyName) ? _errors[propertyName] : null;
        }
        protected virtual void SetProperty<T>(ref T member, T val,
            [CallerMemberName] string propertyName = null)
        {
            if (!object.Equals(member, val))
            {
                member = val;
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
            ValidateProperty(propertyName, val);
        }
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        private void ValidateProperty<T>(string propertyName, T value)
        {
            var results = new List<ValidationResult>();
            ValidationContext context = new ValidationContext(this);
            context.MemberName = propertyName;
            Validator.TryValidateProperty(value, context, results);
            if (results.Any())
            {
                _errors[propertyName] = results.Select(c => c.ErrorMessage).ToList();
            }
            else
            {
                _errors.Remove(propertyName);
            }
            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
        }
    }
}

当调用事件操作时,不会触发WPF应用程序侦听方法

编辑:我解决了它不起作用的原因:在我使用它的视图中,我有一个TileMenuViewModel的新实例化;我在使用视图模型进行测试后忘记了它。它将DataContext设置为一个新实例,在该实例中触发了事件,而MainWindowViewModel正在侦听其自身TileMenuViewModel实例的事件。所以我简单地删除了这行:

DataContext = new TileMenuViewModel();

从我的TileMenuView,并将我的活动连接起来。我将把这条线索留给任何可能觉得有用的人。

不过,我仍然想知道将事件用作静态是好是坏?

原始答案:

我最终通过将事件标记为静态来解决这个问题。即:

public static event Action NavToCampaings = delegate { };

当然,我在不使用类实例的情况下订阅了该事件:

TileMenuViewModel.NavToCampaings += OnNavToCampaigns;

然而,我仍然想知道为什么我不能让这个事件系统作为实例成员与事件一起工作;以及使用事件静态是否会带来任何问题(或者这只是一种糟糕的做法?)

任何解释都会被告知。