Implementing MVC in wxWidgets Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2293
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania
Contact:

Implementing MVC in wxWidgets

Post by evstevemd » Sat Aug 13, 2011 3:51 pm

Hi,
I was trying to implement ideas in this post to make real world application of principle. All I do now is simple: One wxListCtrl and one button (actually button are two but one is just for exit). I want, when user click the button to fetch new Emails, the model should simulate fetching Emails (for now hardcoded array of strings in Model class) through ModelClass::getNewEmails method.
I have set up wxFrame and child controls and now I want to write simple model class. I have faced two problems as for now:
1. How do I send some data to Model (something like useId logged in client) to model so that it can know which emails to fetch?
2. How to send alert to View from Model so that it will get the right data and update itself

For both, from linked post, there this idea of registering callbacks and firing events. I even got class from Brice which I find a bit hard to understand (I skipped operator overloading in my study of C++) and I find myself stranded and have no real answer as what should be next. Your ideas in my learning path are real crucial and appreciated,
Thanks!
(The code I have for now basically do basic stuffs that I found it useless to attach the for now. Later on I will post them here!)
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
[Ubuntu 19.04/Windows 10 Pro/MacOS 10.13 - GCC/MinGW/Clang, CodeLite IDE]

briceandre
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 670
Joined: Tue Aug 31, 2010 6:22 am
Location: Belgium

Re: Implementing MVC in wxWidgets

Post by briceandre » Sat Aug 13, 2011 4:31 pm

Here is a bunch of code on how to use that. It is not complete, I did not compiled it and I doubt it will compile as is, but it will probably give you an idea.

Code: Select all

class Model
{
   public:

      typedef struct
      {
         wxString name;
         wxString content;
      } EMailData_t;

      static Model& GetInstance()
      {
         static Model model;
         return model;
      }

      void FetchEMails()
      {
         /* Suppose we get only one mail... */
         EMailData_t data;
         data.name = "EMail 1";
         data.content = "Hello World";

         /* store e-mail in DB */
         //TODO

         /* Notify interested viewers */
         new_mail_callbacks.TriggerCallbacks(&data, true);
      }

      template <typename T_object>
      CallbackHandler::Callback_ID NotifyOnNewMail(T_object* object, void(T_object::*function)(void*,void*), void* user_data)
      {
         new_mail_callbacks.RegisterCallback(object, function, user_data);
      }

      void UnnotifyOnNewMail(CallbackHandler::Callback_ID id)
      {
         new_mail_callbacks.UnregisterCallback(id);
      }


   private:

      Model()
      {
      }

      CallbackHandler new_mail_callbacks;
};

class Frame : public wxFrame
{
   public:
      Frame():wxFrame()
      {
         /* Format frame */
         //TODO

         /* connect viewer to model */
         received_mail_cb_id = Model::GetInstance().NotifyOnNewMail(this, &Frame::OnNewMailReceived, NULL);

         /* connect retrieve_mail button to OnRetrieveButtonClicked method */
         //TODO : Connect(...);
      }

      ~Frame()
      {
         Model::GetInstance().UnnotifyOnNewMail(received_mail_cb_id);
      }

   private:

      void OnNewMailReceived(void* trigger_data, void* user_data)
      {
         Model::EMailData_t* email_data = (Model::EMailData_t*)trigger_data;

         /* Update mail_list with new e-mail received */
         //TODO...
      }

      void OnRetrieveButtonClicked()
      {
         Model::GetInstance().FetchEMails();
      }

      wxButton* retrieve_mail;
      wxListCtrl* mail_list;

      CallbackHandler::Callback_ID received_mail_cb_id;
};

User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2293
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania
Contact:

Re: Implementing MVC in wxWidgets

Post by evstevemd » Sun Aug 14, 2011 9:06 pm

Thanks Brice. I was out of net for a long while.
Let me check with your code and I will be back.
Again thanks alot!
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
[Ubuntu 19.04/Windows 10 Pro/MacOS 10.13 - GCC/MinGW/Clang, CodeLite IDE]

User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2293
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania
Contact:

Re: Implementing MVC in wxWidgets

Post by evstevemd » Mon Aug 15, 2011 12:07 am

I'm struggling on how callback register/Unregister works.
Does it use class provided in other threads? I think I'm diving in whole new field and have done good reading on net concerning call backs. I have not got it yet that much, but still learning :oops:
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
[Ubuntu 19.04/Windows 10 Pro/MacOS 10.13 - GCC/MinGW/Clang, CodeLite IDE]

briceandre
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 670
Joined: Tue Aug 31, 2010 6:22 am
Location: Belgium

Re: Implementing MVC in wxWidgets

Post by briceandre » Mon Aug 15, 2011 6:43 am

I'm struggling on how callback register/Unregister works.
Do you have problems with the way you should use the CallbackHandler class, or do you have problem to understand its internal stuff ?
Does it use class provided in other threads?
The CallbackHandler class does not use threads. Note also that it is not thread-safe. So, if you envisage using it in multi-threaded applications, you should either ensure that it will not be accessed by ultiple threads or you should modify its code so that it is thread safe (note that it shoul be quite easy to do).
I think I'm diving in whole new field and have done good reading on net concerning call backs. I have not got it yet that much, but still learning
If you are interested in understanding how it works, I will just give you a few points so that it will be easier for you to start reading it.

The class allows registering all kind of functions as callbacks (static or non-static member functions, and non-class functions). In C++, you do not hanlde all those callbacks types in the same way : for static-member functions and non-class functions, all you need to know in order to invoke your callback is the signature of the function and its address. For non-static member functions, you need to know its signature, its address, and the class to which it belongs.

Note that, for simplicity purpose, all functions that are registered in the CallbackHandler mechanism will have the same signature : "void callback(void* trigger_data, void* user_data)". The trigger_data is a parameter that is provided by the one who triggers the callbacks (the same for all callbacks for one trigger). The user_data is a parameter that is provided by the one who registers the callback (for one callback, it will always be the same).

All callbacks data (address of function, user_data associated to it, and the class it belongs, if requested) are stored in an object called 'GenericCallback'. The CallbackHandler thus stores an array of GenericCallback that contains all registered callbacks. But, GenericCallback is an virtual class that needs to be derived.

All static member functions and non-class functions callbacks are stored in a derived object called 'StaticCallBack'. We can use the same class for all those callbacks as we will store the same stuff for all of them.

The problem arises for non-static member functions. In order to perform the call, you must cast the object pointer into the proper class, which requests a specific code for each class for which you register a non-static member function callback. This is why I use templates. The class 'InstanceCallBack' derives our 'GenericCallback', but with the use of templates. So, for each class for which you register a member-function callback, you will have a dedicated 'InstanceCallBack' class that will allow to register the callbacks.

Now, for the operator overload, I sincerely do not remember why I used them. If you read the code, you will realise that you do not need them. This code was written a long time ago and maybe that, at that time, it had an utility ? So, if it really bothers you, you can simmply remove them. To do so, you need to suppress the 'void operator()(void* triger_data, void* user_data)' declared in the three classes and replace the code of the functions 'void execute(void* triger_data, void* user_data)' by the one located in the 'operator()' one.

I hope that with this information, it will be simplier. If you have questions, do not hesitate.

User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2293
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania
Contact:

Re: Implementing MVC in wxWidgets

Post by evstevemd » Mon Aug 15, 2011 5:49 pm

I was reading the codes you gave me. What I have understood is being done is a kind of observer pattern. That is, for each call to model from controller, the model calls the TriggerCallBacks function which updates all views registered on the publisher class (CallBackHandler). The CallBackHandler maintains list of views and tells them to updates themselves via registered callbacks. I have two questions, presumably my explanations are correct.
1. Can you elaborate the process of registering callbacks and views objects. I try to understand the code but some things seem to be missing or may be I miss them like Entry_t class. How do you register views objects/callbacks
2. How to work with diffrent models in same app using same callbackhandler class?
Sorry if they are kiddish. I am a kid in this arena I have dived in. So please bear with my ignorance!
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
[Ubuntu 19.04/Windows 10 Pro/MacOS 10.13 - GCC/MinGW/Clang, CodeLite IDE]

briceandre
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 670
Joined: Tue Aug 31, 2010 6:22 am
Location: Belgium

Re: Implementing MVC in wxWidgets

Post by briceandre » Mon Aug 15, 2011 6:30 pm

CallBackHandler is just a helper that has nothing to do with model, view or controller. It's just an object in which you can store different callbacks that you can invoke thanks to the method 'TriggerCallbacks'.

When you implement your model, you will define a set of events to which each view can register. In my example, I only created one event type : 'NotifyOnNewMail', but you can create as many as you want ('MailMarkedAsRead', 'MailSuppressed', etc). A view can subscribe to the events in which it is interested. Imagine that a view is interested in new mails, but not in mails marked as read, it will thus subscribe only to first event type.

Thus, for each event type, you can use, in your model, a dedicated instance of CallbackHanlder to store all callbacks of all views that have registered to this event type.

Well, I am not sure I answered your question, but I think I did not understood what you mean... Note that 'Entry_t' is an internal stuff of CallbackHanlder. You are not obliged to understand the internal mechanisms of CallbackHanlder to understand the way I implement MVC pattern. What I would suggest you is to try to understand the MVC part, try to write your own sample, and, when you will have understood that properly, you can dig into CallbackHandler.

If you write your own sample, you can send it to me if you want. I will read it and give you feedback.

User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2293
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania
Contact:

Re: Implementing MVC in wxWidgets

Post by evstevemd » Mon Aug 15, 2011 10:10 pm

briceandre wrote:CallBackHandler is just a helper that has nothing to do with model, view or controller. It's just an object in which you can store different callbacks that you can invoke thanks to the method 'TriggerCallbacks'..
Well, I wanted to understand how it works so that I can develop something similar, for learning the techniques.
briceandre wrote:When you implement your model, you will define a set of events to which each view can register. In my example, I only created one event type : 'NotifyOnNewMail', but you can create as many as you want ('MailMarkedAsRead', 'MailSuppressed', etc). A view can subscribe to the events in which it is interested. Imagine that a view is interested in new mails, but not in mails marked as read, it will thus subscribe only to first event type..
Do you use event and callbacks interchangeably here?
briceandre wrote:Thus, for each event type, you can use, in your model, a dedicated instance of CallbackHanlder to store all callbacks of all views that have registered to this event type..
You mean for each event CallbackHandler instance will hold list of view that are interested as well as callbacks to be called? If so when do the views register themselves? In wxFrame constructor?
briceandre wrote:Well, I am not sure I answered your question, but I think I did not understood what you mean... Note that 'Entry_t' is an internal stuff of CallbackHanlder. You are not obliged to understand the internal mechanisms of CallbackHanlder to understand the way I implement MVC pattern. .
you answered a lot of questions, some of them may be I could have asked later!
briceandre wrote:What I would suggest you is to try to understand the MVC part, try to write your own sample, and, when you will have understood that properly, you can dig into CallbackHandler..
Once I'm past this fog I will go on writing my sample
briceandre wrote:If you write your own sample, you can send it to me if you want. I will read it and give you feedback.
I will do and thanks for support!
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
[Ubuntu 19.04/Windows 10 Pro/MacOS 10.13 - GCC/MinGW/Clang, CodeLite IDE]

briceandre
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 670
Joined: Tue Aug 31, 2010 6:22 am
Location: Belgium

Re: Implementing MVC in wxWidgets

Post by briceandre » Tue Aug 16, 2011 5:47 am

Do you use event and callbacks interchangeably here?
WhenI use the word 'event', it's conceptual. When such an 'event' occurs, you should notify all views that are interested in it. So, as disussed in the previous thread, you can use different mechanisms (wxEvents, boost signals). But here, in this sample, I use my self-made class callled CallbackHandler. So, each time I speak of 'event', you should translate 'CallbackHanlder'.
You mean for each event CallbackHandler instance will hold list of view that are interested as well as callbacks to be called? If so when do the views register themselves? In wxFrame constructor
In my way of implementing MVC, a view never register itself. It registers 1 dedicated callback for each event it is interested in. In my example, the view was interested in new mails incoming, so it registered it's callback named 'Frame::OnNewMailReceived'. So, the model has not a list of all views. But, for each 'event', it has a list of all callbacks of all views that are interested in this event.

User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2293
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania
Contact:

Re: Implementing MVC in wxWidgets

Post by evstevemd » Fri Aug 19, 2011 10:12 pm

I have used your code and built something like this.
I have some questions:
Suppose one app have many Models (Reading Email, reading users in database, et al) and all have same view (same wxLC somehow), will I have to register same callback and handle multiple data type or should I make two callback for each action and register them to different models?

If I have many views and many models, how do you advice I should handle the callback Id during unregistering objects? I have especially problem on knowing which callback_id did register to which model so that I can unregister it.
Thanks!

EDIT:
Compile command (Linux):
g++ *.cpp *.h `wx-config --cxxflags --libs` -o appName
Attachments
SimpleMVC.zip
MVC Sample app 2011
(6.71 KiB) Downloaded 321 times
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
[Ubuntu 19.04/Windows 10 Pro/MacOS 10.13 - GCC/MinGW/Clang, CodeLite IDE]

briceandre
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 670
Joined: Tue Aug 31, 2010 6:22 am
Location: Belgium

Re: Implementing MVC in wxWidgets

Post by briceandre » Sat Aug 20, 2011 5:55 am

I read your code and it seems to be OK.
will I have to register same callback and handle multiple data type or should I make two callback for each action and register them to different models?
You can choose the solution that you prefer. Note that if you choose first option, you can use the 'user_data' to discriminate from which model your callback is triggered. In this case, when you register your callback for e-mail model, you provide a user_data whose value is, let's say 1, and for the users, 2. When the callback is trgiggered, you can check this value and you know from where the callback was invoked.
If I have many views and many models, how do you advice I should handle the callback Id during unregistering objects? I have especially problem on knowing which callback_id did register to which model so that I can unregister it.
I don't understand this question. If you register a callback for e-mails, you store it's id in a variable called 'email_callback_id', and when you register the callback for users, you call it 'user_callback_id'. In the destructor of your view, you cannot make a mistake. Maybe I don't understand what you mean ?

A last note on the implementation of CallbackHandler : When I wrote this class, I made the assumption that it would contain only a few number of elements (in my application, this never goes above, let's say 10). If you use it for large amount of callback, the code will not crash, but it will probably be very slow when registering new callbacks. If you manage to handle a large number of callbacks, I would advice you to rewrite the registering-unregistering mechanisms and use a hashtable instead of a vector.

User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2293
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania
Contact:

Re: Implementing MVC in wxWidgets

Post by evstevemd » Sat Aug 20, 2011 2:40 pm

Thanks Brice,
I think my basic questions are answered.
I will use hashmap in my real world code.
Thanks again! :D
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
[Ubuntu 19.04/Windows 10 Pro/MacOS 10.13 - GCC/MinGW/Clang, CodeLite IDE]

ollydbg23
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 200
Joined: Fri Dec 12, 2008 10:31 am

Re: Implementing MVC in wxWidgets

Post by ollydbg23 » Sun May 19, 2019 3:46 am

Hi, I notice that there is a bug in your SimpleMVC.zip sample code which lead to crash when you click the "exit" button.

Here:

Code: Select all

MainFrame::~MainFrame() {
	ModelClass::getInstance()->UnNotifyOnNewMail(view_id);
}


/*Controller functions*/
void MainFrame::controllerGetNewMails(wxCommandEvent& e) {
	//get some data from object if needed
	ModelClass::getInstance()->getNewEmails(1);
	
}

void MainFrame::controllerExit(wxCommandEvent& e) {
	ModelClass::release();
	this->Destroy();
}
Here, when you click the "Exit" button, the function controllerExit will be called, and the static member get deleted.

Code: Select all

void ModelClass::release() {
	delete ModelClass::theInstance;
	ModelClass::theInstance = NULL;
}
But, "this->Destroy" will trigger the function call

Code: Select all

MainFrame::~MainFrame() {
	ModelClass::getInstance()->UnNotifyOnNewMail(view_id);
}
With the ModelClass::getInstance(), you have to construct a static member again:

Code: Select all

ModelClass* ModelClass::getInstance() {
	if(ModelClass::theInstance==NULL) {
		ModelClass::theInstance = new ModelClass;
	}
	return ModelClass::theInstance;
}
But in the new constructed ModelClass::theInstance, you can't find an event handler with the ID "view_id", which finally lead a crash.

Can you fix this issue? Thanks.

Post Reply