【问题标题】:Event fires more and more times事件触发次数越来越多
【发布时间】:2011-06-01 19:43:00
【问题描述】:

我有一个 silverlight mvvm 应用程序,它加载主视图,其中 2 个用户控件加载到 2 个 ContentControls 中,一个带有显示项目的列表框,另一个带有编辑按钮。当我单击编辑按钮时,2 个新的用户控件加载到 ContentControls 中,一个显示要编辑的数据 (EditData),另一个具有保存和取消按钮 (EditAction)。 当我单击保存按钮时,它会引发一个在单独的 GlobalEvents.cs 类中定义的事件,例如:

public event EventHandler OnSaveButtonClicked;  
public void RaiseSaveButtonClicked()  
{  
  this.OnSaveButtonClicked(this, EventArgs.Empty);  
}

我在另一个用户控件 EditData 中订阅它,因为我需要通过自定义 EventArgs 传输编辑过的数据,所以我已经放入了它的 ViewModel 的构造函数:

this.globalEvents.OnSaveButtonClicked += (s, e) => SaveData();  

并在保存数据中:

public void SaveData()  
{  
    globalEvents.RaiseSaveData(EditedGuy);     
}  

引发另一个事件,将以前的用户控件加载到其 ControlContent 中并在列表框中显示编辑的数据。没关系,但是每当我单击编辑然后再次保存时,它会引发事件两次,再次引发 3 次,然后是 4 次,依此类推。我怎样才能让它只被提升一次?我认为这可能是因为每次单击编辑时,都会加载用户控件的新实例,但我不知道,也许对事件的订阅仍然存在,所以我尝试粘贴

this.globalEvents.OnSaveButtonClicked -= (s, e) => SaveData(); 

到 Dispose() 方法但没有成功。我怎样才能做到这一点?

【问题讨论】:

    标签: c# silverlight events mvvm event-handling


    【解决方案1】:

    当你想从事件中注销时,你不能使用 lambdas。

    this.globalEvents.OnSaveButtonClicked += (s, e) => SaveData(); 
    

    这将创建一个 EventHandler 类型的实例 - 我们称之为实例 A - 并将其添加为处理程序。

    this.globalEvents.OnSaveButtonClicked -= (s, e) => SaveData(); 
    

    这不会从事件中移除实例 A,而是创建一个新实例 - 实例 B - 并尝试将其从事件中移除。

    要解决此问题,请创建一个小方法或将该匿名方法保存在字段中:

    class ViewModel
    {
    
        private EventHandler _saveButtonClickedHandler;
        // ...
    
        public ViewModel()
        {
            _saveButtonClickedHandler = (s, e) => SaveData();
            this.globalEvents.OnSaveButtonClicked += _saveButtonClickedHandler;
            // ...
        }
    
        public void Dispose()
        {
            this.globalEvents.OnSaveButtonClicked -= _saveButtonClickedHandler;
            // ...
        }
    
        // ...
    }
    

    【讨论】:

    • 非常感谢!我不知道 lambdas 不能用于从事件中注销。我的错,谢谢你告诉我正确的方法:]
    【解决方案2】:
    this.globalEvents.OnSaveButtonClicked += (s, e) => SaveData();
    

    此行被多次调用,因此您每次都添加一个新的事件处理程序。

    您需要将该行移动到仅调用一次的位置或将事件处理程序更改为:

    this.globalEvents.OnSaveButtonClicked += SaveData;
    
    public void SaveData(object sender, EventArgs e)  
    {  
        globalEvents.RaiseSaveData(EditedGuy);     
        this.globalEvents.OnSaveButtonClicked -= SaveData();
    }
    

    因此,您在处理完事件处理程序后将其删除。这假定处理程序将在您下次进入编辑模式时重新添加。

    【讨论】:

    • 谢谢,这正是我想要的。
    • 还有一件事,为了让我自己清楚,我尝试将事件调用放在不同的方法而不是构造函数中,但是然后在这一行的 GlobalEvents 类中 this.OnSaveButtonClicked(this, EventArgs.Empty);我得到空引用异常。为什么?
    【解决方案3】:

    您可以在您的类中定义一个私有事件处理程序委托变量并在您的构造函数中分配它:

    private SaveButtonClickedHandler _handler;
    

    在构造函数中分配处理程序:

    _handler = (s,e) => SaveData();
    this.globalEvents.OnSaveButtonClicked += _handler;
    

    处置:

    this.globalEvents.OnSaveButtonClicked -= _handler; 
    

    “SaveButtonClickedHandler”是代表名称的伪代码/占位符。

    哈桑

    【讨论】:

      【解决方案4】:

      您必须放入一个适当的事件处理程序方法来调用SaveData() 并注册/取消注册它。否则,您尝试取消注册另一个“新”匿名方法,而不是您注册的原始方法,因为它是匿名的,您实际上无法再访问。

      public void SaveButtonClicked(object sender, EventArgs e)
      {
          SaveData();
      }
      
      this.globalEvents.OnSaveButtonClicked += SaveButtonClicked;
      
      this.globalEvents.OnSaveButtonClicked -= SaveButtonClicked;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-15
        • 1970-01-01
        • 1970-01-01
        • 2019-05-15
        • 2017-06-02
        相关资源
        最近更新 更多