@Aquinary
Хинафаг.

События мыши и запрет на выполнения метода более одного раза?

Всем доброго времени суток!
Пишу собственный 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()проверяется, было ли пересечение кнопки и курсора, был ли клик и т.д. и на основе этого уже вызывается нужное нам событие.
Проблема, которая имеется сейчас: метод вызывается не единожды, а множество раз подряд: при отсутствии наведении мыши на кнопку постоянно вызывается событие OnMouseOut(), при наведении - OnMouseIn(), при нажатии и опускании кнопки соответствующие события.
Все это приводит к тому, что, например, вывод в консоль любого текста происходит множество раз, но требуется не более одного.
Я попробовал решить проблему с помощью дополнительных полей:
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 соответствующие изменения.
И все это работает, до тех пор, пока пользователь ведет себя нормально. Однако если пробовать различные ситуации, например, зажать ЛКМ, после чего навести на кнопку и отпустить, или зажать ЛКМ над кнопкой и отпустить ее в другом месте - все это приводит к тому, что в какой-либо момент то или иное событие, которое должно было быть вызвано - не вызывается, либо наоборот, вызывается то событие, которые вызываться не должно.
Я попробовал как можно более подробно описать свою проблему, но если будут какие-нибудь вопросы - задавайте, уточню, если нужно.
  • Вопрос задан
  • 746 просмотров
Пригласить эксперта
Ответы на вопрос 1
@tex0
Поставьте бряку в том месте где вы подписываетесь на событие
button.MouseInHandler += Название_метода;
и посмотрите сколько раз это происходит.
Это распространенная ситуация, когда в одном контексте на событие подписались, а отписаться забыли и поэтому invocation list события набивается лишними обработчиками.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы