天天看点

深入浅出CChart 每日一课——快乐高四第五十七课 新的起点,炫彩界面库之老树新芽

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。如图所示。

深入浅出CChart 每日一课——快乐高四第五十七课 新的起点,炫彩界面库之老树新芽

在客户区首先添加一个布局元素控件,充满整个客户区。

然后再在这个控件里面添加两个布局元素控件。第一个控件高度方向充满父控件,宽度设置为100。第二个控件高度方向充满父控件,宽度设置为”:1”,即占用剩余空间。

在左边的layoutObject控件里放置一个按钮控件,内容属性修改为“信息按钮”,ID设置为ID_BUTTON_1。

在右边的layoutObject控件里放置一个基础元素控件,ID设置为ID_PICTURE_1,我们的图就准备画在这个控件上。

下面就是我们添加的所有控件之间的隶属关系。

深入浅出CChart 每日一课——快乐高四第五十七课 新的起点,炫彩界面库之老树新芽

与A52课的v2.1.1版界面设计器的结果相比,v3.3.5版设计器给出的设计结果主要有两点不同。

一是标题栏已有默认内容,不用自行处理。旧版设计器标题栏是空的,需要手动添加一个关闭按钮。

二是各元素的ID可以保存为字符串,不再是没有明显内涵的数字,这就方便多了。新版设计器的这个功能已经是免费的了。

深入浅出CChart 每日一课——快乐高四第五十七课 新的起点,炫彩界面库之老树新芽

最后是设计器给出的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="信息按钮&#10;" 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、编译。

第一次编译,发现一百多个错误。

深入浅出CChart 每日一课——快乐高四第五十七课 新的起点,炫彩界面库之老树新芽
深入浅出CChart 每日一课——快乐高四第五十七课 新的起点,炫彩界面库之老树新芽

什么,连个RECT结构都不认识?这个不是在windows.h中定义的吗?那肯定要加上这句话咯!

#include <windows.h>
           

注意,这句话要加在#include “xcgui.h”之前。

再编译,发现只有一个错误了!

深入浅出CChart 每日一课——快乐高四第五十七课 新的起点,炫彩界面库之老树新芽

这个嘛,我们直接把梦飞同学提供的xcgui.h文件中2287行的strcpy_s改成strcpy,编译就成功了!

5、把设计器设计的工程,连同文件夹一起拷贝到LessonA57里面。

在Init()函数里正确设置布局文件的路径。

XC_LoadResource(L".\\Simple\\resource.res");  //加载资源文件
        HXCGUI hXCGUI=XC_LoadLayout(L".\\Simple\\main.xml"); //加载布局文件
           

现在再编译,运行程序如图。

深入浅出CChart 每日一课——快乐高四第五十七课 新的起点,炫彩界面库之老树新芽

程序基本成型,下面添加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")
#endif

using 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取得资源句柄。

程序已经可以运行。

深入浅出CChart 每日一课——快乐高四第五十七课 新的起点,炫彩界面库之老树新芽

 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课基本一致,但这里仍然叙述得比较详细,主要是因为本节课的内容是这个炫彩界面库系列教程的基础,为了方便大家参考,不用再不停地切换。