Проблема таймеров / жестов касания и удержания в .NET CF - PullRequest
1 голос
/ 29 октября 2009

Я столкнулся с некоторым странным поведением в моем приложении .NET CF 2.0, работающем в Windows CE 5.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;
        }
    }
}

Я не могу связать изображения встроенными, поэтому вот ссылка на скриншот, иллюстрирующий поведение

Редактировать: В моем реальном приложении отметка времени обновляет элемент управления. Так что я ограничен работой в одном потоке. (Я не могу действительно выполнить то, что мне нужно, с помощью обработчиков событий).

Ответы [ 4 ]

0 голосов
/ 29 октября 2009

Тем не менее, еще один ответ, который, вероятно, предотвратит это поведение (см. Мой другой ответ для объяснения того, почему вы его видите) будет состоять в том, чтобы перейти от использования таймера форм к таймеру потоков, чтобы процесс потока находился в отдельном потоке. Если таймер не влияет на пользовательский интерфейс, это должно работать хорошо. Если вы по-прежнему видите конфликт (мы не знаем, что на самом деле делает ваш таймер), изменение приоритета потока в начале таймера на что-то вроде BelowNormal предотвратит зависание процессора в интерфейсе пользователя.

0 голосов
/ 29 октября 2009

Почему бы не установить для свойства Timer Enabled значение false в начале вашего обработчика и вернуться к концу в конце?

0 голосов
/ 29 октября 2009

Поскольку Timer является таймером WinForms, он работает в том же контексте потока, что и пользовательский интерфейс и все обработчики пользовательского интерфейса (включая обработчик нажатия мыши). Это означает, что в данный момент времени может работать только один. Проблема, которую вы видите, заключается в том, что ваш обработчик мыши выполняет и выполняет другие задачи, которые вы меняете при выполнении.

Вы можете предотвратить это, используя критическую секцию (Монитор). Вы можете установить блокировку вокруг всего обработчика mousedown (или входа монитора в начале и выхода монитора в конце), а затем один в таймере proc , который блокирует тот же объект . Это будет означать, что весь обработчик mousedown должен будет выполняться до того, как будет запущен процесс таймера, и «тики» таймера будут фактически поставлены в очередь (если вы не использовали Monitor.TryEnter, чтобы специально этого избежать).

Возможный недостаток в том, что обратное также будет правдой - ваш обработчик mousedown никогда не сможет работать, пока не будет завершен какой-либо ожидающий процесс таймера. Независимо от того, является ли это проблемой, будет зависеть от вашего варианта использования, и вы всегда можете смягчить ее в таймере, ища событие или флаг для раннего выхода.

0 голосов
/ 29 октября 2009

Обойти эту проблему можно, установив логический флаг в общедоступной статической переменной класса (возможно, в одноэлементном стиле). Назовите ее, например, IgnoreTick.

Установите для IgnoreTick значение true в обработчике мыши. В вашем тик-обработчике проверьте значение IgnoreTick. Если это правда, вернись, если не делай то, что делаешь. Вам, конечно, придется добавить обработчик мыши, в котором вы вернете IgnoreTick значение false.

...