为外部库的 属性 值更改创建事件处理程序

Create event handler for an external library's property value change

我正在使用一个外部库,该库的 属性 会根据特定事件发生变化。有时值会快速变化(尽管仅在 1 和 2 之间递增)。我想创建一个事件处理程序来检测值是否发生了变化,如果发生了变化,则检索该值作为方程式的一部分,以将表单移动到屏幕上的某个点。我目前正在使用计时器:

private var foo;

public Form1()
{
   this.InitializeComponent();

   this.foo = new Foo();
   this.DesktopLocation = new Point(foo.Property1 + 100, 500);

   Timer timer = new Timer();
   timer.Interval = 1;
   timer.Tick += new EventHandler(this.Timer_Tick);
   timer.Start();
}

private void Timer_Tick(object sender, EventArgs e)
{
   this.DesktopLocation = new Point(this.foo.Property1 + 100, 500);
}

我基于 Trigger a custom event for an "external" value change,但我希望有一个更好的解决方案,因为如果 foo.Property1 在短时间内多次更改,表单会滞后于预期点并且会闪烁。我试图让表单跟随类似于用户使用鼠标移动表单的点。在定时器之前,我在一个单独的线程上使用了一个 while 循环递归:

       private void CheckFoo()
       {
            while (!this.Created)
            {
            }

            if (new Point(this.foo.Property1 + 100, 500) != this.DesktopLocation)
            {
                this.Invoke(new Action(() =>
                {
                    this.DesktopLocation = new Point(this.foo.Property1 + 100, 500);
                }));
            }

            while (this.DesktopLocation == new Point(this.foo.Property1 + 100, 500) && this.ContinueLoop)
            {
            }

            if (this.ContinueLoop == false)
            {
                return;
            }
            else
            {
                this.CheckFoo();
            }
       }

上述方法在视觉上按预期工作了大约 30 秒,但随后它在代码的不同位置崩溃并显示 WhosebugException,通常在 this.DesktopLocation = new Point(this.foo.Property1 + 100, 500); 但有时在其他地方(我没有去过)不幸的是能够复制另一个位置)。我在这里阅读了有关 WhosebugExceptions 的信息:https://www.dotnetperls.com/Whosebugexception 似乎是因为我使用了递归循环,所以我假设我不能使用上述方法。有没有办法在没有视觉问题(或异常)的情况下实现这一目标?

Before the Timer I used a while loop on a separate Thread with recursion:

为什么?为什么要递归?

我觉得你的方法应该是这样写的:

private void CheckFoo()
{
    // It would be better to just not start the thread that
    // runs this method until the "Created" flag is set, rather
    // that having this busy loop to wait. At the very least, there
    // are better mechanisms for waiting than a loop like this.
    while (!this.Created)
    {
    }

    while (this.ContinueLoop)
    {
        if (new Point(this.foo.Property1 + 100, 500) != this.DesktopLocation)
        {
            this.Invoke(new Action(() =>
            {
                this.DesktopLocation = new Point(this.foo.Property1 + 100, 500);
            }));
        }
    }
}

现在,这种轮询并不理想。至少,您应该创建一个专用线程并将线程优先级设置为不高于 BelowNormal and/or 在 while 循环中包括对 Thread.Sleep(1) 的调用(强制线程屈服于任何其他就绪线程)。但是,如果您真的必须以这种频率监视 DLL 的值,并且 DLL 本身确实不提供除了轮询之外的机制以通知您值的更改,您可能坚持这样的代码。

请记住,调用 Invoke() 的成本很高。即使使用上述方法,如果该值以非常高的频率变化,您可能仍然看不到您希望的更新速度。