C# - 根据鼠标所在的位置处理对象?

C# - Disposed Object Dependent on where Mouse is Located?

我的 C# 代码出现问题。

一点背景知识:我正在开发一个 HMI,我需要在 HMI 屏幕上的任何位置检测鼠标移动。屏幕被分成许多不同的控件,所以我使用这个递归函数来检测鼠标在任何控件上的移动:

private void SetMouseTriggers(Control c1)
{
    // For all of the child controls in this control
    for (int i = 0; i < c1.Controls.Count; i++)
    {
        // Get the individual child control
        Control c2 = c1.Controls[i];

        // Add a MouseEventHandler to this control's MouseMove event
        c2.MouseMove += new MouseEventHandler(OnMouseMove);

        // Recursively call this function
        SetMouseTriggers(c2);
    }
}

// This function is entered whenever the mouse moves anywhere on screen
private void OnMouseMove(object sender, MouseEventArgs e)
{

}

我为父控件(即SetMouseTriggers(ParentControl))调用此函数来检查所有子控件。这部分工作正常。

我的问题是当我尝试使用 HMI 的特定功能时遇到错误 "Cannot access a disposed object",即更改用户。

奇怪的是,只有当鼠标位于人机界面的某些控件上时才会出现错误。当鼠标位于其他 HMI 控件上时,程序可以正常运行。

我尝试调试程序,但无法访问问题实际发生的来源。

如果有帮助,这里是反汇编:

我的下一个想法是检查每个控件的 isDisposed 属性,看看我是否至少可以确定是哪个控件导致了问题。请参阅下面我创建的用于检测此问题的递归函数。我将程序置于调试模式并在控件被释放时设置断点,但在我测试的各种场景中我永远无法打到这个断点。

private void CheckAllControls(System.Windows.Forms.Control c1)
{
    // For all of the child controls in this control
    for (int i = 0; i < c1.Controls.Count; i++)
    {
        // Get the individual child control
        System.Windows.Forms.Control c2 = c1.Controls[i];

        // Check if the control is disposed
        if (c2.IsDisposed) 
        { // Breakpoint set here
        }

        // Recursively call this function
        CheckAllControls(c2);
    }
}

这告诉我控件必须放置在 System.Windows.Forms.dll 库中的某处。

我的问题是:

  1. 即使我无法访问出现问题的代码,是否有解决此问题的方法?

  2. 即使没有办法解决这个问题,有人可以解释发生了什么以及为什么吗? (即为什么只有当鼠标位于某些控件上时才会出现此问题?)

试试那个例子, 我刚刚订阅了处置的事件,并在处置时取消了对委托的订阅。 并添加了一些可能有助于稍后调试和理解代码的写入。

private void CheckAllControls(System.Windows.Forms.Control c1)
        {
            // For all of the child controls in this control
            for (int i = 0; i < c1.Controls.Count; i++)
            {
                // Get the individual child control
                System.Windows.Forms.Control c2 = c1.Controls[i];

                // Check if the control is disposed
                if (c2.IsDisposed)
                { // Breakpoint set here
                }
                c2.Disposed += C2_Disposed;
                // Recursively call this function
                CheckAllControls(c2);
            }
        }

        private void C2_Disposed(object sender, EventArgs e)
        {
            try
            {
                 Debug.WriteLine(string.Format("{0} just disposed", ((Control)sender).Name));
                ((Control)sender).MouseMove -= OnMouseMove;
                ((Control)sender).Disposed -= C2_Disposed;
            }
            catch (Exception ex) {
                Debug.WriteLine(ex);
            }
        }
        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            //your logic...
        }

可能会发生异常,因为触发事件的用户控件的某些部分在触发事件之后且在事件处理程序实际 运行.

之前被处置。

这取决于您的应用程序使用的控件。我可以在错误消息中看到异常来自一个名为 "UserGrid" 的对象。网格、tables 等通常由几个较小的控件组成。由于可以有效处理的控件数量是有限的,因此大多数这些控件只为当前编辑的单元格(鼠标所在的位置)维护一个子控件(一个 table 行/单元格,一个网格元素等),而其他所有只是渲染图片。这可能会导致在后台创建/处理多个对象。这只是一个想法,但它可能会导致您的处置问题。

我认为这个异常发生在事件处理程序中,所以你不应该把行

// Check if the control is disposed
if (c2.IsDisposed) 
{ // Breakpoint set here
}

进入递归注册方法,但进入事件处理程序:

// This function is entered whenever the mouse moves anywhere on screen
private void OnMouseMove(object sender, MouseEventArgs e)
{
    // Check if the control is disposed
    if (sender.IsDisposed) 
    { // Breakpoint set here
    }
}

关于递归解决方案,注册一个低级事件过滤器来捕获整个 window 的鼠标移动事件会更有效。你可以在这里找到一个例子:How do I capture the mouse move event