0

I'd like to create wxFrame that won't get Destroyed when closed, so I can show them back later and also keep updating them even when they are hidden.

I tried to use SetExtraStyle(wxWS_EX_BLOCK_EVENTS) hopping that it won't propagate the wxCloseEvent to who ever is destroying it, but it did not help.

I found with the following solution. I have to create a handler that will handle the close event and hide the frame. In this case the event is not propagated furhter. But it's a bit heavy weight becasue I need to keep tack of the handler and delete it myself.

does Anyone have a smarter solution ?

    class FrameCloseHider
        : public wxEvtHandler
      {
      public:
        explicit FrameCloseHider(wxTopLevelWindow*);
        void internalOnQuit(wxCloseEvent&);
        wxTopLevelWindow* getFrame();
      private:
        wxTopLevelWindow*   frame_;
      };

     wxTopLevelWindow* FrameCloseHider::getFrame()
      {
        return this->frame_;
      }

      void FrameCloseHider::internalOnQuit(wxCloseEvent& obj)
      {
         this->frame_->Hide();
       }  

      FrameCloseHider::FrameCloseHider(wxTopLevelWindow* frame)
        :frame_(frame)
      {
        this->frame_->Connect(
          frame_->GetId(),
          wxEVT_CLOSE_WINDOW,
          wxCloseEventHandler(FrameCloseHider::internalOnQuit),
          NULL,
          this);
      }
0x26res
  • 11,925
  • 11
  • 54
  • 108
  • 1
    Are you sure that you really want to keep the window around, and not separate out the specific data you want to keep updating into another class? *(Like a document/view separation...)* Keeping the OS window around-but-invisible is kind of a weird optimization, and the more such things you do in the program the more confusing it gets. – HostileFork says dont trust SE Nov 29 '12 at 14:44
  • Why cant you derive from wxFrame and put the event handler in the derived class? – Pete Nov 29 '12 at 14:44
  • @Pete: I don't think it makes sense to inherit to add a feature like this one, because I may want to have a lot of different features and inheriting for each of them... is simply impossible. – 0x26res Nov 29 '12 at 14:53
  • @HostileFork: do you have any good documentation on this Document/View separtion ? Maybe stopping updates is an optimisation, but destroying the window and creating a new one is a bit complicated. The app I'm workin on is multithreaded and initializing in the UI thread and the other thread that produce the data is a pain. – 0x26res Nov 29 '12 at 14:55
  • I don't see how it would change anything, as you're really not supposed to make GUI calls from anything but the thread that created the GUI on pretty much most OSes. I'd imagine that applies to showing and hiding (though whether it will crash or hang or lead to bad behavior probably depends on circumstances). You should look for good examples of a worker/GUI separation where workers can queue requests to the GUI pipeline. Qt has a [great cross-thread messaging system](http://stackoverflow.com/questions/7210905/), so definitely consider switching...! :-) – HostileFork says dont trust SE Nov 29 '12 at 15:07
  • @HostileFork: It's probably to late to switch... Anyway the main issue is to initialize in the worker thread the element that will broadcast the data to the UI and in the UI thread and in the UI thread set up the window... So it's a synchronization issue – 0x26res Nov 29 '12 at 15:11
  • @jules Perhaps you should make a heavily reduced simple case that demonstrates your dilemma. For instance, where your "data updated by thread" is just a number that counts up from 0 with an update every second or so. Use that to demonstrate the concern you are facing, and ask it as a new question... – HostileFork says dont trust SE Nov 29 '12 at 15:14
  • I don't think the original question has anything to do with threading - the event handling in this case is all window related and should ALWAYS be done in the UI thread! You can of course post events from other threads but you would not be handling UI events from any other thread than the UI thread. – Pete Nov 30 '12 at 09:38

1 Answers1

1

If you take a look at the wxEvtHandler source code, you'll see that if you provide user data to connect, then it will be deleted when the connection is destroyed.

So, in your example, since wxEvtHandler inherits from wxObject, you should be able do this:

this->frame_->Connect(
  frame_->GetId(),
  wxEVT_CLOSE_WINDOW,
  wxCloseEventHandler(FrameCloseHider::internalOnQuit),
  this, // ** Use the event handler as the user data.
  this);

Now, since you want to ensure that this is only ever created on the heap, make the constructor of FrameCloseHider private and add a static function to make the connection instead:

public:
    static void ConnectTo(wxTopLevelWindow* frame) {
        FrameCloseHider* obj = new FrameCloseHider(frame);
        frame->Connect(
          frame->GetId(),
          wxEVT_CLOSE_WINDOW,
          wxCloseEventHandler(FrameCloseHider::internalOnQuit),
          obj,
          obj);
    }
private:
      FrameCloseHider::FrameCloseHider(wxTopLevelWindow* frame)
        :frame_(frame)
      {
      }

[note: you should probably make this exception safe by using an auto_ptr and releasing it after the connect call] You'll need to test to make sure that using an event handler for the user data in the connection doesn't crash it. It looks like it should be OK from the wx code but it is one of the more complicated aspects of wx and more difficult to tell for sure without spending more time which I don't really have.

You can make this more generic with some templates etc. I have done this but using a slightly different pattern with a singleton event handler that is not passed as the user data but having a separate object that handles the event.

Pete
  • 4,784
  • 26
  • 33
  • I did not have a look at the source code, but from the documentation it's not clear who owns the user data. Also I tried your idea and `~FrameCloseHider` never get called... – 0x26res Dec 03 '12 at 11:23
  • It should be if the frame is deleted. Try stepping into your wxFrame destructor and look at what happens when going through its base class wxEvtHandler dtor. – Pete Dec 03 '12 at 13:32