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")
#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取得資源句柄。
程式已經可以運作。
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課基本一緻,但這裡仍然叙述得比較詳細,主要是因為本節課的内容是這個炫彩界面庫系列教程的基礎,為了友善大家參考,不用再不停地切換。