2. MVC in Windows Desktop Application with Cross-Platform Consideration ideal desktop application development
3. Obviously: MFC is not a good architecture/standard Most likely, there isn’t such an ideal architecture, like the Rails framework, or even something close to Cocoa Windows API and C++ isn’t helping that much on architecture of products The hard work has to be done by ourselves
5. Revised MVC Architecture for Us Model represents information, an underlying file format, database connections and queries View represents user interface, the window and displayed content designed by application logic Controller is the action processor, responding to command sent from views, pick the right model to process it, and update views to reflect action result
7. An Example, Redesigning Notepad Frame is the same (this is the root view):class CMainFrame: public CFrameWindowImpl<CMainFrame>{public:CEditm_edit; BEGIN_MSG_MAP(CMainFrame) MESSAGE_HANDLER(WM_CREATE, OnCreate)MESSAGE_HANDLER(WM_SIZE, OnSize) COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew) COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen) COMMAND_ID_HANDLER(ID_FILE_SAVE, OnFileSave)END_MSG_MAP()...
8. An Example, Redesigning Notepad WM_CREATE handling is the same (still a part of view):LRESULT CMainFrame::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){m_edit.CreateWindow(m_hWnd, WS_CHILD|WS_VISIBLE|... WM_SIZE too (another part of view)LRESULT CMainFrame::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ RECT rc_client;GetClientRect(&rc_client);m_edit.SetWindowPos(NULL, &rc_client...
9. An Example, Redesigning Notepad Before we go further, we research and design this application, in other words, we’ll decide what controller does and what model does
10. An Example, Redesigning Notepad We knew that Notepad should be able to process text files, so we should have a TextFile class that handles reading and writing, this would be a single threaded, static class whose only job is to accept a file name, and return std::wstringto us (this is a model)
11. An Example, Redesigning Notepad So we have:class TextFile{public: static boolReadFile(constwchar_t* filename, std::wstring& content_out); static boolWriteFile(constwchar_t* filename,constwchar_t* content_in);};
12. An Example, Redesigning Notepad We knew that user may issue command such as OpenFile, SaveFile, NewFile, so we design a FileController class that has these methods Additionally, this controller should have a buffer that maintains the current displayed text for editing (for use with views), thus we should have methods such as SetContent, and GetContent To deal with large files and potential slowness in networked opening, we may need to make OpenFile, SaveFileasynchronized, therefore we may need a method named IsLastFileOpCompleted for views to decide when toretrieve content (this is a controller)
13. An Example, Redesigning Notepad So we have:class FileController{public:boolOpenFile(constwchar_t* filename);boolSaveFile(constwchar_t* filename);boolNewFile();boolSetContent(constwchar_t* content);wchar_t* GetContent();boolIsLastFileOpComplete();private:std::wstringm_buffer; // boost::shared_ptr<boost::thread> m_fileop_thread; // boost::mutexm_buffer_mutex;};
14. An Example, Redesigning Notepad What about views?LRESULT CMainFrame::OnFileOpen(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled){CFileDialogfd(TRUE, NULL, NULL, OFN_EXPLORER, L”*.txt”, m_hWnd); if (!fd.DoModal(m_hWnd)) return 0; if (!m_view.m_filecontroller.OpenFile(fd.m_szFileName)) {MessageBox(L”There is a problem with file opening”); return 0; }SetTimer(TIMER_CHECKFILEOPENCOMPLETE, 50); // in WM_TIMER, we check m_filecontroller.IsFileOpComplete() // to decide whether to refresh the m_view using the content // retrieved via m_filecontroller.GetContent...
15. An Example, Redesigning Notepad We used timed polling to make sure our controller doesn’t know any details about views, and is thus directly portable Of course timed polling is bad, it consumes and wastes extra resources while checking for return values, due to the stupidity in its design But it can be easily controlled, when it’s required to cancel such a file operation, you kill the timer, and tell the controller to stop file operation
16. An Example, Redesigning Notepad Timed Polling (Timer) versus Mediator/Observer (Java Callback) versus Delegates (C# Callback) Observer pattern should be used in strict sense *MVC is macro-architecture and design pattern is micro-architecture
17. An Example, Redesigning Notepad Applying Observer pattern:class IFileObserver{public: virtual void SetFileOpComplete(booliscomplete) = 0;};class FileObserver: public IFileObserver{public: void SetReceiveWindow(HWND hwnd) {m_hwnd_receive = hwnd; } virtual void SetFileOpComplete(booliscomplete) { // call SetWindowText depending on |iscomplete|...
18. An Example, Redesigning Notepad Applying Observer pattern:class FileController{public: void AttachObserver(IFileObserver* observer); void DetachObserver();...void _OpenFile_WorkerThread(constwchar_t* filename, bool& ret) { // after file read successm_observer->SetFileOpComplete(true); }...
19. An Example, Redesigning Notepad To implement multi-tabbing, we put FileController inside CEdit, and replicate CEdit (via std::vector<CEdit>) so that each instance has a controller, a TabController might also be needed to manage tabs To implement syntax highlight, we implement HiliteNode (base model), HiliteParser (model) that translates wchar_t* string into std::vector<HiliteNode>, and make sure our FileController can output stuff returned by HiliteParser, then subclass CEdit to implement actual painting For unicode BOM, only need to revise TextFile class
20. Redesigning Notepad, What We’ve Achieved: Complete isolation of UI and application logic Complete isolation of application logic and low-level data formats Possible for independent development for most components Possible to run unit-test for each components, so that product quality can be assured Possible to easily port to other platforms Clear application logic for future reference Reusable components for future projects
21. Arch. Ver. of Notepad v.s. No-arch. Ver. Primary technical difference is class design, class relation, class difference Underlying logic, including frame window management, low-level file reading/writing are mostly the same
22. When Portability is Our Concern: Remember Cocoa/Objective-C is compatible with C++ Establish user interface on Cocoa framework like before, the equivalent view on Windows cannot be used Bind Cocoa’s interface controller actions to our real portable controllers Only controllers and models can be 100% portable