2009-10-29 49 views
1

我在运行Windows CE 5.0的.NET CF 2.0应用程序中遇到了一些奇怪的行为。.NET中的计时器/点按并保持手势问题CF

我有一个定时器定期更新控件,该控件还可以从用户(在鼠标下拉处理程序中)接收点击和保持手势。我发现当TAH开始时(但在它退出之前),一个计时器事件可以开始处理,它在执行过程的中途预先执行鼠标放下处理程序。

据我的研究告诉我,这不是正常的行为,我只是误解了定时器/事件?难道SHRecognizeGesture正在调用Application.DoEvents的等价物吗?

无论如何,有没有人有一个“很好”的方式来修复这个例子,以便当应用程序检查TAH时,定时器代理不会“打勾”。

请参阅下面的示例程序来说明此问题(点住列表框下方的空白处以生成日志消息)。

在此先感谢。

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

namespace DeviceApplication1 
{ 
    public class BugExample : Control 
    { 
     [Flags] 
     internal enum SHRGFLags 
     { 
      SHRG_RETURNCMD = 0x00000001, 
      SHRG_NOTIFYPARENT = 0x00000002, 
      SHRG_LONGDELAY = 0x00000008, 
      SHRG_NOANIMATION = 0x00000010, 
     } 

     [DllImport("aygshell.dll")] 
     private extern static int SHRecognizeGesture(ref SHRGINFO shrg); 

     private struct SHRGINFO 
     { 
      public int cbSize; 
      public IntPtr hwndClient; 
      public int ptDownx; 
      public int ptDowny; 
      public int dwFlags; 
     } 

     public bool TapAndHold(int x, int y) 
     { 
      SHRGINFO shrgi; 

      shrgi.cbSize = 20; 
      shrgi.hwndClient = this.Handle; 
      shrgi.dwFlags = (int)(SHRGFLags.SHRG_RETURNCMD); 
      shrgi.ptDownx = x; 
      shrgi.ptDowny = y; 

      return (SHRecognizeGesture(ref shrgi) > 0); 

     } 

     protected override void OnMouseDown(MouseEventArgs e) 
     { 
      base.OnMouseDown(e); 

      BugExampleForm parent = (BugExampleForm)this.Parent; 

      //The problem is that the parent tick event will fire whilst TapAndHold is running 
      //Does TapAndHold perform an equivelant to Application.DoEvents? 
      parent.AddLog("Tap Hold - Enter"); 
      parent.AddLog(String.Format("Tap Hold - Exit - {0}", TapAndHold(e.X, e.Y))); 

     } 
    } 

    public class BugExampleForm : Form 
    { 
     Timer _timer; 
     BugExample _example; 
     ListBox _logBox; 

     public BugExampleForm() 
     { 
      _example = new BugExample(); 
      _example.Dock = DockStyle.Fill; 

      _logBox = new ListBox(); 
      _logBox.Dock = DockStyle.Top; 

      _timer = new Timer(); 
      _timer.Interval = 1000; 
      _timer.Enabled = true; 
      _timer.Tick += new EventHandler(_timer_Tick); 

      this.SuspendLayout(); 
      this.Text = "Example"; 
      this.Size = new System.Drawing.Size(200, 300); 

      this.Controls.Add(_example); 
      this.Controls.Add(_logBox); 
      this.ResumeLayout(); 
     } 

     void _timer_Tick(object sender, EventArgs e) 
     { 
      AddLog("Tick"); 
     } 

     public void AddLog(string s) 
     { 
      _logBox.Items.Add(s); 
      _logBox.SelectedIndex = _logBox.Items.Count - 1; 
     } 
    } 
} 

我不能链接图片内嵌,所以here is a link的截图说明

编辑的行为:在我的实际应用中,计时器滴答的更新控制。所以我只能在一个线程内工作。 (我无法真正实现我需要的事件处理程序)。

+0

感谢ctacke和psasik为你的帮助。由于代表不足,我无法赞成,但迄今为止您的回复非常好。我想我将不得不采用设置某种形式的全局可访问布尔的“丑陋”解决方案,该布尔表明控件是否已进入TAH部分。然后在表格的基础上检查这个布尔如果适当。生病让我暂时没有回答,看看我能否得到更多回复。 – 2009-10-30 01:08:56

回答

0

解决此问题的方法可能是在公共静态类变量(可能是singleton样式)中设置布尔标志。例如,将其称为IgnoreTick。

将鼠标放下处理程序中的IgnoreTick设置为true。在您的滴答处理程序中检查IgnoreTick的值。如果这是真的,那么返回,如果没有做你做的事情。您当然必须添加一个鼠标上移处理程序,您可以在其中将IgnoreTick设置回false。

+0

这或多或少是我目前的解决方法。 不幸的是,它导致将控件紧密地耦合到表单,并且大写全部上限注释“检查这些情况中的这个值”:) – 2009-10-29 03:42:35

0

为什么不在处理程序的开始处将Timer的Enabled属性设置为false,并在最后返回true?

+0

我假设您是指在控件的mousedown事件期间禁用计时器? 这可能会工作,除非计时器在鼠标处理程序代码运行之前排队了一个事件(虽然我不是100%确定是否会发生这种情况)。 能有人比我更多的知识请详细说明吗? – 2009-10-29 03:39:45

+0

之前不能有一个“排队”。你使用的Timer是一个Forms定时器,所以它运行它与UI线程的上下文 - 就像你的mousedown处理程序一样。一个或另一个Ony都可以运行,所以如果你输入了你的鼠标处理程序,Timer还没有被触发。这实际上给了我一个关于另一个答案的想法。 – ctacke 2009-10-29 12:59:21

0

由于Timer是一个WinForms计时器,它在与UI和所有UI处理程序(包括您的鼠标向下处理程序)相同的线程上下文中运行。这意味着只有一个人可以在特定的时间运行。您看到的问题是因为您的鼠标处理程序正在执行并执行其他正在交换执行的任务。

您可以使用临界区(Monitor)来防止这种情况发生。您可以在整个mousedown处理程序(或监视器在开始时输入并在监视器结束时退出)和锁定在相同对象上的定时器proc 中锁定。这意味着整个mousedown处理程序必须在timer proc运行之前执行,并且定时器“ticks”实际上会排队(除非使用Monitor.TryEnter来专门避免)。

可能的缺点是反过来也是如此 - 你的mousedown处理程序永远不会运行,直到任何挂起的定时器过程完全完成。不管这个问题是否会成为问题将取决于您的用例,并且您可以通过查找事件或标志提前退出以便在定时器过程中减轻它的影响。

+0

这不太合适(我刚刚测试过),是不是在两个处理程序中使用同一个线程进行锁定? Monitor.Enter是可重入的,所以代码只是按照正常情况继续执行。 除非我误解你在说什么? – 2009-10-30 00:45:09

0

然而,anohter的答案可能会阻止此行为(请参阅我的其他答案,解释为什么你会看到它)将从使用Forms定时器更改为线程计时器,以便线程处理程序位于单独的线程。如果定时器proc不影响用户界面,这应该很好。如果您仍然看到争用(我们不知道您的计时器进程真的在做什么),将计时器启动时的线程优先级更改为像BelowNormal这样的内容可以防止用户界面占用CPU量。

+0

在我的实际应用中,计时器计时器正在更新控制器(我更新了OP来澄清),我理解事件处理程序会更好一些,但在这个示例中我并没有那么奢侈。因此,如果我使用Threading.Timer,我将不得不调用回主线程,并且我们回到方形1。 – 2009-10-30 00:59:49