CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 15 of 22

Threaded View

  1. #1
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Event handlers vs. base class method overrides

    This is a spin-off of another thread. I detached it because it was a bit off-topic over there and could be the beginning of another interesting discussion. Also, the original thread has already been marked [RESOLVED] so some people might miss it there.

    Quote Originally Posted by TheGreatCthulhu View Post
    [...] .NET events are essentially just a syntactic sugar for what is in its core the Observer pattern. Events are a form of the public interface, so in that sense, handling an event is no different from calling public methods or using public properties. It can break encapsulation in the same way a method can (by directly exposing some internal aspect of the class). Semantically, events are meant to provide a notification when some state changes, so that event clients (observers) might respond to them.

    For example, a button usually does not handle its own Click event, but some other, external class. The class that fires the events "knows" when the internal state changes, since (if the encapsulation principle was respected) it is the one that changes the state. In that sense, there's generally no need for a class to handle its own events, unless they come from a parent class, and no other mechanism exist.
    Just as sometimes a method can break encapsulation by exposing a pointer or a .NET handle of an internal object, so that an external class might alter it's state, so can an event.
    Sometimes, this behavior is desirable, sometimes it's not.

    In part, it's a matter of preference, and in part is driven by the requirements.
    Oops... I actually failed to observe (pun intended ) the fact that there's a solid, generally accepted design pattern that backs MS's concept of event handlers in .NET.

    My preference for overloads of the base class' On...() methods (if I already have a derived class anyway) may be a heritage from the MFC code I've written: It's pretty much the standard procedure there. Also, MSDN frequently states that overriding these methods is the preferred way to handle events in the "Notes to inheritors" section of the method documentation. I haven't found an example of the phrase on MSDN in a quick search, but IIRC it says (or at least means) that it's the preferred method if you already have a derived class. I think registering an event handler as a delegate with the base class from the derived class looks weird and not reasonable to me anyway.

    The example of the button click handler you give seems to be well chosen. Unless the action to be triggered by the button is close to trivial, even in MFC the click event handler would most likely call out to some other code. And I think that, though the call to them is implemented in a much fancier way, the .NET event handlers are essentially nothing else than that code that gets called.

    As already mentioned in the original thread, I have recently written a demo project for another thread which contains a class that uses (resp. is used with) a combination of both base class method overrides and event handlers. I didn't actually post the code in the other thread because it was for a homework assignment, but that was almost a month ago and so I think the homework has already been handed in in the meantime anyway. So I think I can post the code here and now as a basis for discussion.

    The thread the code originally was written for contains additional discussion about the code and in particular the description of the objective of the assignment in post #1.

    The code generates a draggable shape on the form in response to a button click. The shape gets destroyed when it itself is right-clicked. The shape in question is a pentagram and what's going on inside the class that draws it isn't really spectacular and doesn't matter here. So I'll just post the .h file declaring that class' interface:

    Code:
    // Pentagram.h
    
    #pragma once
    
    using namespace System;
    using namespace System::Drawing;
    
    namespace Test7
    {
    
    public ref class Pentagram
    {
    public:
      Pentagram(Point location, float radius) : m_location(location), m_radius(radius)
      {}
    
      void SetLocation(Point location);
      void Draw(Graphics ^gr);
    
    private:
      Point m_location;
      float m_radius;
    
    private:
      Pentagram() {}  // Disable default construction
    };
    
    }
    The class that implements the actual draggable shape on the form (by using the Pentagram class above) is derived from PictureBox and is called PentagramBox:

    Code:
    // PentagramBox.h
    
    #pragma once
    
    #include "Pentagram.h"
    
    namespace Test7
    {
    
    using namespace System;
    using namespace System::Windows;
    using namespace System::Windows::Forms;
    using namespace System::Drawing;
    
    public ref class PentagramBox : public PictureBox
    {
    public:
      PentagramBox(Control ^parent, Rectangle bounds);
    
    protected:
      virtual void OnPaint(PaintEventArgs ^pe) override;
      virtual void OnMouseDown(MouseEventArgs ^e) override;
      virtual void OnMouseUp(MouseEventArgs ^e) override;
      virtual void OnMouseMove(MouseEventArgs ^e) override;
    
    private:
      Pentagram ^pent;
      bool dragging;
      Point lastmousepos;
    
    private:
      PentagramBox() {}  // Disable default construction
    };
    
    }
    Code:
    // PentagramBox.cpp
    
    #include "StdAfx.h"
    
    #include "PentagramBox.h"
    
    using namespace Test7;
    
    PentagramBox::PentagramBox(Control ^parent, Rectangle bounds)
    {
      Parent = parent;
      Bounds = bounds;
      pent = gcnew Pentagram(Point(bounds.Width / 2, bounds.Height / 2), Math::Min(bounds.Width / 2, bounds.Height / 2));
      dragging = false;
    }
    
    void PentagramBox::OnPaint(PaintEventArgs ^pe)
    {
      __super::OnPaint(pe);
      pent->Draw(pe->Graphics);
    }
    
    void PentagramBox::OnMouseDown(MouseEventArgs ^e)
    {
      if (e->Button == Forms::MouseButtons::Left) {
        dragging = true;
        lastmousepos = e->Location;
      }
      __super::OnMouseDown(e);
    }
    
    void PentagramBox::OnMouseUp(MouseEventArgs ^e)
    {
      if (e->Button == Forms::MouseButtons::Left) dragging = false;
      __super::OnMouseUp(e);
    }
    
    void PentagramBox::OnMouseMove(MouseEventArgs ^e)
    {
      if (dragging) {
        Drawing::Size movevect(e->Location.X - lastmousepos.X, e->Location.Y - lastmousepos.Y);
        Location += movevect;
      }
      __super::OnMouseMove(e);
    }
    You see, almost all aspects of mouse handling are done inside the class itself which provides better encapsulation. Only the right-click that destroys the draggable shape is handled in an event handler inside the form class. These are the form class' event handlers:

    Code:
    // Form1.cpp
    
    #include "stdafx.h"
    
    #include "Form1.h"
    
    using namespace Test7;
    
    System::Void Form1::bnCreateShape_Click(System::Object^  sender, System::EventArgs^  e)
    {
      pentbox = gcnew PentagramBox(this, Rectangle(10, 10, 42, 42));
      pentbox->MouseClick += gcnew MouseEventHandler(this, &Form1::pentbox_MouseClick);
      bnCreateShape->Enabled = false;
    }
    
    void Form1::pentbox_MouseClick(Object ^sender, MouseEventArgs ^e)
    {
      if (e->Button == Forms::MouseButtons::Right) {
        delete pentbox;
        pentbox = nullptr;
        bnCreateShape->Enabled = true;
      }
    }
    I handled that single mouse action differently because I don't feel comfortable with self-destroying objects. And even what I actually did here looks suspicious: Doesn't the event handler return to something that has already been destroyed? Probably .NET's lazy destruction and the fact that tracking handles are reference counted saved my day here. Think I better shouldn't try that in native C++...

    Another reason for handling it that way, aside from being reluctant about self-destroying objets, is that the event handler also manipulates form class members that are not visible to the PentagramBox class.

    Any thoughts?

    I also have attached the complete project (VS 2010) to this post. Unlike the ZIP file, the project inside is named Test7, but I already have uploaded a Test7.zip in another thread, and though uploading files with the same name to different threads would even work, I wanted to make sure to avoid confusion. Of course that project also contains the code files I didn't post here.
    Attached Files Attached Files
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured