2013/08/29

[VC]GDI+繪圖

上次寫了一個GDI+做漸層效果,今天要再用就忘了一些步驟,所以把整個流程寫上來。

Step1.在stdafx.h中加入GDI+的Header file

#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")

Step2.在CWinApp中加入成員變數

GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
在CWinApp::InitInstance()中初始化GDI+資源

GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

Step3.至此,你的程式即可開始使用GDI+囉~

Step4.程式結束前,需釋放GDI+資源,請在CWinApp::ExitInstance()中加入:

GdiplusShutdown(gdiplusToken);

以小弟需求來說--->漸層<---以下程式碼請參考:
/*****MyClass.h*****/
class CMyClass:public CWnd
{
DECLARE_DYNAMIC(CMyClass)
public:
 CMyClass();
 virtual ~CMyClass();  

protected:
 afx_msg void OnPaint();
 
private:
 Gdiplus::PointF  m_gdiPoint1,m_gdiPoint2;
 Gdiplus::Color  m_gdiColor1,m_gdiColor2;
};

/*****MyClass.cpp*****/
CMyClass::CMyClass()
{
 vm_gdiPoint1 =PointF(0,0);
 vm_gdiPoint2 =PointF(0,18);
 vm_gdiColor1 =Color(255,255,255);
 vm_gdiColor2 =Color(0,0,0);
}

void CMyClass::OnPaint()
{
 CPaintDC dc(this);
 CRect rcBar;
 GetClientRect(&rcBar);
 //GDI+
 Gdiplus::LinearGradientBrush  gdiBrush(m_gdiPoint1,m_gdiPoint2,m_gdiColor1,m_gdiColor2);
 Gdiplus::RectF gdiRc((Gdiplus::REAL)rcBar.left,(Gdiplus::REAL)rcBar.top, (Gdiplus::REAL)rcBar.Width(),(Gdiplus::REAL)rcBar.Height());
 Graphics grThis(dc.m_hDC);
 grThis.FillRectangle( &gdiBrush, gdiRc);
}

ref:CodeProject_GDI+
ref:MSDN_GDI+

2013/08/06

[VC] UNICODE 下的字串

從前寫好的ANSI CODE,在要編譯成UNICODE時,常會遇到字元bytes數問題,
往往都需在字串前強制加上L轉型成wchar;不過L到底是啥意思咧?

以下簡單介紹:

1. L"string"
表示將 ANSI字串轉換成unicode字串,就是每個英數字佔用兩個byte。

2. _T("string")
根據你的環境設置,編譯器會根據編譯目標環境選擇合適的(Unicode還是ANSI)字符處理方式。
如果你定義了UNICODE,那麼_T會把字符串前面加一個L。即此時的 _T("ABCD") 等同於 L"ABCD"。 
如果沒有定義 UNICODE,那麼_T不會在字符串前面加那個L,_T("ABCD") 就等價於 "ABCD"

3. TEXT("str")、_TEXT("str")、_T("str")
這三個MACRO都是一樣的意思,即交由編譯器自動判斷。
如下面三語句:   
TCHAR   szStr1[]   =   TEXT("str1");   
char   szStr2[]   =   "str2";   
WCHAR   szStr3[]   =   L("str3");   

當環境設置為UNICODE時,szStr1與str3所佔bytes數相等,
當環境未設置為UNICODE時,szStr1與str2所佔bytes數相等,

無論環境是否設置UNICODE,
str2永遠生成ANSI字串
str3永遠生成UNICODE字串

引用結論:為了程序的可移植性,建議盡量使用_T()表示字串。   

ref. http://www.cnblogs.com/txwsh1/archive/2008/03/06/1093335.html

2013/07/08

[MFC]CButton類如何響應WM_LBUTTONDOWN or WM_LBUTTONUP

建立一個button在dialog上的時候,他預設只有兩種事件可以響應,一是BN_CLICKED,另一則是BN_DOUBLECLICKED,那我要滑鼠左鍵按下與彈上的事件該怎麼做呢?

VC6是這樣做的:
1.在原Dialog下新增一CMyButton類別,其父類別為CButton。
2.為MyButton類別添加WM_LBUTTONDOWN與WM_LBUTTONUP的消息響應。
3.在Dialog下宣告一個MyButton的成員變數。
4.在MyButton.cpp->CMyButton::OnLButtonDown 內編輯你要滑鼠左鍵按下時,要做什麼事。

2013/07/04

[MFC] Tree Control Item 如何辨識唯一值

當我們在TreeCtrl上新增Item完後,若要在之後的操作中,判別此Item是否為唯一項目,目前只知道能用ItemText來判斷,但萬一ItemText非唯一值時,這樣的判斷方式就會有問題!
為了避免這樣的狀況發生,我在新增Item時,偷用一個欄位:lParam
先在.h定義一個與Item相關的其他訊息結構:
typedef struct _treeItemInfo;
{
long nId;
//也可以放入其他需要紀錄的資訊,如 char sTextEx[8];
} ITEMINFO, *PITEMINFO;

然後在.cpp內修改tree insert的部份:

void CMyDlg::InsertNewItem()
{
HTREEITEM hSubItem;//新增Item後會回傳Item handle
TV_INSERTSTRUCT tvis;
TV_ITEM tvi;//set item member data
tvi.mask= TVIF_TEXT;
tvi.pszText= "test";//顯示在Item上的文字
//set insert struct;
member data
tvis.item= tvi;//insert items into CTreeCtrl
tvis.hParent= NULL;//此為根結點,故父節點為NULL
tvis.hInsertAfter= TVI_ROOT;//此為根結點
hSubItem = m_tree.InsertItem(tvis);

//方法1
PITEMINFO l_pItemEx = new ITEMINFO;
l_pItemEx->nId = 1000;
strcpy(l_pItemEx->sTextEx,"MyInfo");
m_tree.SetItemData( hSubItem, l_pItemEx);//將序號結構地址與此節點綁在一起
//方法2
DWORD dItemId = 1000;//表示Item序號
m_tree.SetItemData( hSubItem, dItemId);//將序號與此節點綁在一起
}

當滑鼠拖曳某項Item時,會響應TVN_BEGINDRAG消息,這時從響應函式內將ID取出。
void CMyDlg::OnTvnBegindrag(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast LPNMTREEVIEW(pNMHDR);
*pResult = 0;
// TODO: 在此加入控制項告知處理常式程式碼

DWORD tmpId;
//方法1
HTREEITEM hDragItem = ((NM_TREEVIEW*)pNMHDR)->itemNew.hItem;
PITEMINFO l_pTmpItemEx = (PITEMINFO)m_tree.GetItemData(hDragItem);
tmpId = m_tree.GetItemData(hDragItem);
}

//方法2
TVITEM tvitem= ((NM_TREEVIEW*)pNMHDR)->itemNew;
tmpId = tvitem.lParam;
//取得ID後,作你想做的事吧!

ps.方法1&2選一個用即可。

參考:StackOverflow  how to create new Property for MFC(VC++) treeview Control?