星期四, 八月 31, 2006
CListCtrl(2)
定义可以排序的列表类
只需要多定义两个变量
class SortCListCtrl : public CListCtrl
{
// Construction
public:
SortCListCtrl();
// Attributes
public:
BOOL m_fAsc;//是否顺序排序
int m_nSortedCol;//当前排序的列
....
}
在使用可以排序列表时 实例化自己的变量
SortCListCtrl m_yktlist;
//响应点击列函数
void CAuditingCertView::
OnColumnclickListYkt(NMHDR* pNMHDR, LRESULT* pResult)
{
for (int i = 0; i < m_yktlist.GetItemCount(); i)
{
m_yktlist.SetItemData(i, i);//供排序使用的item编号
}
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
//设置排序方式
if( pNMListView->iSubItem ==m_yktlist.m_nSortedCol )
m_yktlist.m_fAsc = !m_yktlist.m_fAsc;
else
{
m_yktlist.m_fAsc = TRUE;
m_yktlist.m_nSortedCol = pNMListView->iSubItem;
}
//调用排序函数,此函数为CListCtrl定义好的,但是需要调
用我们定义的函数才比较任意两个项目的值
m_yktlist.SortItems(MyListCompare, (LPARAM)&m_yktlist);
for ( i = 0; i < m_yktlist.GetItemCount(); i){
m_yktlist.SetItemData(i, i);//供排序使用的item编号
CString s;
s.Format("%d",i 1);//编号
m_yktlist.SetItemText(i,0,s);
}
*pResult = 0;
}
///全局函数,比较两个项目的依据
int CALLBACK MyListCompare(LPARAM lParam1,
LPARAM lParam2, LPARAM lParamSort)
{
//通过传递的参数来得到CSortList对象指针,从而得到排序方式
SortCListCtrl * pV=(SortCListCtrl *)lParamSort;
//通过ItemData来确定数据
CString szComp1,szComp2;
int iCompRes;
szComp1=pV->GetItemText(lParam1,pV->m_nSortedCol);
szComp2=pV->GetItemText(lParam2,pV->m_nSortedCol);
switch(pV->m_nSortedCol)
{
case(0):
//以第一列为根据排序 编号
iCompRes=atof(szComp1)<=atof(szComp2)?-1:1;
break;
case(4):
//以第5列为根据排序 总次数
iCompRes=atof(szComp1)<=atof(szComp2)?-1:1;
break;
default:
iCompRes=szComp1.Compare(szComp2);
break;
}
//根据当前的排序方式进行调整
if(pV->m_fAsc)
return iCompRes;
else
return -iCompRes;
}
导出数据为excel文件
使用ODBC将数据输出到excel数据区
void ExportAsExcel(CString filename,
CListCtrl &resultlist,CWnd * wnd)
{
CDatabase database;
CString sDriver = "MICROSOFT EXCEL DRIVER (*.XLS)";
// Excel安装驱动
CString sSql,sExcelFile;
//弹出对话框选择路径
CFileDialog fileDlg (FALSE, "Path", filename,
OFN_FILEMUSTEXIST OFN_HIDEREADONLY, "*.xls",wnd);
if( fileDlg.DoModal()==IDOK)
{
sExcelFile = fileDlg.GetPathName(); // 要建立的Excel文件
CFileFind finder;
BOOL bWorking = finder.FindFile(sExcelFile);//寻找文件
if (bWorking)//如果已经存在文件,则删除
{
CFile::Remove((LPCTSTR)sExcelFile);
}
}
else return;
TRY
{
// 创建进行存取的字符串
sSql.Format("DRIVER={%s};
DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;
CREATE_DB=\"%s\";DBQ=%s",sDriver, sExcelFile, sExcelFile);
// 创建数据库 (既Excel表格文件) if( database.OpenEx(sSql,
CDatabase::noOdbcDialog) )
{
CHeaderCtrl* pHeader = resultlist.GetHeaderCtrl();
//获得行,列的个数
int nColCount = pHeader->GetItemCount();
int nLineCount = resultlist.GetItemCount();
int ColOrderArray[100];
CString ca[100];
resultlist.GetColumnOrderArray(ColOrderArray, nColCount);
//检索各列的信息,确定列标题的内容
for(int i =0 ; i< nColCount; i )
{
LVCOLUMN lvc;
char text[100];
lvc.mask = LVCF_TEXTLVCF_SUBITEM;
lvc.pszText = text;
lvc.cchTextMax = 100;
resultlist.GetColumn(ColOrderArray[i], &lvc);
ca[i] = lvc.pszText;
}
// 创建表结构
CString tempsql="(";
for(i =0 ; i< nColCount-1; i )
{
tempsql =ca[i];
tempsql =" TEXT,";
}
tempsql =ca[nColCount-1];
tempsql =" TEXT)";
sSql = "CREATE TABLE Sheet1 ";
sSql =tempsql;
database.ExecuteSQL(sSql);
//插入数据
int item_count=resultlist.GetItemCount();
tempsql="(";
for(i =0 ; i< nColCount-1; i )
{
tempsql =ca[i];
tempsql =" ,";
}
tempsql =ca[nColCount-1];
tempsql =")";
for(int itemnum=0;itemnum<item_count;itemnum ){
sSql="";
sSql ="INSERT INTO Sheet1 ";
sSql =tempsql;
sSql ="VALUES ('";
for(i =0 ; i< nColCount-1; i )
{
sSql =resultlist.GetItemText(itemnum, i);
sSql ="','";
}
sSql =resultlist.GetItemText(itemnum, nColCount-1);
sSql ="')";
database.ExecuteSQL(sSql);
}
}
// 关闭数据库
database.Close();
AfxMessageBox("Excel文件写入成功!");
}
CATCH_ALL(e)
{
TRACE1("Excel驱动没有安装: %s",sDriver);
}
END_CATCH_ALL;
}
CListCtrl 使用心得
初始化:
DWORD dwStyle;
dwStyle = m_bzlist.GetStyle();
dwStyle = LVS_EX_GRIDLINES LVS_EX_FULLROWSELECTLVS_SHOWSELALWAYS ;
m_bzlist.SetExtendedStyle(dwStyle);
m_bzlist.SetBkColor(RGB(0xec,0xf1,0xfd));
m_bzlist.SetTextBkColor(RGB(0xfe,0xFF,0xc6));
插入一列:
m_bzlist.InsertColumn(0,"编号");
m_bzlist.SetColumnWidth(0,50);
插入一行: 方法1:
LV_ITEM lvitem; lvitem.pszText="";
lvitem.mask=LVIF_TEXT;
lvitem.iSubItem=0;
lvitem.iItem=0;
m_jbxxlist.InsertItem(&lvitem);
m_jbxxlist.SetItemText(0,0,xh);
m_jbxxlist.SetItemText(0,1,xm);
m_jbxxlist.SetItemText(0,2,nj);
方法2:
m_yktlist.InsertItem(i,"2");
m_yktlist.SetItemText(i,0,s);
m_yktlist.SetItemText(i,1,xh);
m_yktlist.SetItemText(i,2,xm);
读取数据:
resultlist.GetItemText(行数, 列数);
每行前有复选框的列表:
初始化时使用LVS_EX_CHECKBOXES属性
DWORD dwStyle;
dwStyle = m_yktlist.GetStyle();
dwStyle = LVS_EX_GRIDLINES LVS_EX_FULLROWSELECTLVS_EX_CHECKBOXES ;
m_yktlist.SetExtendedStyle(dwStyle);
设置选中:
m_yktlist.SetItemState (行数,LVIS_SELECTED, LVIS_SELECTED);//设为选中状态
m_yktlist.SetCheck(行数,true/false);
判断是否选中:
m_yktlist.GetItemState(行数,LVIS_SELECTED)==LVIS_SELECTED//选中
m_yktlist.GetCheck(行数)
选中当前选中的 Item :
POSITION pos = mListDvdInfo.GetFirstSelectedItemPosition();
while(pos != NULL){ int nIndex = mListDvdInfo.GetNextSelectedItem(pos); } Item
改变的消息相应:
LVN_ITEMCHANGED LVN_ITEMCHANGING
在ListCtrl中进行排序
列表控件(CListCtrl)的顶部有一排按钮,用户可以通过选择不同的列来对记录进行排序。但是 CListCtrl并没有自动排序的功能,我们需要自己添加一个用于排序的回调函数来比较两个数据的大小,此外还需要响应排序按钮被点击的消息。下面讲述一下具体的做法。
CListCtrl提供了用于排序的函数,函数原型为:BOOL CListCtrl::SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData )。其中第一个参数为全局排序函数的地址,第二个参数为用户数据,你可以根据你的需要传递一个数据或是指针。该函数返回-1代表第一项排应在第二项前面,返回1代表第一项排应在第二项后面,返回0代表两项相等。
用于排序的函数原形为:int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort),其中第三个参数为调用者传递的数据(即调用SortItems时的第二个参数dwData)。第一和第二个参数为用于比较的两项的ItemData,你可以通过DWORD CListCtrl::GetItemData( int nItem )/BOOL CListCtrl::SetItemData( int nItem, DWORD dwData )来对每一项的ItemData进行存取。在添加项时选用特定的CListCtrl::InsertItem也可以设置该值。由于你在排序时只能通过该值来确定项的位置所以你应该比较明确的确定该值的含义。
最后一点,我们需要知道什么时候需要排序,实现这点可以在父窗口中对LVN_COLUMNCLICK消息进行处理来实现。
下面我们看一个例子,这个例子是一个派生类,并支持顺序/倒序两种方式排序。为了简单我对全局数据进行排序,而在实际应用中会有多组需要排序的数据,所以需要通过传递参数的方式来告诉派序函数需要对什么数据进行排序。
//全局数据
struct DEMO_DATA
{
char szName[20];
int iAge;
}strAllData[5]={{"王某",30},{"张某",40},{"武某",32},{"陈某",20},{"李某",36}};
//CListCtrl派生类定义
class CSortList : public CListCtrl
{
// Construction
public:
CSortList();
BOOL m_fAsc;//是否顺序排序
int m_nSortedCol;//当前排序的列
protected:
//{{AFX_MSG(CSortList)
//}}AFX_MSG
...
};
//父窗口中包含该CListCtrl派生类对象
class CSort_in_list_ctrlDlg : public CDialog
{
// Construction
public:
CSort_in_list_ctrlDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CSort_in_list_ctrlDlg)
enum { IDD = IDD_SORT_IN_LIST_CTRL_DIALOG };
CSortList m_listTest;
//}}AFX_DATA
}
//在父窗口中定义LVN_COLUMNCLICK消息映射
BEGIN_MESSAGE_MAP(CSort_in_list_ctrlDlg, CDialog)
//{{AFX_MSG_MAP(CSort_in_list_ctrlDlg)
ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, OnColumnclickList1)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//初始化数据
BOOL CSort_in_list_ctrlDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//初始化ListCtrl中数据列表
m_listTest.InsertColumn(0,"姓名");
m_listTest.InsertColumn(1,"年龄");
m_listTest.SetColumnWidth(0,80);
m_listTest.SetColumnWidth(1,80);
for(int i=0;i<5;i++)
{
m_listTest.InsertItem(i,strAllData[i].szName);
char szAge[10];
sprintf(szAge,"%d",strAllData[i].iAge);
m_listTest.SetItemText(i,1,szAge);
//设置每项的ItemData为数组中数据的索引
//在排序函数中通过该ItemData来确定数据
m_listTest.SetItemData(i,i);
}
return TRUE; // return TRUE unless you set the focus to a control
}
//处理消息
void CSort_in_list_ctrlDlg::OnColumnclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
//设置排序方式
if( pNMListView->iSubItem == m_listTest.m_nSortedCol )
m_listTest.m_fAsc = !m_listTest.m_fAsc;
else
{
m_listTest.m_fAsc = TRUE;
m_listTest.m_nSortedCol = pNMListView->iSubItem;
}
//调用排序函数
m_listTest.SortItems( ListCompare, (DWORD)&m_listTest );
*pResult = 0;
}
//排序函数实现
int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
//通过传递的参数来得到CSortList对象指针,从而得到排序方式
CSortList* pV=(CSortList*)lParamSort;
//通过ItemData来确定数据
DEMO_DATA* pInfo1=strAllData+lParam1;
DEMO_DATA* pInfo2=strAllData+lParam2;
CString szComp1,szComp2;
int iCompRes;
switch(pV->m_nSortedCol)
{
case(0):
//以第一列为根据排序
szComp1=pInfo1->szName;
szComp2=pInfo2->szName;
iCompRes=szComp1.Compare(szComp2);
break;
case(1):
//以第二列为根据排序
if(pInfo1->iAge == pInfo2->iAge)
iCompRes = 0;
else
iCompRes=(pInfo1->iAge < pInfo2->iAge)?-1:1;
break;
default:
ASSERT(0);
break;
}
//根据当前的排序方式进行调整
if(pV->m_fAsc)
return iCompRes;
else
return iCompRes*-1;
}
排序最快:
CListCtrl::SortItems
Example
// Sort the item in reverse alphabetical order.
static int CALLBACK
MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
// lParamSort contains a pointer to the list view control.
// The lParam of an item is just its index.
CListCtrl* pListCtrl = (CListCtrl*) lParamSort;
CString strItem1 = pListCtrl->GetItemText(lParam1, 0);
CString strItem2 = pListCtrl->GetItemText(lParam2, 0);
return strcmp(strItem2, strItem1);
}
void snip_CListCtrl_SortItems()
{
// The pointer to my list view control.
extern CListCtrl* pmyListCtrl;
// Sort the list view items using my callback procedure.
pmyListCtrl->SortItems(MyCompareProc, (LPARAM) pmyListCtrl);
}
星期三, 八月 30, 2006
ビットマップボタン
ビットマップボタンは、ビットマップ画像からボタンの外観を作成できるボタンコントロールです。 WTLはビットマップボタンを作成するために
CBitmapButton
というクラスを用意しています。 MFCにも同名のクラスがありますが、それとは使用方法が異なります。CBitmapButton
クラスはCButton
クラスから派生しています。以下に示すのは、CBitmapButton
クラスを使用する例です。下の図のような白いボタンを作成します。
// stdafx.h内 #include <atlbase.h> #include <atlapp.h> extern CAppModule _Module; #include <atlwin.h> #include <atlcrack.h> #include <atlmisc.h> #include <atlctrls.h> #include <atlctrlx.h> // CBitmapButtonクラスを使用するため |
// maindlg.h内 class CMainDlg : public CDialogImpl<CMainDlg> { public: enum { IDD = IDD_MAINDLG }; CBitmapButton m_button_bmp; // ビットマップボタン // メッセージマップ BEGIN_MSG_MAP_EX(CMainDlg) MSG_WM_INITDIALOG(OnInitDialog) COMMAND_ID_HANDLER_EX(IDC_BUTTON_BMP, OnButtonBmp) COMMAND_ID_HANDLER_EX(IDOK, OnOK) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) END_MSG_MAP() LRESULT OnInitDialog(HWND hWnd, LPARAM lParam){ // スクリーンの中央に配置 CenterWindow(); // 大きいアイコン設定 HICON hIcon = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON)); SetIcon(hIcon, TRUE); // 小さいアイコン設定 HICON hIconSmall = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON)); SetIcon(hIconSmall, FALSE); // コントロール設定 CImageList il; il.CreateFromImage(IDB_BITMAP_BUTTON, 64, 1, CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION); m_button_bmp.SubclassWindow(GetDlgItem(IDC_BUTTON_BMP)); m_button_bmp.SetImageList(il); m_button_bmp.SetImages(0, 1, 2, 3); m_button_bmp.SetToolTipText(_T("ビットマップボタン")); m_button_bmp.CenterWindow(); return TRUE; } void OnButtonBmp(UINT uNotifyCode, int nID, HWND hWndCtl){ MessageBox(_T("これはビットマップボタンです。")); } void OnOK(UINT uNotifyCode, int nID, HWND hWndCtl){ EndDialog(nID); } void OnCancel(UINT uNotifyCode, int nID, HWND hWndCtl){ EndDialog(nID); } }; |
// Control.cpp内 #include "stdafx.h" #include "resource.h" #include "maindlg.h" CAppModule _Module; int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow) { HRESULT hRes = ::CoInitialize(NULL); ATLASSERT(SUCCEEDED(hRes)); ::DefWindowProc(NULL, 0, 0, 0L); AtlInitCommonControls(ICC_COOL_CLASSES ICC_WIN95_CLASSES); hRes = _Module.Init(NULL, hInstance); ATLASSERT(SUCCEEDED(hRes)); int nRet = 0; // BLOCK: アプリケーション実行 { CMainDlg dlgMain; nRet = dlgMain.DoModal(); } _Module.Term(); ::CoUninitialize(); return nRet; } |
まず、リソースを作成します。ダイアログにボタンコントロールを配置し、リソースIDを次のように指定します。
コントロール名 | リソースID |
プッシュボタン | IDC_BUTTON_BMP |
また、プロジェクトに次のようなビットマップリソースを
IDB_BITMAP_BUTTON
というIDで追加します。
このビットマップは、ボタンの4つの状態(通常の状態、押された状態、フォーカスが当たっている状態、使用不可の状態)を表す一つの画像です。
次に、stdafx.h内では、
CBitmapButton
クラスを使用するためにatlctrls.hヘッダとatlctrlx.hヘッダをインクルードします。なお、atlctrls.hヘッダを先にインクルードする必要があります。CMainDlg
クラスでは、まず、 CBitmapButton
クラスのインスタンスをメンバ変数として宣言します。これを使うためには、WM_INITDIALOG
メッセージハンドラでサブクラス化する必要があります。次に、
WM_INITDIALOG
メッセージハンドラでは、ビットマップリソース(ID:IDB_BITMAP_BUTTON
)からイメージリストを作成し、それをSetImageList()
によってビットマップボタンに設定します。イメージリストを設定した後は、SetImages()
によってイメージリストのインデックスとボタンの状態を関連付けます。 SetImages()
は、第1引数から順に、通常の状態、押された状態、フォーカスが当たった状態、使用不可の状態のイメージリストのインデックスを受け取ります。さらに、ビットマップボタンには、SetToolTipText()
によってツールチップを設定しています。最後に、リソースIDが
IDC_BUTTON_BMP
のコマンドメッセージハンドラとして、 OnButtonBmp()
を追加します。このハンドラでは単にメッセージボックスを表示しているだけです。このように、MFCのビットマップボタンではボタンの状態別の4つビットマップリソースを用意しなければならないのに対し、 WTLのビットマップボタンはイメージリストからボタンの外観を作成します。
WTLのビットマップボタンは、独自の拡張スタイルを用意しています。
- BMPBTN_HOVER マウスカーソルがビットマップボタンの上を通ると、フォーカスが当たった状態の画像を表示します。
- BMPBTN_AUTO3D_SINGLE 画像の周りに3Dの境界線を描画します。
- BMPBTN_AUTO3D_DOUBLE BMPBTN_AUTO3D_SINGLEよりも少し太い境界線を描画します。
- BMPBTN_AUTOSIZE 画像の大きさに合うようにビットマップボタンのサイズを自動的に変更します。このスタイルはデフォルトで設定されています。
- BMPBTN_SHAREIMAGELISTS ビットマップボタンのオブジェクトが破棄されても、ビットマップボタンに設定されたイメージリストのオブジェクトは破棄しません。
- BMPBTN_AUTOFIRE ビットマップボタンを押し続けると、繰り返し
WM_COMMAND
メッセージを発生させます。
これらの拡張スタイルは、
SetBitmapButtonExtendedStyle()
によって設定します。
CBitmapButton のツールチップ (CTalkButton クラス)
作った2つ目のオリジナルの クラスだ。 できればこんな遠
回りは したくなかったが、結果的に大仕事になってしまった。
(前と同じじゃん。) 原因は VC++環境のツールバーに
ある。大きさは変えられるものの 色が16色しか使えない。
いまどきこんな色数じゃボタンの機能を表すにあたって著
しく 表現の幅を狭められてしまう。 アイコンも最初は16
色になっているので酩酊してしまうが、後で256色に変更
できる ので、 まあよしとしよう。(最初から256色だとな
およいが。 自作のアプリケーションで 16色アイコンなん
ぞ使ったことがない。)せっかくツールバー 用にと画像を用
意したのに16色に落とすと何がなんだか分からなくなって
しまった。 努力を無にしたくなかったのでビットマップボタン
を並べてツールバーに 見せたが、 ツールチップが出ない
ところや、 ステータスバーの表示もないところがいかにも
自作という感じだ。 で、自作のツールバーにツールチップ
や ステータスバーの表示機能を持たせようという試みが
泥沼化しこのクラスになった。 本当は CDialogBar を
使いたかったがダイアログエディタで乗っけた CButton
に BS_ BITMAP でもってビットマップを貼り付けるのがど
うしてもうまくいかず、そのうちに CTalkButtonクラスの
方が何とか形になったので途中であきらめてしまった。
( CButton の BS_BITMAP を使いこなす方法はだい
ぶ後になってら分かった。ついでにツールチップのテキス
トの入れ 方も。でも CReBar が使いこなせた方がプロっぽ
いかも?)class CTalkButton CBitmapButton をその
まま使ってたんではどうしてもツールチップやステータスバー
の表 示が うまくできなかった。なのでこのクラスは
CBitmapButton を継承し機能拡張を行っている。
#if !defined(AFX_TALKBUTTON_H__1DC177F6
_3055_45D1_9E99_0B07854838C4__INCLUDED_)
#define AFX_TALKBUTTON_H__1DC177F6_
3055_45D1_9E99_0B07854838C4__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// TalkButton.h : ヘッダー ファイル
//
///////////////////////////////////////////////////
// CTalkButton ウィンドウ
class CTalkButton : public CBitmapButton
{
// コンストラクション
public:
CTalkButton();
// アトリビュート
public:
// オペレーション
public:
// オーバーライド
// ClassWizard は仮想関数のオーバーライドを生成します。
//{{AFX_VIRTUAL(CTalkButton)
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
//}}AFX_VIRTUAL
// インプリメンテーション
public:
CToolTipCtrl ttc;
virtual ~CTalkButton();
// 生成されたメッセージ マップ関数
protected:
//{{AFX_MSG(CTalkButton)
afx_msg void
OnMouseMove(UINT nFlags, CPoint point);
afx_msg int
OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ は前行の直前に追加の宣言
//を挿入します。
#endif // !defined(AFX_TALKBUTTON_H__
1DC177F6_3055_45D1_9E99_0B07854838C4
__INCLUDED_)
使用法 CBitmapButton に対して追加したメンバ関数
はハンドラだけなので使用方法は CBitmapButton と
同じ。いつも View のメンバに入れて OnCreate() の
中で Create する方法でしか使っていないので 違う使い
方をしたときにどうなるかは分からない。
CRect rect_hlp(0, 0, 32, 32);
helpButton.Create("",
BS_OWNERDRAW BS_PUSHBUTTON
WS_CHILD WS_VISIBLE, rect_hlp, this,
ID_ABOUT);
helpButton.LoadBitmaps(IDB_HLP_FREE,
IDB_HLP_PUSH, IDB_HLP_FREE, IDB_HLP_PUSH);
↑ な感じでいつも使っている。各パラメータの意味は
CBitmapButton を参照。// TalkButton.cpp :
インプリメンテーション ファイル
//
#include "stdafx.h"
#include "TalkButton.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
////////////////////////////////////////////////
// CTalkButton
CTalkButton::CTalkButton()
{
}
CTalkButton::~CTalkButton()
{
}
BEGIN_MESSAGE_MAP(CTalkButton,
CButton)
//{{AFX_MSG_MAP(CTalkButton)
ON_WM_MOUSEMOVE()
ON_WM_CREATE()
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
///////////////////////////////////////////////
// CTalkButton メッセージ ハンドラ
// ツールチップとステータスバーにツールヒントを出
せるビットマップボタン
int CTalkButton::OnCreate(LPCREATESTRUCT
lpCreateStruct)
{
if (CButton::OnCreate(lpCreateStruct) == -1)
return -1;
ttc.Create(this); // 以下ツールチップの作成・登録
ttc.Activate(TRUE);
CRect rect;
CWnd::GetClientRect(&rect);
ttc.AddTool(this, (UINT)GetDlgCtrlID(), &rect,
(UINT)GetDlgCtrlID());
return 0;
}
BOOL CTalkButton::PreTranslateMessage(
MSG* pMsg)
{
// ツールチップのおきまり
if (m_hWnd)
ttc.RelayEvent(pMsg);
return CButton::PreTranslateMessage(pMsg);
}
void CTalkButton::OnMouseMove(UINT nFlags,
CPoint point)
{
// 親ウインドウのステータスバーにツールヒントを表示する。
if (GetCapture() == this) {
CRect rect;
CWnd::GetClientRect(&rect);
if (!rect.PtInRect(point)) { // マウスがボタンの外
に出たときに idle を出したいから
KillTimer(999); // 自分の上にいるときだけキャ
プチャしてるんだよ。
ReleaseCapture(); // どんくさいだろ。
GetParent()->GetParentFrame()->
SetMessageText(AFX_IDS_IDLEMESSAGE);
}
} else {
GetParent()->GetParentFrame()->
SetMessageText((UINT)GetDlgCtrlID());
SetCapture();
SetTimer(999, 200, NULL); // 親ウインドウ監視タイマー
}
// 自作のツールバーにツールチップを出そうとしてえらい苦労した。
// あんまりうまくいかないんで、あきらめてステータスバーの表示に
// 変更したがそれでも大変だった。
// この CTalkButton を CBitmapButton から派生させることで
// 乗り切ったが、あとでツールチップも実装した。
// 結局、派生クラスを作るんだったらツールチップのほうが楽だった。
// 親ウインドウがバックグラウンドになったときにステータスバーを
// idle に戻したかったのでまたタイマーに頼ってしまった。
CButton::OnMouseMove(nFlags, point);
}
void CTalkButton::OnTimer(UINT nIDEvent)
{
// 親ウインドウがバックグラウンドになったときは
キャプチャーをやめる
if (GetForegroundWindow() != GetParent()->
GetParentFrame()) {
KillTimer(999); // またタイマー使っちゃったよ。
ReleaseCapture(); // もっといいメッセージないの?
GetParent()->GetParentFrame()->
SetMessageText(AFX_IDS_IDLEMESSAGE);
}
CButton::OnTimer(nIDEvent);
}
星期二, 八月 29, 2006
玩转keybd_event(转载)
玩转keybd_event
模拟键盘平时不是很常用, 但是当调用某些快捷键执行某项功能时, 它真的是那么的方便呀. 你不信? 看看下面的实现, 你就会大呼: 为什么不早点告诉我? 呵呵, 原来没有blog呀, 都靠这些挣分呢.
1) 显示桌面:
很多软件有显示桌面的功能, 并且大家的方法都是遍历窗口, 然后让它们最小化, 其实 win系统给咱们了一个非常方便的WIN键(就是键盘上在CTRL键和ALT键之间的那个带win标志的按键), 利用它, 可以轻松的完成显示桌面的功能.
keybd_event(VK_LWIN, 0, 0 ,0);
keybd_event('M', 0, 0 ,0);
keybd_event('M', 0, KEYEVENTF_KEYUP ,0);
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP,0);
其他的操作也类似, 比如直接显示开始的运行,就把上面的'M'换成'R'即可。
直接 keybd_event(VK_LWIN, 0, 0 ,0);
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP,0);
直接显示“开始”对话框了。
2) 实现快速的全选
很多的时候,比如listctrl实现全选,你可以用listctrl循环设置每一项的状态为选中,多罗索的事情呀。用快捷键试一试CTRL+A,其他的快捷键一样的用法,呵呵,你知道怎么办了吧?
keybd_event(VK_CONTROL, (BYTE)0, 0 ,0);
keybd_event('A',(BYTE)0, 0 ,0); //此处可以用 'A', (BYTE)65, 用'a'不起作用.
keybd_event('A', (BYTE)0, KEYEVENTF_KEYUP,0);
keybd_event(VK_CONTROL, (BYTE)0, KEYEVENTF_KEYUP,0);
3) 执行某些特殊的键,比如数字键,大小写,下面是数字键的例子
bool bState=true; //true为按下NumLock,false反之
BYTE keyState[256];
GetKeyboardState((LPBYTE)&keyState);
if( (bState && !(keyState[VK_NUMLOCK] & 1))
(!bState && (keyState[VK_NUMLOCK] & 1)) )
{
// Simulate a key press
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY 0,
0 );
// Simulate a key release
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY KEYEVENTF_KEYUP,
0);
}
4) 你想CTRL+ALT+DELETE三键一起按下,
keybd_event(VK_CONTROL, 0, 0 ,0);
keybd_event(VK_MENU,0, 0 ,0);
keybd_event(VK_DELETE,0, 0 ,0);
keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP ,0);
keybd_event(VK_MENU,0, KEYEVENTF_KEYUP ,0);
keybd_event(VK_DELETE,0, KEYEVENTF_KEYUP ,0);
呵呵,这样不会成功呀,因为这几个键直接是操作系统来截获执行的,而模拟键盘只能发向应用程序,所以这种方法不行的(想显示锁定对话框,用 LockWorkStation();)
5) Window2000/NT/XP已经不提倡用这个函数了,上面的方法只是为了让大家开阔一下思路,怎么替代呢,呵呵,看下面,所以上面的所有代码都可以用这个来完成
//2000下用这个代替 ,包含 "winable.h"
INPUT input[4];
memset(input, 0, sizeof(input));
input[0].type = input[1].type = input[2].type = input[3].type = INPUT_KEYBOARD;
input[0].ki.wVk = input[3].ki.wVk = VK_LWIN;
input[1].ki.wVk = input[2].ki.wVk = 'R';
//接下来释放它,这一点很重要。
input[2].ki.dwFlags = input[3].ki.dwFlags = KEYEVENTF_KEYUP;
input[0].ki.time = input[1].ki.time = input[2].ki.time = input[3].ki.time = GetTickCount();
SendInput(4, input, sizeof(INPUT));
感觉比那个有点罗索,呵呵。
====================
附WIN键的部分快捷键:
WIN键+D=快速的切到桌面,再次点击返回
WIN键+E=快速打开资源管理器
WIN键+R=“运行”。
WIN键+M=全部视窗最小化。
WIN键+Shift+M=取消全部视窗最小化。
WIN键+F1=Help。
WIN键+F=“寻找”。
WIN键+Ctrl+F=显示“查找电脑”。
WIN键+Tab=切换工作列的程式。
WIN键+Break=显示系统内容。