Страницы

Поиск по вопросам

понедельник, 24 декабря 2018 г.

Запрет на многократный вызов события при действиях с мышью

Всем доброго времени суток!
Пишу собственный GUI-интерфейс для игрушки.
Класс Button
Имеется следующий набор событий-делегатов:
public event EventHandler MouseUpHandler; public event EventHandler MouseDownHandler; public event EventHandler MouseOutHandler; public event EventHandler MouseInHandler;
Каждому из событию соответствует свой метод:
private void OnMouseIn() {...} private void OnMouseOut() {...} private void OnMouseUp() {...} private void OnMouseDown() {...}
В них выполняется примерно такой код:
private void OnMouseDown() { EventHandler tempHandler = MouseDownHandler; // получаем делегат события if (tempHandler != null) // проверяем, не пустой ли делегат { tempHandler(this, EventArgs.Empty); // вызываем событие } _state = ButtonState.Click; // используется для определения правильных координат на спрайте текстуры кнопки во время ее рисования }
Соответствующие методы присутствуют для других событий-делегатов. В этом же классе присутствует метод Update(), который просчитывает логику кода:
public void Update() { /* Формируем данные о положении мыши и о зоне пересечения (на основе позиции и размеров кнопки) */ MouseState mouseState = Mouse.GetState(); Point mousePosition = new Point(mouseState.X, mouseState.Y);
Rectangle buttonRectangle = new Rectangle ( (int) this.Position.X, (int) this.Position.Y, (int) this.Size.X, (int) this.Size.Y );
if (buttonRectangle.Contains(mousePosition)) // проверяем на наличие пересечения курсора мыши и кнопки { if (mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed) //ЛКМ - нажатие кнопки мыши { OnMouseDown(); } if (_mousePrevState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed && mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Released) // ЛКМ - отпускание кнопки мыши { OnMouseUp(); } } else // выход курсора за границы кнопки { OnMouseOut(); } _mousePrevState = mouseState; // сохраняем предыдущее состояние (MouseUp может быть только после MouseDown) }
В другом месте создается экземпляр кнопки:
Button button = new Button(...);
Соответственно задаются свойства, текстуры и т.д. Все это функционирует. Далее, в том же "другом месте", делегату присваивается метод:
button.MouseInHandler += Название_метода;
С этим, думаю, все должно быть понятно. Логика вкратце - создается объект, ему в специальный делегат присваивается метод, после чего в методе Update() проверяется, было ли пересечение кнопки и курсора, был ли клик и т.д. и на основе этого уже вызывается нужное нам событие.
Проблема, которая имеется сейчас: метод вызывается не единожды, а множество раз подряд.
Все это приводит к тому, что, например, вывод в консоль любого текста происходит множество раз, но требуется не более одного.
Я попробовал решить проблему с помощью дополнительных полей:
private bool _isMouseUp; private bool _isMouseDown; private bool _isMouseIn; private bool _isMouseOut;
С изменением структуры логики методов-событий:
private void OnMouseIn() { if (!_isMouseIn) // если событие не вызывалось { EventHandler tempHandler = MouseInHandler; if (tempHandler != null) { tempHandler(this, EventArgs.Empty); } _isMouseIn = true; // определяем событие, как вызванное и не даем ему совершиться повторно _isMouseOut = false; // после In события можно допустить выполнение Out события } _state = ButtonState.Hover; } // примерно такой же код ниже, разве что теперь Out и In поменялись местами private void OnMouseOut() { if (!_isMouseOut) { EventHandler tempHandler = MouseOutHandler; if (tempHandler != null) { tempHandler(this, EventArgs.Empty); } _isMouseOut = true; _isMouseIn = false; } _state = ButtonState.Normal; }
Для MouseDown и MouseUp соответствующие изменения.
И все это работает, до тех пор, пока пользователь ведет себя нормально. Однако если пробовать различные ситуации, например, зажать ЛКМ, после чего навести на кнопку и отпустить, или зажать ЛКМ над кнопкой и отпустить ее в другом месте - все это приводит к тому, что в какой-либо момент то или иное событие, которое должно было быть вызвано - не вызывается, либо наоборот, вызывается то событие, которые вызываться не должно.
Я попробовал как можно более подробно описать свою проблему, но если будут какие-нибудь вопросы - задавайте, уточню, если нужно.


Ответ

Обычно для UI-элементов вводят понятие Capture.
Когда происходит событие MouseDown, текущий элемент под мышью фиксируется. Эта фиксация и называется capture. После этого все события о перемещении мыши доставляются только этому элементу. При этом событие отпускания мыши тянет за собой автогенерацию клика только если при этом мышь была внутри элемента с capture. После отпускания мыши capture обнуляется.
Таким образом исчезают неожиданные комбинации событий. Вы можете проверить работу capture на окне какого-нибудь приложения: нажмите на крестик закрытия окна в правом верхнем углу, и, не отпуская кнопку мыши, уведите её из кнопки. Теперь отпустите мышь, крестик не сработает.

Кстати, у вас в коде логическая ошибка: для MouseDown нужно проверять предыдущее состояние точно так же, как вы делаете для MouseUp: MouseDown происходит только если предыдущее состоянии мыши было ненажатым.

Комментариев нет:

Отправить комментарий