检查事件(doubleClick)是否正在运行

时间:2014-10-22 10:34:11

标签: c# events

我正在编写一个在很多州之间切换的工具。对于某些事件,我需要确保它们在被调用的函数(在事件内)运行时不会被第二次执行。这就是我之前管理它的方式:

// Global variables //
public bool func1IsRunning = false;
public bool func2IsRunning = false;
...

public void listView_DoubleClick(object sender, EventArgs e) 
{
    if(!func1IsRunning)
    {
        func1();
        func1IsRunning = false;
    }
}

public void func1()
{
    func1IsRunning = true;
    // some code in here //
}

但随着我工具的每一次扩展,全局变量列表都会增长。事件和功能也越来越不清楚。

不是有这样的方式(?):

public void listView_DoubleClick(object sender, EventArgs e) 
{
    if(DoubleClick.IsHandled)
    {
        func1();
    }
}

public void func1()
{
    // some code in here //
    // ................. //

    DoubleClick.IsHandled = true; // at the end of the function //
}

所以我正在寻找的是一种确定事件是否仍在运行的方法。我的代码正在运行,我对它的外观不满意。

有什么想法吗?


更新1

我决定使用史蒂夫的答案,因为它以最清晰的方式解决了我的问题。 无论如何, NOT 目前正常运行。

以下是我的代码的样子:

public void listView_DoubleClick(object sender, EventArgs e)
        {
            try
            {
                listView.DoubleClick -= new EventHandler(listView_DoubleClick);
                itemEdit();
            }
            finally
            {
                listView.DoubleClick += new EventHandler(listView_DoubleClick);
            }
        }

上面的代码并没有禁用处理程序。

public void listView_DoubleClick(object sender, EventArgs e)
        {
            try
            {
                listView.DoubleClick -= listView_DoubleClick;
                itemEdit();
            }
            finally
            {
                listView.DoubleClick += listView_DoubleClick;
            }
        }

此代码也没有禁用处理程序。

这是启用处理程序的行(MainForm.Designer.cs):

this.listView.DoubleClick += new System.EventHandler(this.listView_DoubleClick);

没有引发任何错误。事件一次又一次被解雇。问题在哪里?

更新2:
正如Sinatr在下面的评论中所说,如果我的功能真的在等待或只是启用用户输入,他就会发现错误发生的位置。

根据我错误的书面问题,史蒂夫的回答是正确的。非常感谢你们所有人。

3 个答案:

答案 0 :(得分:3)

只需禁用事件处理程序

public void listView_DoubleClick(object sender, EventArgs e) 
{
    try
    {
         listView.DoubleClick -= listView_DoubleClick;

         // Now, even if func1 causes a DoubleClick event,
         // or user manages to trigger a DobuleClick
         // there is no event registered and this code could
         // reentered until you exit from func1.
         func1();
    }
    finally
    {
         // Important part. the finally block is required 
         // because you should readd the event handler 
         // ALSO in case an exception occurs in func1 
         // and it is not handled there
         listView.DoubleClick += listView_DoubleClick;
    }
}

修改

查看您的评论我怀疑此DoubleClick事件已分配给多个控件。如果是这种情况,使用listview的全局listView全局实例不会禁用双击其他链接到同一代码的控件。
如果是这种情况,那么您需要更通用的方法

public void listView_DoubleClick(object sender, EventArgs e) 
{
    Control c = sender as Control;
    try
    {
         if(c != null)
         {
              c.DoubleClick -= listView_DoubleClick;

              // Now, even if func1 causes a DoubleClick event,
              // or user manages to trigger a DobuleClick
              // there is no event registered and this code could
              // reentered until you exit from func1.
              func1();
         }
    }
    finally
    {
         // Important part. the finally block is required 
         // because you should readd the event handler 
         // ALSO in case an exception occurs in func1 
         // and it is not handled there
         if(c != null) c.DoubleClick += listView_DoubleClick;
    }
}

当然,这只是为了启用/停用DoubleClicks事件,如果您将此事件处理程序分配给其他标准事件(例如具有相同签名的(object sender, EventArgs e)

答案 1 :(得分:1)

如何使用锁定以下内容:

private object globalLock = new object();
private Dictionary<int, object> lockObjects = new Dictionary<int, object>();

public void listView_DoubleClick(object sender, EventArgs e) 
{
    object lockObject;
    lock (globalLock) // to avoid two threads creating the object
    {
        if (!lockObjects.ContainsKey(1))
            lockObjects.Add(1, new object());
        lockObject = lockObjects[1];
    }

    if (Monitor.TryEnter(lockObject) // enter only if no thread has already entered
    {
        try { func1(); }
        finally { Monitor.Exit(lockObject); }
    }
}

这与Steve在线程安全方面的逻辑不同。

答案 2 :(得分:0)

一个简单的状态机应该可以在不需要太多变量的情况下解决您的问题。像这样创建一个名为AppState的Enum

enum AppState
{
     Ready = 1,
     InsideListView1Click = 2,
     InsideListView1DoubleClick = 3
     InsideListView2Click = 4,
     InsideListView2DoubleClick = 5
}

当您向应用程序添加新控件和/或事件处理程序时,此枚举可能会增长。现在使用一个全局变量来跟踪应用程序状态并在事件处理程序中适当地修改它:

private AppState m_State = AppState.Ready;

在事件处理程序中你会这样做:

private void ListView1_DoubleClick(object sender, EventArgs e)
{
    lock
    {
        if(m_State != AppState.Ready)
            return;
        else
            m_State = AppState.InsideListView1DoubleClick;
    }

    //Do your stuff

    m_State = AppState.Ready;
 }

这样,新的调用将被忽略而不是排队。如果您希望同时处于多个状态,则也可以在此枚举上应用[Flags]属性。另请注意,枚举是线程安全的,并且评估它们是原子的,因此多线程也不应该成为问题。