Делаю GUI для MonoGame. Обработка происходит через метод Update(), что не всегда удобно, поэтому я решил вынести некоторый функционал в события. Есть классы MouseListener и KeyboardListener, которые имеют свои события, все вызывается и работает, на этом этапе все хорошо.
public class KeyboardListener : Listener
{
public event EventHandler<KeyboardEventArgs> KeyUp;
public event EventHandler<KeyboardEventArgs> KeyDown;
public event EventHandler<KeyboardEventArgs> KeyPressed;
public event EventHandler<KeyboardEventArgs> KeyReleased;
public event EventHandler<KeyboardEventArgs> KeyTyped;
}
Дальше есть обработчик GUI, который следит за нажатиями мыши и, если нажатие произошло поверх элемента интерфейса, ставит на него фокус. Элементы интерфейса также имеют свои события (общие: нажатие мыши, нажатие кнопки клавиатуры, индивидуальные: изменение текста, картинки в PictureBox и т.д.). После того, как элемент становится фокусным, необходимо подписать вызов некоторых его событий на события из MouseListener и KeyboardListener. Я попробовал сделать через анонимные методы, но это работает только в одну сторону - отписаться таким же способом не получается.
public class Control
{
public event EventHandler<KeyboardEventArgs> KeyUp;
...
protected void OnKeyUp(KeyboardEventArgs args)
{
if (KeyUp != null)
KeyUp.Invoke(this, args);
}
...
public void SetFocused()
{
KeyboardListener.Instance.KeyUp += delegate(object sender, KeyboardEventArgs args)
{
OnKeyUp(args);
};
}
public void SetUnfocused()
{
KeyboardListener.Instance.KeyUp -= delegate(object sender, KeyboardEventArgs args)
{
OnKeyUp(args);
};
}
}
Пробовал подписывать метод Control.KeyUp.Invoke на KeyboardListener.Instance.KeyUp, но Control.KeyUp иногда равен null, и не получается подписать метод нулевой ссылки.
Скорее всего, можно решить проблему через создание специального метода для вызова события с сигнатурой, совпадающей с вызывающим событием, но тогда получится +18 служебных методов, а их и так уже порядком хватает.
Вопрос: как сделать то, что я пытался сделать выше, с наименьшими потерями?
И подвопрос: по-вашему, стоит ли так потеть ради событийного GUI, или есть какой-нибудь другой вариант, чтобы не приходилось раздувать метод Update на 500 строк, вмещая в него всю логику, какую только можно было придумать?
P.S. не бейте за стену текста, не придумал, как объяснить короче
UPD: Смотрел различные open-source GUI, нашел весьма неплохой вариант: не объявлять в Control общие события вроде KeyDown или MouseMoved, а заменить их виртуальными методами вроде OnKeyDown, OnMouseMoved. В производных классах переопределять эти методы, заключая в них логику, свойственную данному элементу управления, например, проверку на нажатость кнопки для Button. И в производных классах реализовывать только специализированные события вроде Button.Pressed или TextBox.TextChanged.
Отвечаю сам, т.к. нашел решение для себя.
Для моего случая не обязательно подписывать событие на событие (вообще события объявлять не нужно, за исключением некоторых функциональных), можно обойтись иначе, т.к. поведение у разных экземпляров класса Label, например, меняться не будет.
public class Control
{
//public event EventHandler<MouseEventArgs> MouseDown;
protected virtual void OnMouseDown(object sender, MouseEventArgs args)
{
}
}
public class Button : Control
{
// Обеспечение функционала конкретного элемента
public event EventHandler<EventArgs> Pressed;
private void OnPressed(EventArgs args)
{
if (Pressed != null)
Pressed.Invoke(this, args);
}
// Замена подписыванию на Control.MouseDown
protected override void OnMouseDown(object sender, MouseEventArgs args)
{
if (/* кнопка нажата */)
OnPressed(EventArgs.Empty);
}
}
Ну и для тех, кто будет по названию вопроса искать решение схожей проблемы:
Объявить метод сигнатуры вызывающего события, который будет инвокать вызываемое событие:
// событие, на которое надо подписаться
public event EventHandler<EventArgs> Event1
// событие, вызов которого нужно подписать
public event EventHandler<EventArgs> Event2;
// метод для "ручного" вызова
private void OnEvent2(EventArgs args)
{
if (Event2 != null)
Event2.Invoke(this, args);
}
// метод для подписывания на Event1
public void InvokeEvent2(object sender, EventArgs args)
{
if (Event2 != null)
Event2.Invoke(this, args);
}
// подписывание
Event1 += InvokeEvent2;
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Подскажите, как сделать, чтобы текст, раннее введенный в ячейку DataGrid, повторялся после следующего ввода в другую ячейку (примерно как в Excel,...
Допустим есть файлВ один момент времени им может пользоваться один инстанс программы