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

本文关键字:运行 是否 事件 双击 检查 | 更新日期: 2023-09-27 18:27:22

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

// 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

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

以下是我的代码的样子:

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在下面的评论中所问的,我的功能是否真的在等待,或者只是启用用户输入,他发现了错误的地方。

根据我写错的问题,Steve的答案是正确的。非常感谢你们。

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

只需禁用事件处理程序

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;
    }
}

编辑

根据您的评论,我怀疑此双击事件被分配给了多个控件。如果是这种情况,使用列表视图的全局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) 的Click,则无法正常工作

使用锁进行如下操作如何:

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的逻辑不同,因为它是线程安全的。

一个简单的状态机应该可以在不需要太多变量的情况下解决您的问题。创建一个名为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]属性。还要注意,枚举是线程安全的,评估它们是原子的,所以多线程也不应该是问题。