На самом деле есть две части вашей проблемы: первая - создание события, а вторая - его подключение к чему-либо.
Создание события является простой частью:
public class Card {
public event EventHandler<MouseEventArgs> Click;
protected void OnClick(MouseEventArgs e) {
EventHandler<MouseEventArgs> handler = Click;
if(handler != null) {
handler(this, e);
}
}
internal void CheckIfClicked(MouseEventArgs e) {
if(/* this mouse event intersects this Card */) {
OnClick(e);
}
}
}
Тогда в других местах вы можете настроить подписчиков для этого события, как обычно:
public class CardWatcher {
private Card card;
public CardWatcher(Card card) {
this.card = card;
this.card.Click += card_Clicked;
}
private void card_Clicked(object sender, MouseEventArgs e) {
// Our Card has been clicked; do something about it
}
}
(Обратите внимание, что мы подключаем обработчик событий вручную, но это почти такой же код, который конструктор Visual Studio сгенерировал бы, если бы вы использовали графический интерфейс для присоединения обработчика событий к элементу управления.)
И затем вам нужно где-то зафиксировать эти события щелчка (на самом деле, это похоже на суть вашей проблемы):
public partial class CardContainer : UserControl {
private List<Card> cards = new List<Card>();
public CardContainer() {
InitializeComponent();
// Initialize cards here...
}
protected override void OnMouseUp(MouseEventArgs e) {
foreach(Card card in cards) {
card.CheckIfClicked(e);
}
base.OnMouseUp(e);
}
}
Обратите внимание, что обычно существует два способа реагирования на событие:
- Создайте класс подкласса и переопределите поведение
OnEvent
метода
- Присоединить делегат обработчика событий
Как указали другие ответы, операционная система Windows заботится о доставке событий объектам, которые являются окнами (то есть объектам, имеющим дескриптор окна). В .NET это означает объекты, которые являются подклассами Control
.
Достаточно просто создать такие классы самостоятельно, либо путем подкласса Control
напрямую, либо путем подкласса чего-то вроде UserControl
, что затем позволяет создавать элементы управления, которые могут быть контейнерами для других элементов управления, которые вы создаете в представлении конструктора ( так же, как вы создаете формы).
Итак, ваш выбор следующий:
- Сделать
Card
элементом управления; затем его можно размещать в формах и напрямую получать события щелчка (и другие события). Недостатком является то, что это может использовать много ресурсов, если у вас много активных элементов одновременно, поскольку каждый выделяет дескриптор окна из Windows.
- Создайте контейнер для
Card
s, который является контрольным. Этот контейнерный элемент управления можно размещать в формах и напрямую получать события щелчка (и другие события). Недостаток заключается в том, что вы должны вручную выяснить, как делегировать эти события щелчка по отдельным карточкам. Поскольку фактическое событие Click
не несет с собой координат, вам, вероятно, придется обрабатывать событие MouseUp
.
- Поместите ваши
Card
s в другой контейнер (например, Form
) и прикрепите обработчик к событию Click
(или MouseUp
) этого контейнера. В этом обработчике выясните, были ли нажаты какие-либо из ваших Card
, и соответственно делегируйте события щелчка.
Обратите внимание, что все это не зависит от того, есть ли у Card
событие Click
, на которое могут подписаться пользователи Card
. Если вы посмотрите на первый пример кода в этом ответе, то нет причины, по которой метод CheckIfClicked
в Card
должен фактически запускать реальное событие, если у вас нет таких классов, как CardWatcher
, которые заинтересованы в знании событий click на Card
s. Если только событие Card
когда-либо заботится о событии click, то вы можете просто создать метод, такой как Card.HasBeenClicked(MouseEventArgs e)
, поместить туда код обработки кликов и позволить CheckIfClicked
вызывать его напрямую.