giving classes click events C#

2019-02-19 13:49发布

Hi i was just wondering if theres a way to give classes there own click event. For instance i have a card class, and is there a way of knowing when the user clicks on the rectangle (that displays the picture of the card) from that class?

or better yet, how do i go about knowing when the cards rectangle is clicked?

标签: c# events class
3条回答
Explosion°爆炸
2楼-- · 2019-02-19 14:14

You can easily add events to your class, but the only way the class can know when something was clicked is when it is, or has a component that is, based on a window.

That's what controls are for. If you need to write a class that can detect such events, I would recommend creating a control.

查看更多
一夜七次
3楼-- · 2019-02-19 14:23

There are actually two parts of your problem: first is creating the event, and second is hooking it up to something.

Creating the event is the easy part:

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);
        }
    }
}

Then elsewhere you can set up subscribers for that event, like usual:

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
    }
}

(Note that we're attaching an event handler manually, but this is pretty much exactly the same code the Visual Studio designer would generate if you used the GUI to attach an event handler to a control.)

And then you need to somewhere capture those click events (actually, this seems like the crux of your issue):

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);
    }
}

Notice that there are normally two ways of responding to an event:

  1. Create a subclass class, and override the behavior of the OnEvent method
  2. Attach an event handler delegate

As other answers have pointed out, the Windows operating system takes care of delivering events to objects that are windows (that is, objects that have a window handle). Within .NET, this means objects that are subclasses of Control.

It's easy enough to create such classes yourself, either by subclassing Control directly or by subclassing something like UserControl, which then allows you to create controls that can be containers for other controls that you create in the design view (the same way you create forms).

So, your choices are the following:

  • Make Card a control itself; then it can be positioned on forms and receive click events (and other events) directly. The drawback is that this can use a lot of resources if you have a lot of these elements active at one time, since each allocates a window handle from Windows.
  • Make a container for Cards that is a control. This container control can be positioned on forms and receive click events (and other events) directly. The drawback is that you have to figure out manually how to delegate those click events on to the individual cards. Since the actual Click event doesn't carry coordinates with it, you'll probably have to handle the MouseUp event instead.
  • Put your Cards into some other container (like a Form) and attach a handler to that container's Click (or MouseUp) event. In that handler, figure out if any of your Cards have been clicked on, and delegate the click events accordingly.

Note that all of this is independent of whether or not the Card itself has a Click event that users of the Card can subscribe to. If you look at the first code sample in this answer, there's no reason why the CheckIfClicked method on Card has to actually fire a real event, unless you have classes like CardWatcher which are interested in knowing about click events on Cards. If only the Card itself ever cares about the click event, then you can simply create a method like Card.HasBeenClicked(MouseEventArgs e), put your click-handling code there, and let CheckIfClicked call it directly.

查看更多
虎瘦雄心在
4楼-- · 2019-02-19 14:29

To get "mouse was clicked here" messages from Windows, you need to have a window handle; in WinForms, anything deriving from Windows.Forms.Control will have a window handle, and it will get mouse messages. Those messages will be translated automatically into invocations of the .NET MouseDown, MouseUp, MouseClick etc. events.

So probably your card should be a control. If it's not (e.g. if you have a "Hand" control that is responsible for managing and drawing lots of cards) then that control needs to be the one that gets mouse events (e.g. MouseClick) and figures out which card actually got clicked based on the context and the coordinates of the mouse event.

查看更多
登录 后发表回答