天天看點

深入淺出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課基本一緻,但這裡仍然叙述得比較詳細,主要是因為本節課的内容是這個炫彩界面庫系列教程的基礎,為了友善大家參考,不用再不停地切換。