c++c++builderc++builder-6

Assigning events to a VCL control created dynamically at runtime


I'm trying to create a dynamic VCL control at runtime and assign an event handler to it.

I think I've tried everything to get this to work, but cannot (everything I try generates different errors).

If you could help fill in the question marks in the code below, it would be of great help!

Oh, in case you're wondering why it's in a namespace instead of a class, it's because this is actually a large library that I just kept adding to. When I change it to a class, you'd think it would be fine, but no, it generates a gazillion weird errors.

test.h

namespace TestSpace {
    TTimer *time;
    AnsiString file;
    void __fastcall MyFunc(AnsiString f);
    ?Declaration for OnTimer event?
}

test.cpp

void __fastcall TestSpace::MyFunc(AnsiString f) {
    TestSpace::file = f;
    TestSpace::time->OnTimer = ?;
    TestSpace::time->Enabled = true;
}

TestSpace::?(TObject* Sender) {
    TestSpace::time->Enabled = false;
    DeleteFile(TestSpace::file);
}

Solution

  • A VCL event handler is expected to be a non-static member of a class. Your OnTimer handler is not a class member, it is a free-floating function instead (the namespace is not important).

    The correct way to solve this issue is to create a class for your OnTimer event handler, and then instantiate that class alongside the TTimer, eg:

    test.h

    namespace TestSpace {
        class TimerEvents {
        public:
            void __fastcall TimerElapsed(TObject *Sender);
        };
        TTimer *time;
        TimerEvents time_events;
        AnsiString file;
        void __fastcall MyFunc(AnsiString f);
    }
    

    test.cpp

    void __fastcall TestSpace::MyFunc(AnsiString f) {
        TestSpace::file = f;
        TestSpace::time->OnTimer = &(time_events.TimerElapsed);
        TestSpace::time->Enabled = true;
    }
    
    void __fastcall TestSpace::TimerEvents::TimerElapsed(TObject* Sender) {
        // 'this' is the TimerEvents object
        // 'Sender' is the TTimer object
        TestSpace::time->Enabled = false;
        DeleteFile(TestSpace::file);
    }
    

    That being said, there is actually an alternative way to use a free-floating function as a VCL event handler like you want, by using the System::TMethod struct:

    test.h

    namespace TestSpace {
        TTimer *time;
        AnsiString file;
        void __fastcall MyFunc(AnsiString f);
        // NOTE: must add an explicit void* parameter to receive
        // what is supposed to be a class 'this' pointer...
        void __fastcall TimerElapsed(void *This, TObject *Sender);
    }
    

    test.cpp

    void __fastcall TestSpace::MyFunc(AnsiString f) {
        TestSpace::file = f;
        TMethod m;
        m.Data = ...; // whatever you want to pass to the 'This' parameter, even null...
        m.Code = &TestSpace::TimerElapsed;
        TestSpace::time->OnTimer = reinterpret_cast<TNotifyEvent&>(m);
        TestSpace::time->Enabled = true;
    }
    
    void __fastcall TestSpace::TimerElapsed(void *This, TObject* Sender) {
        // 'This' is whatever you assigned to TMethod::Data
        // 'Sender' is the TTimer object
        TestSpace::time->Enabled = false;
        DeleteFile(TestSpace::file);
    }