C# 尝试在WPF中激发事件时,EventHandler为null 问题

C# 尝试在WPF中激发事件时,EventHandler为null 问题,c#,wpf,event-handling,C#,Wpf,Event Handling,我在类LoginVM中创建了一个事件,如下所示: public class LoginVM : INotifyPropertyChanged { public event EventHandler<string> PasswordSet; } 正如您可能知道的那样,我正试图从ViewModel到视图触发事件,但每次我从ViewModel触发事件时,密码集都是空的,并且有错误。如果没有事件的侦听器,则事件是空的 private void RaisePasswordSet(St

我在类
LoginVM
中创建了一个事件,如下所示:

public class LoginVM : INotifyPropertyChanged
{
    public event EventHandler<string> PasswordSet;
}

正如您可能知道的那样,我正试图从
ViewModel
视图
触发事件,但每次我从
ViewModel
触发事件时,
密码集
都是空的,并且有错误。

如果没有事件的侦听器,则事件是空的

private void RaisePasswordSet(String pass) {
    YourEventArgs args = new YourEventArgs(pass);
    if(PasswordSet != null) PasswordSet(this, args);
}

您的问题是,当您尝试引发事件时,没有人侦听它

当事件没有侦听器时,事件为空

private void RaisePasswordSet(String pass) {
    YourEventArgs args = new YourEventArgs(pass);
    if(PasswordSet != null) PasswordSet(this, args);
}

您的问题是,当您尝试引发事件时,没有人收听它

最好像您那样初始化
LoginVM
的构造函数中的密码。这时应该进行初始化。通常,您会设置一个属性,XAML中的绑定将负责更新控件。虚拟机上不需要事件。但是这是一个密码框,所以您不能绑定它,您编写的事件是正确的

但在您的实现中,这就给您留下了一系列事件:

  • 创建虚拟机
  • VM在其构造函数中引发PasswordSet,而不检查是否存在任何处理程序
  • 视图将VM分配给DataContext
  • 视图将处理程序添加到PasswordSet事件
  • 在第2步,您会得到一个异常,因为您没有检查处理程序

    这是你要做的

    在VM或任何地方,始终使用此模式引发事件:


    C#
    最好像您那样初始化
    LoginVM
    的构造函数中的密码。这时应该进行初始化。通常,您会设置一个属性,XAML中的绑定将负责更新控件。虚拟机上不需要事件。但是这是一个密码框,所以您不能绑定它,您编写的事件是正确的

    但在您的实现中,这就给您留下了一系列事件:

  • 创建虚拟机
  • VM在其构造函数中引发PasswordSet,而不检查是否存在任何处理程序
  • 视图将VM分配给DataContext
  • 视图将处理程序添加到PasswordSet事件
  • 在第2步,您会得到一个异常,因为您没有检查处理程序

    这是你要做的

    在VM或任何地方,始终使用此模式引发事件:



    C#
    您是否也在XAML中创建viewmodel?或者其他地方?我不明白你的登录码。你似乎在一个活动之外提出这个事件。您的问题可能是引发事件的LoginVM对象不是您所使用的对象。向我们展示活动是如何进行的happens@EdPlunkett不,仅针对DataContextLoginVM是否在其构造函数中调用PopulateLatestServer(),或者在其构造函数或某些初始值设定项可能调用的任何代码中调用?您是否也在XAML中创建viewmodel?或者其他地方?我不明白你的登录码。你似乎在一个活动之外提出这个事件。您的问题可能是引发事件的LoginVM对象不是您所使用的对象。向我们展示活动是如何进行的happens@EdPlunkett不,仅针对DataContextDoes LoginVM在其构造函数中调用PopulateLatestServer(),或者在其构造函数或某些初始值设定项可能调用的任何代码中调用PopulateLatestServer()?但问题是它将始终为空,但我需要在那时触发该事件。@BenClarke,我认为您调用PopulateLatestServer()方法,然后再对其进行任何订阅。这可能是原因,处理程序总是null@BenClarke您有两种解决方案:使用事件,并且必须确保在订阅之前初始化值,因为初始值不会通知您,或者在XAML a属性中将ViewModel绑定到您的应用程序Control@nkoniishvt我设法把它修好了。我无法绑定该属性,因为它是密码箱。我只是没有在构造函数中调用该方法。但问题是它将始终为null,但我需要在该时间触发该事件。@BenClarke,我想您在订阅它之前调用PopulateLatestServer()方法。这可能是原因,处理程序总是null@BenClarke您有两种解决方案:使用事件,并且必须确保在订阅之前初始化值,因为初始值不会通知您,或者在XAML a属性中将ViewModel绑定到您的应用程序Control@nkoniishvt我设法把它修好了。我无法绑定该属性,因为它是密码箱。我还没有在构造函数中调用该方法。
    
    private void RaisePasswordSet(String pass) {
        YourEventArgs args = new YourEventArgs(pass);
        if(PasswordSet != null) PasswordSet(this, args);
    }
    
    protected void OnPasswordSet(String e)
    {
        var handler = PasswordSet;
    
        if (handler != null)
        {
            handler(this, e);
        }
    }
    
    protected void OnPasswordSet(String e) => PasswordSet?.Invoke(this, e);
    
    private void PopulateLatestServer()
    {
        try
        {
            string SERVER_ID = Registry.GetValue(@"HKEY_CURRENT_USER\SOFTWARE\PODIA", "LATESTSERVER", null).ToString();
    
            BDO_SERVERS latestserver = SavedServers.Where(a => a.Server_ID == SERVER_ID).FirstOrDefault();
    
            setServerURL(latestserver.ServerURL, false);
            Username = latestserver.Username;
            OnPasswordSet(latestserver.Password);
        }
        catch (Exception)
        {
            Global.WriteLog("Could not find last logged in server.", EventLogEntryType.Warning);
        } 
    }
    
    public partial class LoginV : MetroWindow
    {
        public LoginV()
        {
            InitializeComponent();
    
            LoginVM _loginVM = new LoginVM();
            this.DataContext = _loginVM;
            _loginVM.PasswordSet += new EventHandler<string> (_loginVM_PasswordSet);
    
            UpdatePassword();
        }
    
        protected void UpdatePassword()
        {
            passwordBox.Password = e;
        }
    
        private void _loginVM_PasswordSet(object sender, string e)
        {
            UpdatePassword();
        }
    
    public class LoginVM : INotifyPropertyChanged
    {
        public LoginVM(EventHandler<string> passwordSetHandler)
        {
            if (passwordSetHandler != null)
            {
                PasswordSet += passwordSetHandler;
            }
    
            PopulateLatestServer();
        }
    
        //  If the consumer doesn't want to handle it right way, don't force the issue.
        public LoginVM()
        {
            PopulateLatestServer();
        }
    
    public class LoginVM : INotifyPropertyChanged
    {
        private event EventHandler<string> _passwordSet;
        public event EventHandler<string> PasswordSet
        {
            add
            {
                _passwordSet += value;
                //  ...or else save latestServer in a private field, so here you can call 
                //  OnPasswordSet(_latestServer.Password) -- but since it's a password, 
                //  best not to keep it hanging around. 
                PopulateLatestServer();
            }
            remove { _passwordSet -= value; }
        }