• [WTL/Win32]_[中级]_[MVP架构在实际项目中的应用]


    场景

    1. 在开发Windows和macOS的界面软件时,Windows用的是WTL/Win32技术,而macOS用的是Cocoa技术。而两种技术的本地语言一个主打是C++,另一个却是Object-c。界面软件的源码随着项目功能增多而增多,这就会给同步Windows和macOS的功能造成很大负担。 有大部分的调用底层逻辑,界面数据搜索等代码逻辑会重复写两遍,用C++和Object-C写。初步估计这部分代码至少占界面代码的50%。这无疑会影响产品发布速度,影响软件质量(可能有些逻辑两个平台不能完全一样),增大了开发的工作量,这部分时间无疑是浪费了的。有什么办法可以把这部分代码进行重用?

    说明

    1. 界面代码往往就是获取数据,显示数据,之后点击按钮处理数据。这就需要获取数据,处理数据需要和平台相关的界面代码剥离,显示数据部分依赖平台的框架进行数据绘制。 按照这种逻辑,最好的办法就是数据获取和处理使用C++语言处理,处理这些和界面无关的逻辑代码。 当然如果有特殊情况也可以用.mm文件(这种是Object-C和C++混编的文件后缀)来调用Object-C平台接口处理。比如presenter_mac.mm和presenter_win.cpp。

    2. 界面架构MVP架构可以满足这个要求。当然这和Object-c和C++可以混编有些关系,如果是Swift语言,不能直接调用C++类,需要通过桥接调用Object-C,再通过Object-C调用C++来处理。 使用C++处理跨平台逻辑,最好使用C++11以上标准,因为这个标准多了很多有用的库和特性节省MVP架构的很多代码,如lambda,thread,functional等。

    3. 看看MVP架构的分层,主要是以下三层。很好理解,Presenter作为View和Model的通讯层,起到了连接视图和底层模型逻辑处理的作用,也起到了跨平台时处理不同平台界面框架获取数据的本地实现的桥梁。

    View <-> Presenter <-> Model
    
    1. 这里说的Presenter可根据界面语言的实现进行基于本地的实现,比如macOS下Presenter层需要处理NSString作为字符串存储的数据,而在Windows下需要处理std::wstring作为字符串处理的数据。这些数据如果传递给Model,那么需要转换为std::string的UTF8编码进行处理。

    2. 在这个三层模型里,依赖关系需要注意设计, 切不可以互相依赖。

      • View依赖Presenter接口,View里有Presenter的成员变量,Presenter的实例需要通过方法注入。这样如果View更换不同的Presenter也可以通过注入的方法。 View通过成员变量presenter_调用它的方法。异步处理通过传入std::function绑定的方法给Presenter,当Presenter处理完之后再调用绑定的方法,类似回调函数的处理。
      
      private:
      	shared_ptr<Presenter> presenter_ = nullptr;
      
      ...
      void CView::setPresenter(shared_ptr<Presenter> presenter)
      {
      	presenter_ = presenter;
      }
      
      • Presenter依赖Model接口,注意说的接口是根据实际项目的复杂度来定义虚拟类接口,如果实现只有一个,定义一个普通类就行。Presenter通过成员变量model_调用它的方法。异步处理通过传入std::function绑定的方法给Model,当Model处理完之后再调用绑定的方法,类似回调函数的处理。
      protected:
      	shared_ptr<Model> model_;
      
      ...
      void Presenter::setModel(shared_ptr<Model> model)
      {
      	model_ = model;
      }
      
      

    例子

    1. 以下的虚拟listview是MVP架构的实现。

    View.h

    // View.h : interface of the CView class
    //
    /
    
    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Presenter;
    
    enum
    {
    	kMyButtonId = WM_USER+1,
    	kMyButtonId2,
    	kMyButtonId3,
    	kMyListViewId
    };
    
    class CView : public CWindowImpl<CView>
    {
    public:
    	DECLARE_WND_CLASS(NULL)
    
    	BOOL PreTranslateMessage(MSG* pMsg);
    
    	BEGIN_MSG_MAP_EX(CView)
    		MSG_WM_CREATE(OnCreate)
    		MESSAGE_HANDLER(WM_PAINT, OnPaint)
    		NOTIFY_HANDLER(kMyListViewId,NM_CLICK,OnNMClickListResult)
    		NOTIFY_HANDLER(kMyListViewId,LVN_GETDISPINFO,OnGetListViewData)
    		NOTIFY_HANDLER(kMyListViewId,LVN_ODCACHEHINT,OnPrepareListViewData)
    		NOTIFY_HANDLER(kMyListViewId,LVN_ODFINDITEM,OnFindListViewData)
    		COMMAND_RANGE_HANDLER_EX(kMyButtonId,kMyButtonId3,OnCommandIDHandlerEX)
    		REFLECT_NOTIFICATIONS()
    	END_MSG_MAP()
    
    	void setPresenter(shared_ptr<Presenter> presenter);
    
    protected:
    // Handler prototypes (uncomment arguments if needed):
    //	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    //	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    //	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
    	int OnCreate(LPCREATESTRUCT lpCreateStruct);
    	LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
    	void UpdateLayout();
    	LRESULT OnNMClickListResult(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
    	LRESULT OnGetListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
    	LRESULT OnPrepareListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
    	LRESULT OnFindListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
    	void OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl);
    
    	void ReloadMockData();
    	void ReloadListView();
    
    private:
    	std::wstring GetControlText(HWND hwnd,wchar_t* buf = NULL);
    	CListViewCtrl listview_;
    
    	CFont font_normal_;
    	CFont font_bold_;
    
    	CBrushHandle brush_white_;
    	CBrushHandle brush_hollow_;
    	CBrush brush_red_;
    
    	CButton buttonReloadMockData_;
    	CButton buttonReloadListView_;
    	CButton buttonDeleteListViewOneRow_;
    
    private:
    	shared_ptr<Presenter> presenter_ = nullptr;
    };
    
    

    View.cpp

    // View.cpp : implementation of the CView class
    //
    /
    
    #include "stdafx.h"
    #include "resource.h"
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include "View.h"
    #include 
    #include 
    #include 
    #include "Presenter.h"
    #include "Photo.h"
    
    
    using namespace std;
    
    BOOL CView::PreTranslateMessage(MSG* pMsg)
    {
    	return FALSE;
    }
    
    LRESULT CView::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
    	CPaintDC dc(m_hWnd);
    	CMemoryDC mdc(dc,dc.m_ps.rcPaint);
    
    	CRect rect_client;
    	GetClientRect(&rect_client);
    	mdc.FillSolidRect(rect_client,RGB(255,255,255));
    	//TODO: Add your drawing code here
    
    	return 0;
    }
    
    static HFONT GetFont(int pixel,bool bold,const wchar_t* font_name)
    {
    	LOGFONT lf; 
    	memset(&lf, 0, sizeof(LOGFONT)); // zero out structure 
    	lf.lfHeight = pixel; // request a 8-pixel-height font
    	if(bold)
    	{
    		lf.lfWeight = FW_BOLD;  
    	}
    	lstrcpy(lf.lfFaceName, font_name); // request a face name "Arial"
    	
    	HFONT font = ::CreateFontIndirect(&lf);
    	return font;
    }
    
    
    std::wstring CView::GetControlText(HWND hwnd,wchar_t* buf)
    {
    	auto length = ::GetWindowTextLength(hwnd);
    	bool bufNull = false;
    	if(!buf){
    		buf = new wchar_t[length+1]();
    		bufNull = true;
    	}
    	
    	::GetWindowText(hwnd,buf,length+1);
    	std::wstring str(buf);
    
    	if(bufNull)
    		delete []buf;
    
    	return str;
    }
    
    static std::wstring GetProductBinDir()
    {
    	static wchar_t szbuf[MAX_PATH];  
    	GetModuleFileName(NULL,szbuf,MAX_PATH);  
        PathRemoveFileSpec(szbuf);
    	int length = lstrlen(szbuf);
    	szbuf[length] = L'\\';
    	szbuf[length+1] = 0;
    	return std::wstring(szbuf);
    }
    
    LRESULT CView::OnGetListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
    {
    	NMLVDISPINFO* plvdi = (NMLVDISPINFO*) pnmh;
    	auto iItem = plvdi->item.iItem;
    	if (-1 == iItem)
    		return 0;
    	
    	auto count = presenter_->getPhotoCount();
    	if(count <= iItem)
    		return 0;
    
    	auto photo = presenter_->getPhoto(iItem);
    	if(plvdi->item.mask & LVIF_TEXT){
    		switch(plvdi->item.iSubItem)
    		{
    		case 0:
    			StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, to_wstring((int64_t)iItem+1).c_str());
    			break;
    		case 1:
    			StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, photo->name.c_str());
    			break;
    		case 2:
    			StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, photo->format.c_str());
    			break;
    		case 3:
    			StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, photo->createDate.c_str());
    			break;
    		}
    	}
    	
    	return 0;
    }
    
    LRESULT CView::OnPrepareListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
    {
    	return 0;
    }
    
    LRESULT CView::OnFindListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
    {	
    	return 0;
    }
    
    LRESULT CView::OnNMClickListResult(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
    {
    	return 0;
    }
    
    void CView::OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)
    {
    	switch(nID)
    	{
    	case kMyButtonId:
    		{
    			ReloadMockData();
    			MessageBox(L"刷新模拟数据完成");
    			break;
    		}
    	case kMyButtonId2:
    		{
    			ReloadListView();
    			MessageBox(L"重新加载表格数据完成");
    			break;
    		}
    	case kMyButtonId3:
    		{
    			int iItem = -1;
    			while((iItem = listview_.GetNextItem(iItem,LVNI_SELECTED)) != -1){
    				presenter_->removePhoto(iItem);
    				listview_.DeleteItem(iItem);
    				iItem--;
    			}
    
    			MessageBox(L"已删除");
    			break;
    		}
    	}
    }
    
    void CView::ReloadListView()
    {
    	listview_.SetItemCount(0);
    	presenter_->clearPhotos();
    }
    
    void CView::ReloadMockData()
    {
    	presenter_->loadPhotos([this]() {
    		listview_.SetItemCount(presenter_->getPhotoCount());
    	});
    	
    }
    
    int CView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	font_normal_ = ::GetFont(16,false,L"Arial");
    	font_bold_ = ::GetFont(16,true,L"Arial");
    	
    	brush_hollow_ = AtlGetStockBrush(HOLLOW_BRUSH);
    	brush_white_ = AtlGetStockBrush(WHITE_BRUSH);
    	brush_red_.CreateSolidBrush(RGB(255,0,0));
    
    	// 1.创建CListViewCtrl
    	listview_.Create(m_hWnd,0,NULL,WS_CHILD | WS_TABSTOP |WS_VISIBLE
    		|LVS_ALIGNLEFT|LVS_REPORT|LVS_SHOWSELALWAYS|WS_BORDER|LVS_OWNERDATA,0,kMyListViewId);
    	listview_.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_DOUBLEBUFFER);
    	listview_.SetFont(font_normal_);
    	auto header = listview_.GetHeader();
    	header.SetFont(font_bold_);
    	listview_.SetBkColor(RGB(255,255,255));
    
    	listview_.InsertColumn(0,L"No.",LVCFMT_LEFT,40);
    	listview_.InsertColumn(1,L"Name",LVCFMT_LEFT,100);
    	listview_.InsertColumn(2,L"Format",LVCFMT_LEFT,100);
    	listview_.InsertColumn(3,L"Create Date",LVCFMT_LEFT,100);
    
    	// 2.创建按钮
    	buttonReloadMockData_.Create(m_hWnd,0,L"加载新数据",WS_CHILD|WS_VISIBLE,0,kMyButtonId);
    	buttonReloadMockData_.SetFont(font_normal_);
    
    	buttonReloadListView_.Create(m_hWnd,0,L"刷新表格",WS_CHILD|WS_VISIBLE,0,kMyButtonId2);
    	buttonReloadListView_.SetFont(font_normal_);
    
    	buttonDeleteListViewOneRow_.Create(m_hWnd,0,L"删除选中行",WS_CHILD|WS_VISIBLE,0,kMyButtonId3);
    	buttonDeleteListViewOneRow_.SetFont(font_normal_);
    	
    
    	UpdateLayout();
    
    	return 0;
    }
    
    void CView::UpdateLayout()
    {
    	CRect rect;
    	GetClientRect(&rect);
    
    	CClientDC dc(m_hWnd);
    	dc.SelectFont(font_normal_);
    
    	CSize size_control(500,300);
    	CRect rect_control = CRect(CPoint(20,20),size_control);
    	listview_.MoveWindow(rect_control);
    
    	CSize size_button;
    	buttonReloadMockData_.GetIdealSize(&size_button);
    	rect_control = CRect(CPoint(rect_control.left,rect_control.bottom+10),size_button);
    	buttonReloadMockData_.MoveWindow(rect_control);
    
    	CSize sizeButton2;
    	buttonReloadListView_.GetIdealSize(&sizeButton2);
    	rect_control = CRect(CPoint(rect_control.right+10,rect_control.top),sizeButton2);
    	buttonReloadListView_.MoveWindow(rect_control);
    
    	CSize sizeButton3;
    	buttonDeleteListViewOneRow_.GetIdealSize(&sizeButton3);
    	rect_control = CRect(CPoint(rect_control.right+10,rect_control.top),sizeButton3);
    	buttonDeleteListViewOneRow_.MoveWindow(rect_control);
    }
    
    void CView::setPresenter(shared_ptr<Presenter> presenter)
    {
    	presenter_ = presenter;
    }
    

    Presenter.h

    
    #ifndef PRESENTER_H
    #define PRESENTER_H
    
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Photo;
    class Model;
    
    typedef function<void()> FuncSimple;
    
    class Presenter
    {
    
    public:
    	shared_ptr<Photo> getPhoto(int i);
    	void clearPhotos();
    	size_t getPhotoCount();
    	void removePhoto(int nItem);
    	void loadPhotos(FuncSimple func);
    	void setModel(shared_ptr<Model> model);
    
    protected:
    	vector<shared_ptr<Photo>> photos_;
    
    protected:
    	shared_ptr<Model> model_;
    	FuncSimple funcLoadFinish_;
    };
    
    
    #endif // !PRESENTER_H
    
    

    Presenter.cpp

    #include "stdafx.h"
    #include "Presenter.h"
    #include "Photo.h"
    #include "Model.h"
    #include "dispatch_queue.h"
    
    shared_ptr<Photo> Presenter::getPhoto(int i)
    {
    	return (i < photos_.size()) ? photos_[i] : nullptr;
    }
    
    void Presenter::clearPhotos()
    {
    	photos_.clear();
    }
    
    size_t Presenter::getPhotoCount()
    {
    	return photos_.size();
    }
    
    void Presenter::removePhoto(int nItem)
    {
    	if (nItem < getPhotoCount()) {
    		auto ite = photos_.begin() + nItem;
    		photos_.erase(ite);
    	}
    }
    
    void Presenter::loadPhotos(FuncSimple func)
    {
    	funcLoadFinish_ = func;
    	model_->loadPhoto([this](vector<shared_ptr<Photo>>* photos) {
    
    		// 需要把数据发送到主线程更新,这样才不会出现多线程访问共享数据冲突。
    		DispatchQueue::DispatchAsync(DispatchQueue::DispatchGetMainQueue(), 
    			new FuncSimple([this, photos]() {
    				photos_.insert(photos_.end(), photos->begin(), photos->end());
    				if (funcLoadFinish_)
    					funcLoadFinish_();
    
    				delete photos;
    		}));
    
    	});
    }
    
    void Presenter::setModel(shared_ptr<Model> model)
    {
    	model_ = model;
    }
    
    

    Model.h

    
    #ifndef MODEL_H
    #define MODEL_H
    
    #include 
    #include 
    #include 
    #include "Photo.h"
    
    using namespace std;
    
    typedef function<void(vector<shared_ptr<Photo>>*)> FuncLoadPhoto;
    
    class Model
    {
    public:
    	void loadPhoto(FuncLoadPhoto func);
    
    protected:
    	FuncLoadPhoto funcLoadPhoto_;
    };
    
    #endif
    
    

    Model.cpp

    #include "stdafx.h"
    #include "Model.h"
    #include 
    #include "Photo.h"
    
    using namespace std;
    
    void Model::loadPhoto(FuncLoadPhoto func)
    {
    	funcLoadPhoto_ = func;
    
    	// 模拟异步加载数据
    	thread t1([this]() {
    		wchar_t buf[MAX_PATH] = { 0 };
    		int index = 0;
    		
    		auto photos = new vector<shared_ptr<Photo>>();
    		for (int i = 0; i < 10000; ++i, ++index) {
    			auto photo = new Photo();
    			wsprintf(buf, L"Name-%d", index);
    			photo->name = buf;
    
    			wsprintf(buf, L"Format-%d", index);
    			photo->format = buf;
    
    			wsprintf(buf, L"createDate-%d", index);
    			photo->createDate = buf;
    
    			photos->push_back(move(shared_ptr<Photo>(photo)));
    		}
    
    		if (funcLoadPhoto_)
    			funcLoadPhoto_(photos);
    	});
    
    	t1.detach();
    }
    

    dispatch_queue.h

    #ifndef __DISPATCH_QUEUE_H
    #define __DISPATCH_QUEUE_H
    
    #include 
    #include 
    #include 
    
    enum
    {
        WMC_DISPATCH_MAIN_QUEUE = WM_USER+1000
    };
    
    typedef struct DispatchQueueObject1
    {
        DWORD threadId;
        HWND m_hwnd;
    	UINT msg;
    }DispatchQueueObject;
    
    class DispatchQueue
    {
    public:
    	static DWORD GetMainThreadId();
    	static bool IsCurrentMainThread();
    	static void DispatchQueueInit(HWND hwnd);
    	static DispatchQueueObject* DispatchGetMainQueue();
    	static void FreeDispatchMainQueue(DispatchQueueObject* dqo);
    	static void DispatchAsync(DispatchQueueObject* queue,std::function<void()>* callback);
    };
    
    
    
    #endif
    

    dispatch_queue.cpp

    #include "stdafx.h"
    
    #include "dispatch_queue.h"
    
    static HWND gMainHwnd = NULL;
    static DWORD gMainThreadId = 0;
    
    bool DispatchQueue::IsCurrentMainThread()
    {
    	return GetMainThreadId() == GetCurrentThreadId();
    }
    
    DWORD DispatchQueue::GetMainThreadId()
    {
    	return gMainThreadId;
    }
    
    void DispatchQueue::DispatchQueueInit(HWND hwnd)
    {
        gMainHwnd = hwnd;
    	gMainThreadId = GetCurrentThreadId();
    }
    
    void DispatchQueue::DispatchAsync(DispatchQueueObject* queue,std::function<void()>* callback)
    {
    	if(queue->threadId){
            ::PostThreadMessage(queue->threadId,queue->msg,(WPARAM)callback,0);
        }else{
    		::PostMessage(queue->m_hwnd,queue->msg,(WPARAM)callback,0);
        }
    	FreeDispatchMainQueue(queue);
    }
    
    DispatchQueueObject* DispatchQueue::DispatchGetMainQueue()
    {
        DispatchQueueObject* object = (DispatchQueueObject*)malloc(sizeof(DispatchQueueObject));
        memset(object,0,sizeof(DispatchQueueObject));
        object->m_hwnd = gMainHwnd;
    	object->msg = WMC_DISPATCH_MAIN_QUEUE;
        return object; 
    }
    
    void DispatchQueue::FreeDispatchMainQueue(DispatchQueueObject* dqo)
    {
    	free(dqo);
    }
    

    MainFrm.h

    // MainFrm.h : interface of the CMainFrame class
    //
    /
    
    #pragma once
    
    #include "View.h"
    #include "Presenter.h"
    #include "Model.h"
    #include "dispatch_queue.h"
    
    class CMainFrame : 
    	public CFrameWindowImpl<CMainFrame>, 
    	public CUpdateUI<CMainFrame>,
    	public CMessageFilter, public CIdleHandler
    {
    public:
    	DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
    
    	CView m_view;
    
    	virtual BOOL PreTranslateMessage(MSG* pMsg);
    	virtual BOOL OnIdle();
    
    	BEGIN_UPDATE_UI_MAP(CMainFrame)
    	END_UPDATE_UI_MAP()
    
    	BEGIN_MSG_MAP(CMainFrame)
    		MESSAGE_HANDLER(WM_CREATE, OnCreate)
    		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    		COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
    		COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
    		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
    		MESSAGE_HANDLER(WMC_DISPATCH_MAIN_QUEUE, OnDispatchMainQueueEvent)
    		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
    		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
    	END_MSG_MAP()
    
    // Handler prototypes (uncomment arguments if needed):
    //	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    //	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    //	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
    
    	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
    	LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
    	LRESULT OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
    	LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
    	LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
    
    	LRESULT OnDispatchMainQueueEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    	{
    		std::function<void()>* func = (std::function<void()>*)wParam;
    		(*func)();
    		delete func;
    		bHandled = TRUE;
    		return 0;
    	}
    };
    	
    

    MainFrm.cpp

    // MainFrm.cpp : implmentation of the CMainFrame class
    //
    /
    
    #include "stdafx.h"
    #include "resource.h"
    
    #include "aboutdlg.h"
    #include "MainFrm.h"
    
    BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
    {
    	if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
    		return TRUE;
    
    	return m_view.PreTranslateMessage(pMsg);
    }
    
    BOOL CMainFrame::OnIdle()
    {
    	return FALSE;
    }
    
    LRESULT CMainFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
    	// 注册线程接收异步主线程消息的窗口
    	DispatchQueue::DispatchQueueInit(m_hWnd);
    
    	auto presenter = make_shared<Presenter>();
    	auto model = make_shared<Model>();
    
    	presenter->setModel(model);
    	m_view.setPresenter(presenter);
    	m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);
    
    	// register object for message filtering and idle updates
    	CMessageLoop* pLoop = _Module.GetMessageLoop();
    	ATLASSERT(pLoop != NULL);
    	pLoop->AddMessageFilter(this);
    	pLoop->AddIdleHandler(this);
    
    	return 0;
    }
    
    LRESULT CMainFrame::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
    {
    	// unregister message filtering and idle updates
    	CMessageLoop* pLoop = _Module.GetMessageLoop();
    	ATLASSERT(pLoop != NULL);
    	pLoop->RemoveMessageFilter(this);
    	pLoop->RemoveIdleHandler(this);
    
    	bHandled = FALSE;
    	return 1;
    }
    
    LRESULT CMainFrame::OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    {
    	PostMessage(WM_CLOSE);
    	return 0;
    }
    
    LRESULT CMainFrame::OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    {
    	// TODO: add code to initialize document
    
    	return 0;
    }
    
    LRESULT CMainFrame::OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    {
    	CAboutDlg dlg;
    	dlg.DoModal();
    	return 0;
    }
    
    

    项目下载

    https://download.csdn.net/download/infoworld/89445554

    在这里插入图片描述
    注意: 关于WTL的开发学习可以订阅我的课程:
    使用WTL进行Windows桌面应用开发-第一部_在线视频教程-CSDN程序员研修院

    参考

    1. 观察者模式在项目中实际使用例子

    2. 观察者模式在项目中实际使用例子2

    3. QQ NT全新重构,探寻24岁QQ大重构背后的思考_跨端开发

  • 相关阅读:
    Redis 持久化
    Bootstrap-- 内容
    TF-IDF算法:从文本中发现价值的黄金法则
    24.面向对象六大原则
    安信可LoRaWAN网关 RG-02 接入TTN平台,控制多节点LoRaWAN模组。
    C++ 顺序表和单链表的二路归并思想(详解+示例代码)
    【LeetCode每日一题】——225.用队列实现栈
    vue中ElementUi的el-table表格绑定行点击事件
    Cocos2d-x 3D渲染技术 (二)
    Eyeshot 2022.3 Fem Released Crack
  • 原文地址:https://blog.csdn.net/infoworld/article/details/139737964