CChart对多种DirectUI库具有良好的适配性,炫彩界面库也不例外。前面已经有两篇关于炫彩界面库的课程,分别是第17课“深入浅出CChart 每日一课——第十七课 时尚加潮流,DirectUI之炫彩界面库_baita96的博客-CSDN博客”和A52课“深入浅出CChart 每日一课——快乐高四第五十二课 旧梦重温,天上人间之炫彩界面库_baita96的博客-CSDN博客_炫彩界面库”。
其中第17课讲述的是在炫彩主窗口绘图的方法,A52课讲述的是在炫彩界面元素上绘图的方法。第17课没有使用UI设计器,A52课使用了一个较老版本v2.1.1的UI设计器。这两课针对的炫彩版本都比较古老,其内容只能作为参考,在最新的版本中不一定能正常编译和运行。
截至目前为止,炫彩界面库和炫彩设计器(IDE)的最新版本为v3.3.5。
在炫彩界面库作者梦飞同学的邀请下,笨笨计划对炫彩界面库中CChart的使用发布一个系列教程。
本节课作为系列教程的第一课,计划对A52课的内容进行改进,使之能在最新版本的炫彩界面库中运行。
A57.1 界面布局文件
仿照A52课,我们利用最新版的UI设计器做一个完全一样的界面布局,工程名仍为Simple。如图所示。
在客户区首先添加一个布局元素控件,充满整个客户区。
然后再在这个控件里面添加两个布局元素控件。第一个控件高度方向充满父控件,宽度设置为100。第二个控件高度方向充满父控件,宽度设置为”:1”,即占用剩余空间。
在左边的layoutObject控件里放置一个按钮控件,内容属性修改为“信息按钮”,ID设置为ID_BUTTON_1。
在右边的layoutObject控件里放置一个基础元素控件,ID设置为ID_PICTURE_1,我们的图就准备画在这个控件上。
下面就是我们添加的所有控件之间的隶属关系。
与A52课的v2.1.1版界面设计器的结果相比,v3.3.5版设计器给出的设计结果主要有两点不同。
一是标题栏已有默认内容,不用自行处理。旧版设计器标题栏是空的,需要手动添加一个关闭按钮。
二是各元素的ID可以保存为字符串,不再是没有明显内涵的数字,这就方便多了。新版设计器的这个功能已经是免费的了。
最后是设计器给出的xml文件。
<?xml version="1.0" encoding="UTF-8"?>
<!--炫彩界面库-窗口布局文件-->
<head><bindJsFile value="" />
</head>
<windowUI center="true" content="炫彩界面库 - 我的窗口名" rect="20,20,709,537" showT="true" windowStyle="2031"><layoutEleUI layout.height="fill" layout.width="fill" rect="82,30,693,499" showT="true" expandT="true"><layoutEleUI layout.height="fill" layout.width="150" rect="0,0,100,100" showT="true" expandT="true"><buttonUI content="信息按钮 " id="@ID_BUTTON_1" rect="270,89,60,25" showT="true" expandT="true" /></layoutEleUI><layoutEleUI layout.height="fill" layout.width=":1" rect="240,87,100,100" showT="true" expandT="true"><elementUI id="@ID_PICTURE_1" layout.height="fill" layout.width="fill" rect="74,29,100,100" showT="true" expandT="true" /></layoutEleUI></layoutEleUI>
</windowUI>
这个文件用设计器实现很简单,用手撸的话,那就比较困难了。
A57.2 代码编写
A52课采用的是VS2010编写代码,其实炫彩界面库的兼容性非常好,还支持古老的VC6。
由于VC6的操作相对简单,本次炫彩界面库的系列教程就采用VC6进行介绍。对于高版本VS,也是完全没有问题的。
1、首先用VC6建立一个名为LessonA57的Win32 Application空工程,注意是空工程。
2、新建一个cpp文件加入工程,其内容是炫彩说明书中以cpp方式加载界面布局文件的模板,和A52课完全一样。如下:
#pragma comment(lib, "XCGUI.lib")
#include "xcgui.h"class CMyWindowDemo
{
public:HWINDOW m_hWindow;CMyWindowDemo(){Init();}void Init(){XC_LoadResource(L".\\resource.res"); //加载资源文件HXCGUI hXCGUI=XC_LoadLayout(L".\\layout.xml"); //加载布局文件if(XC_IsHWINDOW(hXCGUI)){m_hWindow=(HWINDOW)hXCGUI;//XC_GetObjectByID(m_hWindow, 100); //获取指定ID对象//XC_GetObjectByName(L"name") //获取指定name对象//XC_ShowLayoutFrame(TRUE); //显示布局边界XWnd_AdjustLayout(m_hWindow);XWnd_ShowWindow(m_hWindow,SW_SHOW);}else{//错误}
};int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{XInitXCGUI(FALSE);CMyWindowDemo MyWindow;XRunXCGUI();XExitXCGUI();return 0;
}
注意到WinMain函数里面第一行,XInitXCGUI(FALSE);
这个函数的参数表示炫彩是否以Direct2D作为渲染引擎。这里我们暂时不考虑Direct2D ,故参数设置为FALSE。
3、将炫彩的三个库文件xcgui.h、XCGUI.lib、XCGUI.dll拷贝到LessonA57文件夹中。
4、编译。
第一次编译,发现一百多个错误。
什么,连个RECT结构都不认识?这个不是在windows.h中定义的吗?那肯定要加上这句话咯!
#include <windows.h>
注意,这句话要加在#include “xcgui.h”之前。
再编译,发现只有一个错误了!
这个嘛,我们直接把梦飞同学提供的xcgui.h文件中2287行的strcpy_s改成strcpy,编译就成功了!
5、把设计器设计的工程,连同文件夹一起拷贝到LessonA57里面。
在Init()函数里正确设置布局文件的路径。
XC_LoadResource(L".\\Simple\\resource.res"); //加载资源文件HXCGUI hXCGUI=XC_LoadLayout(L".\\Simple\\main.xml"); //加载布局文件
现在再编译,运行程序如图。
程序基本成型,下面添加CChart的支持,其过程和A52课完全一样。
6、把CChart的库文件拷贝到LessonA57文件夹,在cpp的开始加上CChart的支持,以及数学库的支持。
#include "Chart.h"
#if defined(_UNICODE) || defined(UNICODE)
# pragma comment(lib,"CChartu.lib")
#else
# pragma comment(lib,"CChart.lib")
#endifusing namespace NsCChart;#include <math.h>
7、在HWINDOW m_hWindow;这一行下面添加一个CChart绘图变量。
CChart m_Chart;
8、在Init()函数里初始化绘图变量。
double pi=4.0*atan(1.0);for(int i=0; i<720; ++i){m_Chart.AddPoint2D(i, 1.3*sin(i*2.0*pi/360));}m_Chart.SetTitle(_T("炫彩界面库"));
9、编写绘图函数并注册。
绘图函数如下,注意它是CMyWindowDemo类的成员函数。
int OnPanelDraw(HDRAW hDraw,BOOL *pbHandled){RECT rect;HELE hPanel = (HELE)XC_GetObjectByIDName(m_hWindow, L"ID_PICTURE_1");XEle_GetClientRect(hPanel, &rect);HDC hDC = XDraw_GetHDC(hDraw);XEle_DrawEle(hPanel, hDraw);m_Chart.OnDraw(hDC, rect);*pbHandled=TRUE;return 0;}
在Init()中注册这个绘图函数。
HELE hPanel = (HELE)XC_GetObjectByIDName(m_hWindow, L"ID_PICTURE_1");XEle_RegEventCPP(hPanel, XE_PAINT, &CMyWindowDemo::OnPanelDraw);
注意到这里与A52课不同的地方在于,我们把资源号保存在字符串里面,这里用XC_GetObjectByIDName取得资源句柄。
程序已经可以运行。
10、编写消息响应函数并注册
所有消息响应函数如下:
int OnEleLButtonDown(UINT nFlags,POINT *pPt,BOOL *pbHandled){HWND hWnd = XWnd_GetHWND(m_hWindow);if(m_Chart.OnLButtonDown(hWnd, *pPt, 0)){XWnd_Redraw(m_hWindow);}*pbHandled = FALSE;return 0;}int OnEleLButtonUp(UINT flags,POINT *pPt,BOOL *pbHandled){HWND hWnd = XWnd_GetHWND(m_hWindow);if(m_Chart.OnLButtonUp(hWnd, *pPt, 0)){XWnd_Redraw(m_hWindow);}*pbHandled = FALSE;return 0;}int OnEleLButtonDblClk(UINT nFlags,POINT *pPt, BOOL *pbHandled){HWND hWnd = XWnd_GetHWND(m_hWindow);if(m_Chart.OnLButtonDblClk(hWnd, *pPt, 0)){XWnd_Redraw(m_hWindow);}*pbHandled = FALSE;return 0;}int OnEleRButtonDown(UINT nFlags,POINT *pPt, BOOL *pbHandled){HELE hPanel = (HELE)XC_GetObjectByID(m_hWindow, 151);XEle_PointClientToWndClient(hPanel, pPt);HWND hWnd = XWnd_GetHWND(m_hWindow);ClientToScreen(hWnd, pPt);if(m_Chart.OnContextMenu(NULL, hWnd, *pPt)){XWnd_Redraw(m_hWindow);}*pbHandled = FALSE;return 0;}int OnEleMouseMove(UINT nFlags,POINT *pPt, BOOL *pbHandled){HWND hWnd = XWnd_GetHWND(m_hWindow);if(m_Chart.OnMouseMove(hWnd, *pPt, 0)){XWnd_Redraw(m_hWindow);}*pbHandled = FALSE;return 0;}int OnEleKeyDown(WPARAM wParam,LPARAM lParam,BOOL *pbHandled){HWND hWnd = XWnd_GetHWND(m_hWindow);UINT key = (UINT)wParam;if(m_Chart.OnKeyDown(hWnd, key)){XWnd_Redraw(m_hWindow);}*pbHandled = FALSE;return 0;}
注意到由于版本升级,A52课中的接口XWnd_RedrawWnd改变为XWnd_Redraw。
同时,这里比A52课多编写了一个键盘消息响应函数OnEleKeyDown。
注册代码如下,放置在上一条注册代码之后。
XEle_RegEventCPP(hPanel, XE_LBUTTONDOWN, &CMyWindowDemo::OnEleLButtonDown);XEle_RegEventCPP(hPanel, XE_LBUTTONUP, &CMyWindowDemo::OnEleLButtonUp);XEle_RegEventCPP(hPanel, XE_LBUTTONDBCLICK, &CMyWindowDemo::OnEleLButtonDblClk);XEle_RegEventCPP(hPanel, XE_RBUTTONDOWN, &CMyWindowDemo::OnEleRButtonDown);XEle_RegEventCPP(hPanel, XE_MOUSEMOVE, &CMyWindowDemo::OnEleMouseMove);XEle_RegEventCPP(hPanel, XE_KEYDOWN, &CMyWindowDemo::OnEleKeyDown);
好了,到此为止,已经完成了代码编写(界面左边那个按钮就不管了)。
11、DirectUI修正。
这时候实际上还有一个小问题。如果你试着拖动图例,你会发现图例位置似乎不能按你的想法移动。
造成这个问题的原因,在于炫彩是DirectUI库,只有一个主窗口句柄。而CChart是基于句柄的,在CChart的消息响应中,会把绘图元素的父窗口认成炫彩的全局主窗口,而实际上其父窗口只是其上级元素。这就造成了坐标错位。
CChart库已经对这个问题进行了处理,只需要加入如下代码即可修正这个问题。
RECT rect;XEle_GetWndClientRect(hPanel, &rect);m_Chart.SetAdditionalCanvasOffset(rect.left, rect.top);m_Chart.SetDuiRepair(true);
注意到这段代码的位置有要求,必须把它放置在主窗口显示代码XWnd_ShowWindow(m_hWindow,SW_SHOW);这一行之后才能正确发挥作用。原因在于必须在窗口显示以后,XEle_GetWndClientRect才能获取到正确的坐标。
打完收工!
本课的内容和A52课基本一致,但这里仍然叙述得比较详细,主要是因为本节课的内容是这个炫彩界面库系列教程的基础,为了方便大家参考,不用再不停地切换。