#c_sharp #события #логика #xna
Всем доброго времени суток! Пишу собственный 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 соответствующие изменения. И все это работает, до тех пор, пока пользователь ведет себя нормально. Однако если пробовать различные ситуации, например, зажать ЛКМ, после чего навести на кнопку и отпустить, или зажать ЛКМ над кнопкой и отпустить ее в другом месте - все это приводит к тому, что в какой-либо момент то или иное событие, которое должно было быть вызвано - не вызывается, либо наоборот, вызывается то событие, которые вызываться не должно. Я попробовал как можно более подробно описать свою проблему, но если будут какие-нибудь вопросы - задавайте, уточню, если нужно.
Ответы
Ответ 1
Обычно для UI-элементов вводят понятие Capture. Когда происходит событие MouseDown, текущий элемент под мышью фиксируется. Эта фиксация и называется capture. После этого все события о перемещении мыши доставляются только этому элементу. При этом событие отпускания мыши тянет за собой автогенерацию клика только если при этом мышь была внутри элемента с capture. После отпускания мыши capture обнуляется. Таким образом исчезают неожиданные комбинации событий. Вы можете проверить работу capture на окне какого-нибудь приложения: нажмите на крестик закрытия окна в правом верхнем углу, и, не отпуская кнопку мыши, уведите её из кнопки. Теперь отпустите мышь, крестик не сработает. Кстати, у вас в коде логическая ошибка: для MouseDown нужно проверять предыдущее состояние точно так же, как вы делаете для MouseUp: MouseDown происходит только если предыдущее состоянии мыши было ненажатым.
Комментариев нет:
Отправить комментарий