这一课是要在Duilib窗口上实现多区域绘图。前面高四第三课里面,已经介绍了CChart在Duilib里面的用法,其基本过程是在Duilib里面创建一个带句柄的窗口,然后把CChartWnd粘在上面。
在计划写本课之前,本以为在Duilib里面多区域绘图是易如反掌的事,因为每个创建的窗口都是独立的,但把CChartWnd粘上后,多个窗口之间还是有点问题。没办法,只好祭出了屠龙刀,解决了这个问题,不过代码就稍微要多一点了。
(笨笨注:上述描述已经过时,新版本CChart完美实现了窗口多次粘贴CChartWnd,因此用高四第三课的方法完全可以实现Duilib窗口多区域绘图,的确是易如反掌。)
基本过程还是和前面高四第三课一样的,但由于不使用CChartWnd了,在创建带句柄的窗口时,需要自己实现这些窗口的窗口函数,并注册窗口类。
下面直接给出源代码(只有一个cpp文件)和窗口布局XML文件,有关细节请参考高四第三课。
首先给出最终效果图。
从图可见,我们画了两个图,一个是折线图,一个是饼图。
窗口布局XML文件如下。
可以看到,与前面高四第三课的区别仅仅在于客户区部分,这里我们有两个标签Wnd1和Wnd2,也就是我们的两个绘图窗口。这两个标签需要在代码里面实现。
代码文件LessonA08.cpp全文如下。
#pragma once
#include "DuiLib\UIlib.h"
using namespace DuiLib;
#ifdef _DEBUG
# ifdef _UNICODE
# pragma comment(lib, "DuiLib\\DuiLib_ud.lib")
# else
# pragma comment(lib, "DuiLib\\DuiLib_d.lib")
# endif
#else
# ifdef _UNICODE
# pragma comment(lib, "DuiLib\\DuiLib_u.lib")
# else
# pragma comment(lib, "DuiLib\\DuiLib.lib")
# endif
#endif
#include "Chart.h"
#if defined(_UNICODE) || defined(UNICODE)
# pragma comment(lib,"CChartu.lib")
#else
# pragma comment(lib,"CChart.lib")
#endif
using namespace NsCChart;
class CWndUI: public CControlUI
{
public:
CWndUI(): m_hWnd(NULL){}
virtual void SetInternVisible(bool bVisible = true)
{
__super::SetInternVisible(bVisible);
::ShowWindow(m_hWnd, bVisible);
}
virtual void SetPos(RECT rc)
{
__super::SetPos(rc);
::SetWindowPos(m_hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
}
BOOL Attach(HWND hWndNew)
{
if (! ::IsWindow(hWndNew))
{
return FALSE;
}
m_hWnd = hWndNew;
return TRUE;
}
HWND Detach()
{
HWND hWnd = m_hWnd;
m_hWnd = NULL;
return hWnd;
}
protected:
HWND m_hWnd;
};
LRESULT CALLBACK ChartProc1(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
static CChart chart;
switch (message)
{
case WM_CREATE:
chart.SetType(kTypeXY);
chart.AddPoint2D(1, 1);
chart.AddPoint2D(2, 2);
chart.AddPoint2D(3, 2);
chart.AddPoint2D(4, 1);
break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_MOUSEMOVE:
case WM_CONTEXTMENU:
case WM_ERASEBKGND:
chart.Interactive(hWnd, message, wParam, lParam);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
chart.OnDraw(hWnd);
EndPaint(hWnd, &ps);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChartProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
static CChart chart;
switch (message)
{
case WM_CREATE:
chart.SetType(kTypePie);
chart.AddPie(2, _T("刘岩"));
chart.AddPie(2, _T("瞿颖"));
chart.AddPie(4, _T("周迅"));
chart.AddPie(8, _T("王菲"));
break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_MOUSEMOVE:
case WM_CONTEXTMENU:
case WM_ERASEBKGND:
chart.Interactive(hWnd, message, wParam, lParam);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
chart.OnDraw(hWnd);
EndPaint(hWnd, &ps);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
ATOM MyRegisterWnd(TCHAR *szClass, WNDPROC proc)
{
WNDCLASS wcls;
// check to see if class already registered
if (GetClassInfo(GetModuleHandle(NULL), szClass, &wcls))
{
return 1;// name already registered - ok if it was us
}
// Use standard "button" control as a template.
GetClassInfo(NULL, _T("button"), &wcls);
// set new values
wcls.style |= CS_DBLCLKS; // Make it to receive double clicks
wcls.lpfnWndProc = proc;
wcls.hInstance = GetModuleHandle(NULL);
wcls.lpszClassName = szClass;
return RegisterClass(&wcls);
}
class CDuiFrameWnd : public WindowImplBase
{
public:
virtual LPCTSTR GetWindowClassName() const { return _T("DUIMainFrame"); }
virtual CDuiString GetSkinFile() { return _T("duilib.xml"); }
virtual CDuiString GetSkinFolder() { return _T(""); }
virtual CControlUI* CreateControl(LPCTSTR pstrClassName)
{
if (_tcsicmp(pstrClassName, _T("Wnd1")) == 0)
{
CWndUI *pUI = new CWndUI;
HWND hWnd = CreateWindow(_T("mychart1"), _T("win32"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 0, 0, 0, m_PaintManager.GetPaintWindow(), NULL, NULL, NULL);
pUI->Attach(hWnd);
return pUI;
}
else if (_tcsicmp(pstrClassName, _T("Wnd2")) == 0)
{
CWndUI *pUI = new CWndUI;
HWND hWnd = CreateWindow(_T("mychart2"), _T("win32"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 0, 0, 0, m_PaintManager.GetPaintWindow(), NULL, NULL, NULL);
pUI->Attach(hWnd);
return pUI;
}
//return NULL;
return WindowImplBase::CreateControl(pstrClassName);
}
};
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
CPaintManagerUI::SetInstance(hInstance);
MyRegisterWnd(_T("mychart1"), ChartProc1);
MyRegisterWnd(_T("mychart2"), ChartProc2);
CDuiFrameWnd duiFrame;
duiFrame.Create(NULL, _T("DUIWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
duiFrame.CenterWindow();
duiFrame.ShowModal();
return 0;
}
基本结构和LessonA03.cpp是一样的,下面是不同点。
首先,这里我们编写了两个窗口函数ChartProc1和ChartProc2,分别对于图中的折线图和饼图,这实际就是笨笨CChartWnd的一个简化版。
其次,我们在WinMain函数里面注册了了两个窗口类,一个窗口类的类名为mychart1,窗口函数为ChartProc1,另一个窗口类的类名为mychart2,窗口函数为ChartProc2。为了简化注册,编写了一个MyRegisterWnd函数。
最后,在CreateControl函数里面,对标签Wnd1创建mychart1的窗口,对标签Wnd2创建mychart2的窗口。
本课就结束了。大家注意,请在笨笨的网站下载VS2010编译的版本,VC6的版本在unicode问题上可能和duilib配置不同,有点冲突。