计时器/在.NET CF中点击并按住手势问题

时间:2009-10-29 02:24:53

标签: c# compact-framework timer windows-ce tapandhold

我在Windows CE 5.0上运行的.NET CF 2.0应用程序中遇到了一些奇怪的行为。

我有一个定时更新控件的计时器,该控件还可以从用户接收点击和保持手势(在鼠标按下处理程序中)。我发现的是,当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会显示说明行为的屏幕截图

编辑:在我的实际应用中,计时器勾选正在更新控件。所以我只能在一个线程内工作。 (我无法用事件处理程序完成我所需要的)。

4 个答案:

答案 0 :(得分:0)

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

在鼠标按下处理程序中将IgnoreTick设置为true。在您的tick处理程序中,检查IgnoreTick的值。如果这是真的,那就回去吧,如果不做你做的事情。您当然必须添加一个鼠标处理程序,您将在其中将IgnoreTick设置为false。

答案 1 :(得分:0)

为什么不在处理程序的开头将Timer的Enabled属性设置为false,并在结束时将其设置为ture?

答案 2 :(得分:0)

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

您可以使用关键部分(监控)来防止这种情况发生。你可以锁定整个mousedown处理程序(或者在开始时监视器输入,在结尾处监视器退出),然后在定时器proc 中锁定同一个对象。这意味着整个mousedown处理程序必须在定时器proc运行之前执行,并且定时器“ticks”实际排队(除非你使用Monitor.TryEnter来专门避免这种情况)。

可能的反面是反过来也是如此 - 你的mousedown处理程序永远不会运行,直到任何挂起的计时器proc完全完成。这是否是一个问题将取决于您的用例,您可以通过查找事件或标志提前退出来在计时器proc中缓解它。

答案 3 :(得分:0)

然而,anohter的答案可能会阻止这种行为(请参阅我的其他答案以解释为什么你看到它)将从使用Forms计时器更改为Threading计时器,因此线程proc在一个单独的线程上。如果定时器proc不影响UI,这应该很好。如果你仍然看到争用(我们不知道你的计时器proc真正做了什么)将计时器proc开始时的线程优先级更改为像BelowNormal那样可以防止从UI中占用CPU量。