最好做法当然是继承CStatic然后重载OnPaint(),完全自己来画,这样能够获得最大的灵活性,但是比较麻烦。
可以使用以下方便的方法:
同样创建一个CStatic的派生类,处理父窗口的反射消息WM_CTLCOLOR,即添加HBRUSH CtlColor(CDC *pDC, UINT nCtlColor)这个消息映射函数 。注意,不是HBRUSH OnCtlColor(CDC *pDC, CWnd *pWnd, UINTnCtlColor)!
其实还有一个方法,就是处理父窗口的OnCtlColor(),更简单一点,但是不符合封装的原则。
HBRUSH CStatic_bg_color_sizeDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: 在此更改 DC 的任何特性
if (nCtlColor == CTLCOLOR_STATIC)
{
pDC->SetBkMode(TRANSPARENT); // 设置透明背景
pDC->SetTextColor(RGB(0, 0, 255)); // 设置文本颜色
return (HBRUSH)GetStockObject(HOLLOW_BRUSH); // 返回透明画刷
}
return hbr;
}
-------------------------------------------------------------------------------------------------
通过上述代码,就可以得到彩色的文本以及透明的背景,但是,还存在一个问题,当该Static控件的文本内容或者属性,在运行过程中发生变化的时候,由于背景一直没有擦除(为了实现透明),会出现重影,导致文本模糊成一团。
解决方法是,让父窗口进行重绘更新 ,对,不要看错了,是控件所属的父窗口,而不是控件本身,让控件本身重绘也不会解决问题的,这里还会引出一个问题,如果重绘整个父窗口,由于GDI并不内嵌双缓冲,势必造成严重的闪烁问题,解决办法当然是只让父窗口重绘控件所占的部分,其他部分不进行重绘 ,代码如下:
C++代码
void CSample::SetText( const TCHAR *pszText)
{
this ->SetWindowText(pszText);
RECT stRect;
// 获取控件位置
this ->GetWindowRect(&stRect);
// 重要!调用父窗口的S2C函数进行坐标转换
this ->GetParent()->ScreenToClient(&stRect);
// 重绘控件所在区域,在这里擦除背景
this ->GetParent()->InvalidateRect(&stRect, true );
}
这样就能够实现动态改变文本属性而不出现重影现象,注意这里调用了父窗口的ScreenToClient()函数来进行坐标的转换 ,调用控件本身的S2C函数的话,得到的坐标无法用来进行下一步的重绘工作。
现在还有一个比较隐蔽的问题,就是文本字符串的长度,如果新的字符串的长度比原来的长,而之前拖放Static控件长度又不足的时候,就会造成超出的部分无法显示,当然你大可以在拖放的时候就尽量弄得长一点,但是如果能随着文本内容而自动调整控件长度,那不是会好得多么。
为了实现这样的效果,上面的代码要修改如下:
C++代码
void CSample::SetText( const TCHAR *pszText)
{
CDC *pDC = this ->GetDC();
// 获取文本在当前绘图环境下所占的宽度和高度
CSize clSize = pDC->GetTextExtent(pszText, _tcslen(pszText));
RECT stRect;
// 获取控件当前矩形区域
this ->GetWindowRect(&stRect);
// 调整宽度为新文本所占宽度
stRect.right = stRect.left + clSize.cx;
// 重要!调用父窗口S2C函数转换坐标
this ->GetParent()->ScreenToClient(&stRect);
// 调整控件大小以适应新文本
this ->MoveWindow(&stRect);
// 重绘控件以避免重影
this ->GetWindowRect(&stRect);
this ->GetParent()->ScreenToClient(&stRect);
this ->GetParent()->InvalidateRect(&stRect, true );
}
同样,这里也是调用父窗口的S2C函数 ,这样得到的坐标才能正确使用。代码经过上述修改,就实现了控件随文本动态调整宽度的效果。
以上只是实现Static背景透明、更改文本颜色以及动态调整控件大小的简单演示,实际的应用中可能还需要考虑很多情况,适当修改代码,但基本原理是不变的。当然要获得最大的灵活性,还是得自己来绘制了。
控件风格
含义
SS_BLACKFRAME
指定一个具有与窗口边界同色的框(缺省为黑色)。
SS_BLACKRECT
指定一个具有与窗口边界同色的实矩形(缺省为黑色)。
SS_CENTER
使显示的正文居中对齐,正文可以回绕。
SS_GRAYFRAME
指定一个具有与屏幕背景同色的边框。
SS_GRAYRECT
指定一个具有与屏幕背景同色的实矩形。
SS_ICON
使控件显示一个在资源中定义的图标,图标的名字有Create函数的lpszText参数指定。
SS_LEFT
左对齐正文,正文能回绕。
SS_LEFTNOWORDWRAP
左对齐正文,正文不能回绕。
SS_NOPREFIX
使静态正文串中的&不是一个热键提示符。
SS_NOTIFY
使控件能向父窗口发送鼠标事件消息。
SS_RIGHT
右对齐正文,可以回绕。
SS_SIMPLE
使静态正文在运行时不能被改变并使正文显示在单行中。
SS_USERITEM
指定一个用户定义项。
SS_WHITEFRAME
指定一个具有与窗口背景同色的框(缺省为白色)。
SS_WHITERECT
指定一个具有与窗口背景同色的实心矩形(缺省为白色)。
除了上表中的风格外,一般还要为控件指定WS_CHILD和WS_VISIBLE窗口风格。一个典型的静态正文控件的风格为WS_CHILD|WS_VISIBLE|SS_LEFT。
函数声明
用途
HBITMAP SetBitmap( HBITMAP hBitmap );
指定要显示的位图。
HBITMAP GetBitmap( ) const;
获取由SetBitmap指定的位图。
HICON SetIcon( HICON hIcon );
指定要显示的图标。
HICON GetIcon( ) const;
获取由SetIcon指定的图标。
HCURSOR SetCursor( HCURSOR hCursor );
指定要显示的光标图片。
HCURSOR GetCursor( );
获取由SetCursor指定的光标。
HENHMETAFILE SetEnhMetaFile( HENHMETAFILE hMetaFile );
指定要显示的增强图元文件。
HENHMETAFILE GetEnhMetaFile( ) const;
获取由SetEnhMetaFile指定的图元文件。
消息
含义
EN_CHANGE
编辑框的内容被用户改变了。与EN_UPDATE不同,该消息是在编辑框显示的正文被刷新后才发出的。
EN_ERRSPACE
编辑框控件无法申请足够的动态内存来满足需要。
EN_HSCROLL
用户在水平滚动条上单击鼠标。
EN_KILLFOCUS
编辑框失去输入焦点。
EN_MAXTEXT
输入的字符超过了规定的最大字符数。在没有ES_AUTOHSCROLL或ES_AUTOVSCROLL的编辑框中,当正文超出了编辑框的边框时也会发出该消息。
EN_SETFOCUS
编辑框获得输入焦点。
EN_UPDATE
在编辑框准备显示改变了的正文时发送该消息。
EN_VSCROLL
用户在垂直滚动条上单击鼠标。
控件风格
含义
ES_AUTOVSCROLL
当用户在最后一个可见行按回车键时,正文向上滚动一页。
ES_CENTER
在多行编辑框中使正文居中。
ES_LEFT
左对齐正文。
ES_LOWERCASE
把用户输入的字母统统转换成小写字母。
ES_MULTILINE
指定一个多行编辑器。若多行编辑器不指定ES_AUTOHSCROLL风格,则会自动换行,若不指定ES_AUTOVSCROLL,则多行编辑器会在窗口中正文装满时发出警告声响。
ES_NOHIDESEL
缺省时,当编辑框失去输入焦点后会隐藏所选的正文,当获得输入焦点时又显示出来。设置该风格可禁止这种缺省行为。
ES_OEMCONVERT
使编辑框中的正文可以在ANSI字符集和OEM字符集之间相互转换。这在编辑框中包含文件名时是很有用的。
ES_PASSWORD
使所有键入的字符都用“*”来显示。
ES_RIGHT
右对齐正文。
ES_UPPERCASE
把用户输入的字母统统转换成大写字母。
ES_READONLY
将编辑框设置成只读的。
ES_WANTRETURN
使多行编辑器接收回车键输入并换行。如果不指定该风格,按回车键会选择缺省的命令按钮,这往往会导致对话框的关闭。
除了上表中的风格外,一般还要为控件指定WS_CHILD、WS_VISIBLE、WS_TABSTOP和WS_BORDER窗口风格,WS_BORDER使控件带边框。创建一个普通的单行编辑框应指定风格为WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL,这将创建一个带边框、左对齐正文、可水平滚动的单行编辑器。要创建一个普通多行编辑框,还要附加ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|WS_HSCROLL| WS_VSCROLL风格,这将创建一个可水平和垂直滚动的,带有水平和垂直滚动条的多行编辑器。
函数声明
用途
void Clear( )
清除编辑框中被选择的正文。
void Copy( )
把在编辑框中选择的正文拷贝到剪贴板中。
void Cut( )
清除编辑框中被选择的正文并把这些正文拷贝到剪贴板中。
void Paste( )
将剪贴板中的正文插入到编辑框的当前插入符处。
BOOL Undo( )
撤消上一次键入。对于单行编辑框,该函数总返回TRUE,对于多行编辑框,返回TRUE表明操作成功,否则返回FALSE。
m_edit.Clear();
m_edit.Copy();
m_edit.Paste();
m_edit.Undo();
m_edit.Cut();
方法一: m_edit1.SetReadOnly(TRUE); 方法二: ::SendMessage(m_edit1.m_hWnd, EM_SETREADONLY, TRUE, 0);
int nStart, nEnd;
CString strTemp;
m_edit1.GetSel(nStart, nEnd);
if(nStart == nEnd)
{
strTemp.Format(_T("光标在%d"),nStart);
AfxMessageBox(strTemp);
}
else
{
//得到edit选中的内容 m_edit1.GetWindowText(strTemp);
strTemp =strTemp.Mid(nStart) - strTemp.Mid(nEnd);
AfxMessageBox(strTemp);
} 注:GetSel后,如果nStart和nEnd,表明光标处于某个位置(直观来看就是光标在闪动); 如果nStart和nEnd不相等,表明用户在edit中选中了一段内容。
CString str;
m_edit1.SetSel(-1, -1);
m_edit1.ReplaceSel(str);
CEdit::SetSel
voidSetSel(DWORD dwSelection, BOOL bNoScroll = FALSE);
void SetSel(intnStartChar, int nEndChar, BOOL bNoScroll = False);
参数: dwSelection 低位字指定起始位置,高位字为结束位置。如果低位为0,高位为-1,则编辑控件中的全部文本被选中;如果低位字为-1,则任何当前选定内容被去掉选定状态。
bNoScroll 指示是否显示脱字符是滚动可见的。如果值为FALSE,则显示,TRUE不显示。
nStartChar 指出当前选中部分的开始位置。如果nStartChar=0且nEndChar=-1,则编辑控件的文本被全选;如果nStartChar=-1,则任何当前选定内容被去掉选定状态。
nEndChar 指出结束位置。
方法一:(摘自msdn) // The pointer to my edit.
extern CEdit* pmyEdit;
int nFirstVisible = pmyEdit->GetFirstVisibleLine();
// Scroll the edit control so thatthe first visible line
// is the first line of text.
if (nFirstVisible > 0)
{
pmyEdit->LineScroll(-nFirstVisible,0);
} 方法二: m_richedit.PostMessage(WM_VSCROLL,SB_BOTTOM, 0);
可以从CEdit派生一个类,添加WM_CHAR消息映射。下面一个例子实现了限定输入16进制字符的功能。 void CMyHexEdit::OnChar(UINT nChar, UINT nRepCnt, UINTnFlags)
{
if ( (nChar >= '0' &&nChar <= '9') ||
(nChar>= 'a' && nChar <= 'f') ||
(nChar >= 'A' && nChar<= 'F') ||
nChar== VK_BACK ||
nChar == VK_DELETE) //msdn的virtual key
{
CEdit::OnChar(nChar, nRepCnt, nFlags);
}
}
我自己创建LimitedTextEdit。添加以上消息处理函数即可。
使用:
LimitedTextEdit m_edit; //第一步
DDX_Control(pDX, IDC_EDIT1, m_edit); //第二步
使用原因:由于RichEdit2.0A自动为宽字符(WideChar),所以它可以解决中文乱码以及一些汉字问题 方法一:(msdn上的做法,适用于用VC.NET及以后版本创建的工程)
BOOL CMFCApplication1App::InitInstance()
{
…………………
CWinApp::InitInstance();
AfxInitRichEdit2();
AfxEnableControlContainer();
// 创建shell 管理器,以防对话框包含
// 任何shell 树视图控件或 shell 列表视图控件。
…………………
return FALSE;
}
方法二:以对话框为例: (1) 增加一全局变量 HMODULE hMod;
(2) 在CxxxApp::InitInstance()中添加一句hMod = LoadLibrary(_T("riched20.dll")); 在CxxxApp::ExitInstance()中添加一句FreeLibrary(hMod);
(3) 在对话框上放一个richedit,文本方式打开.rc文件修改该richedit控件的类名"RICHEDIT"to "RichEdit20a".
(4) 在对话框头文件添加 CRichEditCtrl m_richedit; 在OnInitDialog中添加m_richedit.SubclassDlgItem(IDC_RICHEDIT1, this);
CHARFORMAT cf;
ZeroMemory(&cf, sizeof(CHARFORMAT));
cf.cbSize = sizeof(CHARFORMAT);
cf.dwMask = CFM_BOLD | CFM_COLOR |CFM_FACE |
CFM_ITALIC | CFM_SIZE | CFM_UNDERLINE;
cf.dwEffects = 0;
cf.yHeight = 12*12;//文字高度 cf.crTextColor = RGB(200, 100, 255);//文字颜色 strcpy(cf.szFaceName ,_T("隶书"));//设置字体 m_richedit1.SetSel(1, 5); //设置处理区域 m_richedit1.SetSelectionCharFormat(cf);
PARAFORMAT2 pf;
pf2.cbSize = sizeof(PARAFORMAT2);
pf2.dwMask = PFM_LINESPACING |PFM_SPACEAFTER;
pf2.dyLineSpacing = 200;
pf2.bLineSpacingRule = 4;
m_richedit.SetParaFormat(pf2);
COleDataSourcesrc;
STGMEDIUMsm;
sm.tymed=TYMED_GDI;
sm.hBitmap=hbmp;
sm.pUnkForRelease=NULL;
src.CacheData(CF_BITMAP,&sm);
LPDATAOBJECTlpDataObject =
(LPDATAOBJECT)src.GetInterface(&IID_IDataObject);
pRichEditOle->ImportDataObject(lpDataObject,0, NULL);
lpDataObject->Release();
字体设置代码
最后添加字体变换函数:
CHARFORMATcf;
LOGFONTlf;
memset(&cf,0, sizeof(CHARFORMAT));
memset(&lf,0, sizeof(LOGFONT));
//判断是否选择了内容
BOOLbSelect = (GetSelectionType() != SEL_EMPTY) ? TRUE : FALSE;
if(bSelect)
{
GetSelectionCharFormat(cf);
}
else
{
GetDefaultCharFormat(cf);
}
//得到相关字体属性
BOOLbIsBold = cf.dwEffects & CFE_BOLD;
BOOLbIsItalic = cf.dwEffects & CFE_ITALIC;
BOOLbIsUnderline = cf.dwEffects & CFE_UNDERLINE;
BOOLbIsStrickout = cf.dwEffects & CFE_STRIKEOUT;
//设置属性
lf.lfCharSet= cf.bCharSet;
lf.lfHeight= cf.yHeight/15;
lf.lfPitchAndFamily= cf.bPitchAndFamily;
lf.lfItalic= bIsItalic;
lf.lfWeight= (bIsBold ? FW_BOLD : FW_NORMAL);
lf.lfUnderline= bIsUnderline;
lf.lfStrikeOut= bIsStrickout;
sprintf(lf.lfFaceName,cf.szFaceName);
CFontDialogdlg(&lf);
dlg.m_cf.rgbColors= cf.crTextColor;
if (dlg.DoModal()== IDOK)
{
dlg.GetCharFormat(cf);//获得所选字体的属性
if (bSelect)
SetSelectionCharFormat(cf); //为选定的内容设定所选字体
else
SetWordCharFormat(cf); //为将要输入的内容设定字体
}
首先在Form上放置一个RichEdit。
在窗体的构造函数中添加以下代码:
__fastcallTMainForm::TMainForm(TComponent* Owner)
: TForm(Owner)
{
unsigned mask =SendMessage(RichEdit1->Handle, EM_GETEVENTMASK, 0, 0);
SendMessage(RichEdit1->Handle,EM_SETEVENTMASK, 0, mask | ENM_LINK);
SendMessage(RichEdit1->Handle,EM_AUTOURLDETECT, true, 0); //自动检测URL
RichEdit1->Text = "欢迎访问C++ Builder\n"
"网址: http://www.ccrun.com\n"
"偶的信箱:\n"
"mailto::info@ccrun.com\n"
"嘿嘿\n";
}
重载窗体的WndProc
1。在.h中添加:
protected:
virtual void __fastcall WndProc(Messages::TMessage&Message);
2。在.cpp中添加:
//---------------------------------------------------------------------------
void__fastcall TMainForm::WndProc(Messages::TMessage &Message)
{
if (Message.Msg == WM_NOTIFY)
{
if (((LPNMHDR)Message.LParam)->code== EN_LINK)
{
ENLINK* p = (ENLINK*)Message.LParam;
if (p->msg == WM_LBUTTONDOWN)
{
SendMessage(RichEdit1->Handle, EM_EXSETSEL, 0,(LPARAM)&(p->chrg));
ShellExecute(Handle,"open", RichEdit1->SelText.c_str(), 0, 0, SW_SHOWNORMAL);
}
}
}
TForm::WndProc(Message);
}
首先确定你要显示在按钮控件的图片类型是什么,这里我是ico图标,由于要在按钮里显示ico图标,所以要把按钮控件的属性改一下,方法是右击按钮控件,选择属性,单击样式选项卡,把图标这一项给勾上。
按钮控件类(CButton)类里有个成员函数SetIcon可以设置按钮显示的图标,该函数只有一个参数,那就是图标句柄。
然后在对话框类的初始化函数(CFirstDlg::OnInitDialog)添加如下语句:
m_Quit.SetIcon((HICON)::LoadImage(NULL,”e:\i.ico”,IMAGE_ICON,48,48,LR_LOADFROMFILE));//假设E盘下有一个i.ico图标
第二种根据图标ID加载:
m_Quit.SetIcon(LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDI_ICON1)));
记住语句添加的位置是在OnInitDialog函数所有代码之后,return TRUE;之前
首先在对话框类(CFirstDlg)里添加一个m_ToolTip类对象(public:公有),如:CToolTipCtrlm_ToolTip;然后在对话框类里的OnInitDialog函数添加以下语句:
m_ToolTip.Create(this);
m_ToolTip.AddTool(&m_Quit,”文本信息”); //其中m_Quit为按钮控件关联的变量
接着往对话框类添加一个虚函数 PreTranslateMessage,这个虚函数有一个参数MSG *pMsg;MSG这个结构在API常用函数里有解释。这个函数会截获所有发送到对应窗口的消息。
在这个函数添加这个语句:m_ToolTip.RelayEvent(pMsg);
完整的就是:
BOOL CFirstDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
m_ToolTip.RelayEvent(pMsg);
return CDialog::PreTranslateMessage(pMsg);
}
正确做法是用new调用CButton构造函数生成一个实例: CButton *p_MyBut = new CButton(); 然后用CButton类的Create()函数创建,该函数原型如下: BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd*pParentWnd, UINT nID );
lpszCaption是按钮上显示的文本; dwStyle指定按钮风格,可以是按钮风格与窗口风格的组合,取值有: 窗口风格: WS_CHILD 子窗口,必须有 WS_VISIBLE 窗口可见,一般都有 WS_DISABLED 禁用窗口,创建初始状态为灰色不可用的按钮时使用 WS_TABSTOP 可用Tab键选择 WS_GROUP 成组,用于成组的单选按钮中的第一个按钮 按钮风格:
控件风格
含义
BS_AUTOCHECKBOX
同BS_CHECKBOX,不过单击鼠标时按钮会自动反转。
BS_AUTORADIOBUTTON
同BS_RADIOBUTTON,不过单击鼠标时按钮会自动反转。
BS_AUTO3STATE
同BS_3STATE,不过单击按钮时会改变状态。
BS_CHECKBOX
指定在矩形按钮右侧带有标题的选择框。
BS_DEFPUSHBUTTON
指定缺省的命令按钮,这种按钮的周围有一个黑框,用户可以按回车键来快速选择该按钮,一个对话框中只能指定一个默认按钮。
BS_GROUPBOX
指定一个组框。
BS_LEFTTEXT
使控件的标题显示在按钮的左边。
BS_OWNERDRAW
指定一个自绘式按钮。
BS_PUSHBUTTON
指定一个命令按钮。
BS_RADIOBUTTON
指定一个单选按钮,在圆按钮的右边显示正文。
BS_3STATE
同BS_CHECKBOX,不过控件有三种状态:选择、未选择和变灰。
除了上表中的风格外,一般还要为控件指定WS_CHILD、WS_VISIBLE和WS_TABSTOP窗口风格,WS_TABSTOP使控件具有Tabstop属性。创建一个普通按钮应指定的风格为WS_CHILD|WS_VISIBLE|WS_TABSTOP。创建一个普通检查框应指定风格WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX。创建组中第一个单选按钮应指定风格WS_CHILD| WS_VISIBLE | WS_TABSTOP | WS_GROUP| BS_AUTORADIOBUTTON,组中其它单选按钮应指定风格则不应该包括WS_TABSTOP和WS_GROUP。 BS_BITMAP 按钮上将显示位图 -------------------------------------------------------------------------------------------------
rect指定按钮的大小和位置; pParentWnd指示拥有按钮的父窗口,不能为NULL; nID指定与按钮关联的ID号,用上一步创建的ID号。
-------------------------------------------------------------------------------------------------
例:p_MyBut->Create("动态按钮", WS_CHILD| WS_VISIBLE | BS_PUSHBUTTON, CRect(20,10,80,40), this, IDC_MYBUTTON ); 这样,我们就在当前对话框中的(20,10)处创建了宽60,高30,按钮文字为“动态按钮”的下压式按钮。
为了使创建过程更方便易用,我定义了如下函数:
CButton* CTextEditorView::NewMyButton(int nID,CRectrect,int nStyle)
{
CString m_Caption;
m_Caption.LoadString( nID ); //取按钮标题 CButton *p_Button = new CButton();
ASSERT_VALID(p_Button);
p_Button->Create( m_Caption, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | nStyle,rect, this, nID ); //创建按钮 return p_Button;
}
其中m_Caption.LoadString(nID )是从字符串表中读取按钮文本,这样在创建按钮ID时,应该把文本设置好,参数nStyle为除必须风格外的额外风格。 以下,我调用该函数创建三个按钮,并指定第一个按钮为默认按钮,按钮的ID已预先设置好了: CButton *p_MyBut[3];
p_MyBut[0] = NewMyButton( ID_MYBUT1, CRect(10,20,50,35), BS_DEFPUSHBUTTON);
p_MyBut[1] = NewMyButton( ID_MYBUT2, CRect(55,20,95,35), 0 );
p_MyBut[2] = NewMyButton( ID_MYBUT3, CRect(100,20,140,35), 0 );
-------------------------------------------------------------------------------------------------
动态控件的响应函数不能用ClassWizard添加,只能手动添加。仍以上面的按钮为例,我们制作按钮的单击响应函数。
1.在MESSAGE_MAP中添加响应函数:
MESSAGE_MAP表中定义了消息响应函数,其格式为:消息名(ID,函数名),当我们用ClassWizard添加函数时,会自动添加在AFX_MSG_MAP括起的区间内,如:
BEGIN_MESSAGE_MAP(CTextEditorView, CFormView)
//{{AFX_MSG_MAP(CTextEditorView)
ON_BN_CLICKED(IDC_ICONBUT0, OnIconbut0)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
手工添加时不要添加到AFX_MSG_MAP区间内,以防ClassWizard不能正常工作,如: BEGIN_MESSAGE_MAP(CTextEditorView, CFormView)
//{{AFX_MSG_MAP(CTextEditorView)
ON_BN_CLICKED(IDC_ICONBUT0, OnIconbut0)
//}}AFX_MSG_MAP
ON_BN_CLICKED(ID_MYBUT1, OnMybut1)
ON_BN_CLICKED(ID_MYBUT2, OnMybut2)
ON_BN_CLICKED(ID_MYBUT3, OnMybut3)
END_MESSAGE_MAP()
其中ON_BN_CLICKED是按钮单击消息。 2.在头文件中添加函数定义:
用ClassWizard添加函数时,会在头文件的AFX_MSG区间内添加函数定义,如:
protected:
//{{AFX_MSG(CTextEditorView)
afx_msg void OnIconbut0();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
我们模仿这种形式,只是把函数定义添加到AFX_MSG区间外就行了: protected:
//{{AFX_MSG(CTextEditorView)
afx_msg void OnIconbut0();
//}}AFX_MSG
afx_msg void OnMybut1();
afx_msg void OnMybut2();
afx_msg void OnMybut3();
DECLARE_MESSAGE_MAP()
3.编写消息响应函数: 以上是把消息和函数关联起来了,具体在单击按钮后应做的工作在函数中完成:
void CTextEditorView::OnMybut1()
{
MessageBox( "哈!你单击了动态按钮。" );
}
void CTextEditorView::OnMybut2()
{
……
}
void CTextEditorView::OnMybut3()
{
……
}
除了按钮的响应函数外,你还可以用上面获得的指针访问按钮,如: 修改按钮的大小和位置:p_MyBut[0]->MoveWindow(……); 修改按钮文本:p_MyBut[0]->SetWindowText(……); 显示/隐藏按钮:p_MyBut[0]->ShowWindow(……);等等。 三、回收资源:
由于动态控件对象是由new生成的,它不会被程序自动释放,所以需手工释放。在控件不再使用时可以删除它:
if( p_MyBut[0] )
delete p_MyBut[0];
以上就是按钮控件动态生成的方法。下面,再看一下单选按钮的动态生成问题。 四、实例:单选按钮组的动态生成
单选按钮也属于CButton类,但由于单选按钮总是成组使用的,所以它在制作和使用上与普通按钮有一定区别。
假设有三个单选按钮组成一组,初始时,第一个单选按钮处于选中状态。
我们先来看静态制作方法:在对话框中放置三个单选按钮,设置属性如下: Radio1属性:Visible、Group、Tab stop、Auto
Radio2属性:Visible、Tab stop、Auto
Radio3属性:Visible、Tab stop、Auto 这样的属性设置就把三个单选按钮分成了一组,它们一次只能有一个被选中,若对话框中还有其它成组的单选按钮,使用时也会互不干扰。但这时还没有使第一个按钮处于选中状态。 接着就用ClassWizard为这组单选按钮添加变量,这里只需为第一个单选按钮添加变量即可。设变量名为m_Radio,类型选为int型。在构造函数中ClassWizard把m_Radio的值设置为-1,我们把它改为0,这样在运行程序时可以看到第一个单选按钮处于选中状态了。 之后,还应该用ClassWizard为三个单选按钮添加单击响应函数,在里面修改m_Radio的值对应三个单选按钮就可以了。 以上就是通常制作单选按钮组的办法,现我们欲改为动态生成,主要要解决按钮分组和单击控制问题。以下为制作步骤:
1.定义三个单选按钮的ID:
打开资源中的“String Table”,在其中添加三个ID值: 第一个:ID为IDC_MYRADIO1,Caption为单选1 第二个:ID为IDC_MYRADIO2,Caption为单选2 第三个:ID为IDC_MYRADIO3,Caption为单选3 其中Caption为按钮上要显示的文字,可根据需要设置。
2.用CButton类的Create()函数生成三个单选按钮:
为方便起见,先定义一个函数生成单选按钮:
CButton* CTextEditorView::NewMyRadio(int nID,CRectrect,int nStyle)
{
CString m_Caption;
m_Caption.LoadString( nID ); //取按钮标题 CButton *p_Radio = new CButton();
ASSERT_VALID(p_Radio);
p_Radio->Create( m_Caption, WS_CHILD | WS_VISIBLE | nStyle | WS_TABSTOP |BS_AUTORADIOBUTTON, rect, this, nID ); //创建按钮 return p_Radio;
}
函数LoadString()用于从“String Table”中读取按钮文本,Create()函数中设定了单选按钮必须的属性,其中就包括了Visible、Tab stop、Auto属性。 参数nID为单选按钮ID号,rect为单选按钮尺寸,nStyle为除必要属性外的其它属性。返回值为指向新建按钮的指针。 有了这个函数后,创建单选按钮组时只要依次调用该函数即可,其中单选按钮组的第一个单选按钮必须指定WS_GROUP属性。
CButton *p_MyRadio[3];
p_MyRadio[0] = NewMyRadio( IDC_MYRADIO1, CRect(15,90,60,105), WS_GROUP );
p_MyRadio[1] = NewMyRadio( IDC_MYRADIO2, CRect(15,108,60,123), 0 );
p_MyRadio[2] = NewMyRadio( IDC_MYRADIO3, CRect(15,126,60,141), 0 );
3.定义单选按钮组的控制变量,设置第一个单选按钮为选中状态: 这里不能用ClassWizard添加变量,也不要在DoDataExchange()中添加控制变量,因为动态控件一开始并不存在,在DoDataExchange()中添加控制变量会造成运行错误。这里我们只需在头文件中随意定义一个int型变量作为控制变量即可,如: int m_SelRadio; 在构造函数中设置其初值为0:m_SelRadio = 0; 在上面的创建按钮的语句中,用SetCheck()函数设置初始选中的按钮:
CButton *p_MyRadio[3];
p_MyRadio[0] = NewMyRadio( IDC_MYRADIO1, CRect(15,90,60,105), WS_GROUP );
p_MyRadio[1] = NewMyRadio( IDC_MYRADIO2, CRect(15,108,60,123), 0 );
p_MyRadio[2] = NewMyRadio( IDC_MYRADIO3, CRect(15,126,60,141), 0 );
p_MyRadio[m_SelRadio]->SetCheck(1); //设置第一个单选为选中状态
在SetCheck()函数中,参数为1表示设置为选中状态,为0表示未选中状态。 4.添加鼠标单击响应函数:
鼠标单击某单选按钮后,其状态已经能自动改变,这里我们还需修改控制变量m_SelRadio的值,以便跟踪选中的单选按钮。
首先在MESSAGE_MAP中把鼠标单击消息与响应函数联系起来:
BEGIN_MESSAGE_MAP(CTextEditorView, CFormView)
//{{AFX_MSG_MAP(CTextEditorView)
ON_BN_CLICKED(IDC_ICONBUT0, OnIconbut0) //ClassWizard在此处添加 //}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_MYRADIO1, OnMyRadio1) //单选按钮1
ON_BN_CLICKED(IDC_MYRADIO2, OnMyRadio2) //单选按钮2
ON_BN_CLICKED(IDC_MYRADIO3, OnMyRadio3) //单选按钮3
END_MESSAGE_MAP()
然后在头文件的MESSAGE_MAP中定义单击函数: protected:
//{{AFX_MSG(CTextEditorView)
afx_msg void OnIconbut0(); //ClassWizard在此处添加 //}}AFX_MSG
afx_msg void OnMyRadio1(); //单选按钮1
afx_msg void OnMyRadio2(); //单选按钮2
afx_msg void OnMyRadio3(); //单选按钮3
DECLARE_MESSAGE_MAP()
这里注意不要把函数加在AFX_MSG区间内,以防影响ClassWizard的使用。 定义具体的响应函数(这里是用手工加入的,不是用ClassWizard加入的): //单击单选按钮1
void CTextEditorView::OnMyRadio1()
{
m_SelRadio=0;
}
//单击单选按钮2
void CTextEditorView::OnMyRadio2()
{
m_SelRadio=1;
}
//单击单选按钮3
void CTextEditorView::OnMyRadio3()
{
m_SelRadio=2;
}
5.回收资源: 在析构函数中,回收创建的单选按钮(也可以在不使用单选按钮时立即回收):
CTextEditorView::~CTextEditorView()
{
int i;
for( i=0; i<3; i++)
{
if(p_MyRadio[i])
delete p_MyRadio[i];
}
}
以上就是动态控件的生成和响应方法,各种不同的控件做法略有不同,但思路和步骤都是类似的,希望以上实例对你能够有所帮助。
//-------------以下是文自己总结--------------
其实拖动控件到窗口就可以。
说下这里的基本思路;
1、在头文件中声明控件执行方法。
public:
afx_msg void OnBnClickedCheck();
2、在程序文件中实现这个方法。
void OnBnClickedCheck()
{
//在这里实现,点击button后相应的实现
}
3、在程序文件中注册这个控件和这个方法之间的关系,就是所谓的注册消息
ON_BN_CLICKED(IDC_Check,OnBnClickedCheck) //这里后面没有分号;
VC为按钮控件添加图片的方法有很多种:
直接调用SetBitmap; CButton pButton->SetBitmap(hBitmap);
使用CButtonST控件;
使用CDC;
使用CBitmapButton;
这里主要讲解CBitmapButton的使用,CBitmapButton作为MFC的控件类,具体使用如下:
创建一个新的基于对话框的MFC工程首先添加按钮控件,将属性Owner Draw设为True,按钮ID: IDC_BUTTON1
添加位图资源ID:IDB_BITMAP_CLOSE IDB_BITMAP_RED
在对话框类中添加private成员变量: CBitmapButton m_button; bool LedFlag;
private:
CBitmapButton m_button;
boolLedFlag;
在初始化函数OnInitDialog里添加图片加载:
m_button.LoadBitmaps(IDB_BITMAP1);
m_button.SubclassDlgItem(IDC_BUTTON2,this);
m_button.SizeToContent();
使用CBitmapButton::LoadBitmaps装载各种状态的图片,再用SubclassDlgItem关联到想要的按钮,使用CBitmapButton::SizeToContent函数使按钮适合图片大小。注意Loadbitmaps一定要在关联到按钮之前进行!
为按钮控件添加单击事件处理函数,输入如下代码:
LedFlag = !LedFlag;
if (LedFlag)
{
m_button.LoadBitmaps(IDB_BITMAP1);
m_button.Invalidate();
}
else
{
m_button.LoadBitmaps(IDB_BITMAP2);
m_button.Invalidate();
}
按理来说,写上m_ctrlButton.LoadBitmaps(图片ID);就可以了,可是这样做之后,发现按键了没反应。无意中将对话框最小化再还原,发现图片变了。所以实际上是没有进行刷新导致的。在后面加上m_button.Invalidate()就可以了。
(1) 在Dialog类中,声明CListBox类型的成员变量.
例如在listDlg.h 文件中:
(2) DoDataExchang函数中,把m_list1成员变量连接到控件.
在listDlg.cpp文件中
在 listDlg.cpp 文件中:
//备注: 也可以定义个控件对象的指针,对对象进行操作: 如下: (这样就不需要上述的第一步了)
首先要将Listbox的Selection属性设置为Multiple;
首先要将ListBox的Selection的属性设置为Multiple
添加一个Listbox的双击事件
OnInitDialog();函数里初始化
对CListBox的操作
void CUseControlDlg::OnBnClickedButtonOk()
{
// TODO: 在此添加控件通知处理程序代码
CListBox *m_lstInfo = (CListBox *)GetDlgItem(IDC_LIST1);
//那么你可以用一个循环取出里面的值:
/*
CString str; //临时变量用来接收项的字符串
CString strAll=_T(""); //所有项
int nCount = m_lstInfo->GetCount();//得到项目总数
for(int i = 0; i< nCount; ++i)
{
m_lstInfo->GetText(i,str);
strAll = strAll + str + _T("\r\n");
}
AfxMessageBox(strAll);
*/
//取出单选选中的值
/*
int index;
CString selectStr;
index = m_lstInfo->GetCurSel();
m_lstInfo->GetText(index,selectStr);
AfxMessageBox(selectStr);
*/
//多选,设置selection为Multiple
int nCount = m_lstInfo->GetSelCount();
CString cCount;
CArray
aryListBoxSel.SetSize(nCount);
m_lstInfo->GetSelItems(nCount, aryListBoxSel.GetData());
//得到总数
cCount.Format(_T("%d"),nCount);
AfxMessageBox(cCount);
//得到选中的多项
for (int i=0;i<nCount;i++)
{
CString selStr;
m_lstInfo->GetText(aryListBoxSel[i],selStr);
AfxMessageBox(selStr);
}
LVS_ICON: 为每个item显示大图标 LVS_SMALLICON: 为每个item显示小图标 LVS_LIST: 显示一列带有小图标的item
LVS_REPORT: 显示item详细资料 直观的理解:windows资源管理器,"查看"标签下的"大图标,小图标,列表,详细资料
LVS_ALIGNLEFT
当显示格式是大图标或小图标时,标题放在图标的左边.缺省情况下标题放在图标的下面.
LVS_ALIGNTOP
当显示格式是大图标或小图标时,标题放在图标的上边.
LVS_AUTOARRANGE
当显示格式是大图标或小图标时,自动排列控件中的表项.
LVS_EDITLABELS
用户可以修改标题.
LVS_ICON
指定大图标显示格式.
LVS_LIST
指定列表显示格式.
LVS_NOCOLUMNHEADER
在报告格式中不显示列的表头.
LVS_NOLABELWRAP
当显示格式是大图标时,使标题单行显示.缺省时是多行显示.
LVS_NOSCROLL
列表视图无滚动条.
LVS_NOSORTHEADER
报告列表视图的表头不能作为排序按钮使用.
LVS_OWNERDRAWFIXED
由控件的拥有者负责绘制表项.
LVS_REPORT
指定报告显示格式.
LVS_SHAREIMAGELISTS
使列表视图共享图像序列.
LVS_SHOWSELALWAYS
即使控件失去输入焦点,仍显示出项的选择状态.
LVS_SINGLESEL
指定一个单选择列表视图.缺省时可以多项选择.
LVS_SMALLICON
指定小图标显示格式.
LVS_SORTASCENDING
按升序排列表项.
LVS_SORTDESCENDING
按降序排列表项.
除上表的风格外,一般还要指定WS_CHILD和WS_VISIBLE窗口风格.风格组合WS_CHILD| WS_VISIBLE|LVS_REPORT|LVS_AUTOARRANGE|LVS_EDITLABLES|LVS_SINGLESEL将指定一个自动排列的、可编辑标题的、单选择报告式列表视图控件.要指定大图标、小图标或列表式的列表视图控件,则应该把LVS_REPORT换成LVS_ICON、LVS_SMALLICON或LVS_LIST.
对于用对话框模板创建的列表视图控件,可以在控件的属性对话框中指定上表中列出的控件风格。例如,在属性对话框的Styles页的View栏中选择Icon,相当于指定了LVS_ICON风格. --------------------------------------------------------------------------------
LONG lStyle;
lStyle = GetWindowLong(m_list.m_hWnd,GWL_STYLE);//获取当前窗口style
lStyle &= ~LVS_TYPEMASK; //清除显示方式位 lStyle |= LVS_REPORT; //设置style
SetWindowLong(m_list.m_hWnd, GWL_STYLE,lStyle);//设置style
DWORD dwStyle =m_list.GetExtendedStyle();
dwStyle |= LVS_EX_FULLROWSELECT;//选中某行使整行高亮(只适用与report风格的listctrl) dwStyle |= LVS_EX_GRIDLINES;//网格线(只适用与report风格的listctrl) dwStyle |= LVS_EX_CHECKBOXES;//item前生成checkbox控件 m_list.SetExtendedStyle(dwStyle); //设置扩展风格
--------------------------------------------------------------------------------
选中style中的Show selection always,或者在上面第2点中设置LVS_SHOWSELALWAYS
m_list.InsertColumn(0, "ID", LVCFMT_LEFT, 40 );//插入列 m_list.InsertColumn( 1, "NAME",LVCFMT_LEFT, 50 );
//新插入的在上面 int nRow = m_list.InsertItem(0,"11");// 插入行 m_list.SetItemText(nRow, 1,"jacky");//设置其它列数据
//新插入的数据在下面
int nIndex =m_list.GetItemCount();
LV_ITEM lvItem;
lvItem.mask = LVIF_TEXT ;
lvItem.iItem = nIndex; //行数 lvItem.iSubItem = 0;
lvItem.pszText = (char*)(LPCTSTR)strCount; //第一列 //在最后一行插入记录值.
m_list.InsertItem(&lvItem);
//插入其它列 m_list.SetItemText(nIndex,1,strLat);
--------------------------------------------------------------------------------
int nIndex = 0;
//选中 m_list.SetItemState(nIndex, LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED);
//取消选中 m_list.SetItemState(nIndex, 0,LVIS_SELECTED|LVIS_FOCUSED);
--------------------------------------------------------------------------------
m_list.SetExtendedStyle(LVS_EX_CHECKBOXES);
CString str;
for(int i=0; i<m_list.GetItemCount(); i++)
{
if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED ||m_list.GetCheck(i))
{
str.Format(_T("第%d行的checkbox为选中状态"),i);
AfxMessageBox(str);
}
}
--------------------------------------------------------------------------------
方法一: CString str;
for(int i=0; i<m_list.GetItemCount();i++)
{
if(m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED )
{
str.Format(_T("选中了第%d行"), i);
AfxMessageBox(str);
}
} 方法二: POSITION pos =m_list.GetFirstSelectedItemPosition();
if (pos == NULL)
TRACE0("Noitems were selected!/n");
Else
{
while (pos)
{
int nItem = m_list.GetNextSelectedItem(pos);
TRACE1("Item %d was selected!/n", nItem);
// you could do your own processing on nItem here
}
}
--------------------------------------------------------------------------------
TCHAR szBuf[1024];
LVITEM lvi;
lvi.iItem = nItemIndex;
lvi.iSubItem = 0;
lvi.mask = LVIF_TEXT;
lvi.pszText = szBuf;
lvi.cchTextMax = 1024;
m_list.GetItem(&lvi);
--------------------------------------------------------------------------------
LVCOLUMN lvcol;
char str[256];
int nColNum;
CString strColumnName[4];//假如有4列 nColNum = 0;
lvcol.mask = LVCF_TEXT;
lvcol.pszText = str;
lvcol.cchTextMax = 256;
while(m_list.GetColumn(nColNum,&lvcol))
{
strColumnName[nColNum] = lvcol.pszText;
nColNum++;
}
--------------------------------------------------------------------------------
m_list.EnsureVisible(i, FALSE);
--------------------------------------------------------------------------------
int nHeadNum =m_list.GetHeaderCtrl()->GetItemCount();
--------------------------------------------------------------------------------
//首先删除所有行
m_ListCtrl.DeleteAllItems();
//得到列数
intnItmcount = m_ListCtrl.GetHeaderCtrl()->GetItemCount();
//删除所有列
for(int i = 0; i<nItmcount; i++)
{
m_ListCtrl.DeleteColumn(0);
}
--------------------------------------------------------------------------------
添加listctrl控件的NM_CLICK消息相应函数 void CTest6Dlg::OnClickList1(NMHDR*pNMHDR, LRESULT* pResult)
{
// 方法一: /*
DWORD dwPos =GetMessagePos();
CPoint point(LOWORD(dwPos), HIWORD(dwPos) );
m_list.ScreenToClient(&point);
LVHITTESTINFOlvinfo;
lvinfo.pt =point;
lvinfo.flags =LVHT_ABOVE;
int nItem =m_list.SubItemHitTest(&lvinfo);
if(nItem !=-1)
{
CString strtemp;
strtemp.Format("单击的是第%d行第%d列",lvinfo.iItem, lvinfo.iSubItem);
AfxMessageBox(strtemp);
}
*/
// 方法二:
/*
NM_LISTVIEW*pNMListView = (NM_LISTVIEW*)pNMHDR;
if(pNMListView->iItem != -1)
{
CString strtemp;
strtemp.Format("单击的是第%d行第%d列",
pNMListView->iItem, pNMListView->iSubItem);
AfxMessageBox(strtemp);
}
*/
*pResult =0;
}
--------------------------------------------------------------------------------
添加listctrl控件的NM_CLICK消息相应函数 void CTest6Dlg::OnClickList1(NMHDR* pNMHDR,LRESULT* pResult)
{
DWORD dwPos = GetMessagePos();
CPoint point(LOWORD(dwPos), HIWORD(dwPos) );
m_list.ScreenToClient(&point);
LVHITTESTINFOlvinfo;
lvinfo.pt =point;
lvinfo.flags =LVHT_ABOVE;
UINT nFlag;
int nItem =m_list.HitTest(point, &nFlag);
//判断是否点在checkbox上 if(nFlag ==LVHT_ONITEMSTATEICON)
{
AfxMessageBox("点在listctrl的checkbox上");
}
*pResult =0;
}
--------------------------------------------------------------------------------
添加listctrl控件的NM_RCLICK消息相应函数 void CTest6Dlg::OnRclickList1(NMHDR*pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW*pNMListView = (NM_LISTVIEW*)pNMHDR;
if(pNMListView->iItem != -1)
{
DWORD dwPos = GetMessagePos();
CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
CMenu menu;
VERIFY( menu.LoadMenu( IDR_MENU1 ) );
CMenu* popup = menu.GetSubMenu(0);
ASSERT( popup != NULL );
popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
}
*pResult =0;
}
--------------------------------------------------------------------------------
添加listctrl控件的LVN_ITEMCHANGED消息相应函数 void CTest6Dlg::OnItemchangedList1(NMHDR*pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW*pNMListView = (NM_LISTVIEW*)pNMHDR;
// TODO: Add yourcontrol notification handler code here
CStringsTemp;
if((pNMListView->uOldState & LVIS_FOCUSED) == LVIS_FOCUSED &&(pNMListView->uNewState & LVIS_FOCUSED) == 0)
{
sTemp.Format("%d losted focus",pNMListView->iItem);
}
elseif((pNMListView->uOldState & LVIS_FOCUSED) == 0 &&
(pNMListView->uNewState & LVIS_FOCUSED) == LVIS_FOCUSED)
{
sTemp.Format("%d got focus",pNMListView->iItem);
}
if((pNMListView->uOldState & LVIS_SELECTED) == LVIS_SELECTED&&
(pNMListView->uNewState & LVIS_SELECTED) == 0)
{
sTemp.Format("%d losted selected",pNMListView->iItem);
}
else if((pNMListView->uOldState & LVIS_SELECTED) == 0 &&(pNMListView->uNewState & LVIS_SELECTED) == LVIS_SELECTED)
{
sTemp.Format("%d got selected",pNMListView->iItem);
}
*pResult = 0;
--------------------------------------------------------------------------------
m_list.SetExtendedStyle(LVS_EX_SUBITEMIMAGES);
m_list.SetItem(..); //具体参数请参考msdn
--------------------------------------------------------------------------------
1) 在Combo Box控件属性的Data标签里面添加,一行表示Combo Box下拉列表中的一行。换行用ctrl+回车。
2) 利用函数 AddString() 向 Combo Box 控件添加 Items,如:
m_cbExample.AddString(“StringData1”);
m_cbExample.AddString(“StringData2”);
m_cbExample.AddString(“StringData3”);
3) 也可以调用函数 InsertString() 将 Item 插入指定位置 nIndex,如:
m_cbExample.InsertString(nIndex, “StringData” );
--------------------------------------------------------------------------------
int nIndex =m_cbExample.GetCurSel();
CStringstrCBText;
m_cbExample.GetLBText(nIndex, strCBText);
这样,得到的内容就保存在 strCBText 中。
--------------------------------------------------------------------------------
这种操作一般用于在程序中动态修改控件中该项的值,可以用函数FindStringExact()精确匹配,如:
int nIndex =m_cbExample.FindStringExact( nStartAfter, “value to be found”);
nStartAfter指明从哪一行开始查找。如果查找成功,返回的是该项的位置;否则,返回CB_ERR。
也可以选中包含指定字符串的项,如:
int nIndex =m_cbExample.SelectString( nStartAfter, “value to be selected”);
--------------------------------------------------------------------------------
该操作可以利用函数DeleteString(),需要指定被删除项的位置,如:
m_cbExample.DeleteString(nIndex);
也可以使用函数ResetContent(),清除目前的所有项,如:
m_cbExample.ResetContent()
--------------------------------------------------------------------------------
int nIndex =m_cbExample.GetCurSel(); //当前选中的项
m_cbExample.SetCurSel(nIndex);//设置第nIndex项为显示的内容
--------------------------------------------------------------------------------
DWORDGetEditSel( ) /BOOL SetEditSel( int nStartChar, int nEndChar );
BOOL LimitText(int nMaxChars ); 设置输入框中可输入的最大字符数。
--------------------------------------------------------------------------------
ON_CBN_DBLCLK 鼠标双击
ON_CBN_DROPDOWN 列表框被弹出
ON_CBN_KILLFOCUS/ ON_CBN_SETFOCUS 在输入框失去/得到输入焦点时产生
ON_CBN_SELCHANGE列表框中选择的行发生改变
ON_CBN_EDITUPDATE输入框中内容被更新
--------------------------------------------------------------------------------
1,在Combo Box控件属性的Data标签里面添加,一行表示Combo Box下拉列表中的一行。换行用ctrl+回车。
2,在程序初始化时动态添加
如: //控件内容初始化
CString strTemp;
((CComboBox*)GetDlgItem(IDC_COMBO_CF))->ResetContent();//消除现有所有内容
for(inti=1;i<=100;i++)
{
strTemp.Format("%d",i);
((CComboBox*)GetDlgItem(IDC_COMBO_CF))->AddString(strTemp);
}
3,下拉的时候添加
如: CStringstrTemp;
intiCount=((CComboBox*)GetDlgItem(IDC_COMBO_CF))->GetCount();//取得目前已经有的行数
if(iCount<1)//防止重复多次添加
{
((CComboBox*)GetDlgItem(IDC_COMBO_CF))->ResetContent();
for(inti=1;i<=100;i++)
{
strTemp.Format("%d",i);
((CComboBox*)GetDlgItem(IDC_COMBO_CF))->AddString(strTemp);
}
}
4,删除
DeleteString(UINT nIndex )//删除指定行,
5,插入
InsertString(int nIndex, LPCTSTR lpszItem )//将行插入到指定位置
6,查找
FindString( intnStartAfter, LPCTSTR lpszItem )//可以在当前所有行中查找指定的字符传的位置,nStartAfter指明从那一行开始进行查找。
intSelectString( int nStartAfter, LPCTSTR lpszItem )//可以选中包含指定字符串的行
--------------------------------------------------------------------------------
1,选中:
intiPos=((CComboBox*)GetDlgItem(IDC_COMBO_CF))->GetCurSel();//当前选中的行。
2,设置
((CComboBox*)GetDlgItem(IDC_COMBO_CF))->SetCurSel(n)//设置第n行内容为显示的内容。
--------------------------------------------------------------------------------
1取当前内容
((CComboBox*)GetDlgItem(IDC_COMBO_CF))->GetWindowText(strTemp);
2取其他行内容
((CComboBox*)GetDlgItem(IDC_COMBO_CF))->GetLBText(n,strTemp);
--------------------------------------------------------------------------------
通常要判断控件是否获得了焦点,可以用GetFocus()函数
例如:if(GetFocus()==GetDlgItem(IDC_EDIT_VALUE2))//判断焦点是否在编辑框IDC_EDIT_VALUE2内。
但是combobox 的焦点不同,因为它是由edit和listbox两部分组成的
所以获得焦点要用GetParent():if((GetFocus()->GetParent())==GetDlgItem(IDC_COMBO_CF))
--------------------------------------------------------------------------------
1,首先要知道两点:一、那就是在设计界面里,点击一下Combo Box的下拉箭头,此时出现的调整框就是Combo Box的下拉调整框。
2,二、属性里有个 No integral height 钩选项,表示最大长度为设计长度,如果实际内容比设计长度多,就出现滚动条,少就以实际长度显示。
--------------------------------------------------------------------------------
控件风格
含义
CBS_AUTOHSCROLL
使编辑框组件具有水平滚动的风格.
CBS_DROPDOWN
指定一个下拉式组合框.
CBS_DROPDOWNLIST
指定一个下拉列表式组合框.
CBS_HASSTRINGS
指定一个含有字符串的自绘式组合框.
CBS_OEMCONVERT
使编辑框组件中的正文可以在ANSI字符集和OEM字符集之间相互转换。这在编辑框中包含文件名时是很有用的。
CBS_OWNERDRAWFIXED
指定自绘式组合框,即由父窗口负责绘制列表框的内容,并且列表项有相同的高度.
CBS_OWNERDRAWVARIABLE
指定自绘式组合框,并且列表项有不同的高度.
CBS_SIIMPLE
指定一个简易式组合框.
CBS_SORT
自动对列表框组件中的项进行排序.
CBS_DISABLENOSCROLL
使列表框在不需要滚动时显示一个禁止的垂直滚动条.
CBS_NOINTEGRALHEIGHT
组合框的尺寸由应用程序而不是Windows指定.通常,由Windows指定尺寸会使列表项的某些部分隐藏起来.
CBS_SIMPLE、CBS_DROPDOWN和CBS_DROPDOWNLIST分别用来将组合框指定为简易式、下拉式和下拉列表式.一般还要为组合框指定WS_CHILD、WS_VISIBLE、WS_TABSTOP、WS_VSCROLL和CBS_AUTOHSCROLL风格.如果要求自动排序,还应指定CBS_SORT风格.
对于用对话框模板编辑器创建的组合框控件,可以在控件的属性对话框中指定上表中列出的控件风格。例如,在属性对话框中选择Dropdown,相当于指定了CBS_DROPDOWN.
在MFC基于对话框的应用程序中,Group Box组合框就是将组合框内的控件标识为一组控件来使用:只能选择其中的一个控件(如性别选择,当选择男时,女不被选择;当选择女时男不被选择)。
GroupBox属性设置也有多种。在直接使用Group Box本身时,一般将Group Box拖进对话框后需要重新设置的属性就是标题。如用Group Box做性别选择之用需要将其标题修改为“性别选择”时则需要进行如下操作:选中组合框-->右键-->Caption:性别选择。
当把Group Box拉进对话框后需要对Group Box的属性进行设置,使之具有组合框的功能。除了修改其标题之外,还需要将Group Box的Group属性设置为true。同时对组合框内的控件的group属性不可设置为true。
例:MFC对话框中拖进Group Box,将其标题修改为“性别选择”,并再拖进两个Radio Button按钮进去,一个作为“男”,一个作为“女”,使之用户在同一时刻只能选择“男”或者“女”按钮。
图1 Group Box 及 其内控件属性设置
GroupBox 、Radio Button等控件的属性都是通过选中其本身后通过右键选择属性来进行设置的。其它的属性可根据实际需要再进行设置。
程序运行后,在未对Group Box内控件进行选择时需要对Group Box内的控件进行一个默认的选择。这个只需要在程序运行时添加一条语句即可:
[cpp] view plaincopyprint?
CWnd* GetDlgItem(
intnID
) const;
GetDlgItem函数功能:根据控件的ID返回一个指向此ID控件的指针。
GetDlgItem参数含义:nID表示在一个对话框内的子控件的ID。
void SetCheck(
intnCheck
);
SetCheck函数功能:设置或者重置Radio 按钮或者Check 按钮被选择与否的状态。
SetCheck函数参数功能:指定按钮的状态,具体宏值可在VS2010中按下F1查看。
Group Box内控件的状态随时都有可能被更改。那么如何检测当前状态时哪一个按钮被选择了呢?在Group Box中,往往不同的按钮被选择对应着不同执行的程序段。
可以使用CWnd类中的GetChecedRadioButton方法来判断Group Box内控件的选择与否。
intGetCheckedRadioButton(
intnIDFirstButton,
intnIDLastButton
);
函数功能:检测当前被选中radio按钮的id.
函数参数含义:nIDFirstButton表示Group Box内的第一个控件的ID,nIDLastButton表示在Group Box中最后一个按钮的ID。
函数返回值:返回当前被选中radio的ID值。如果木有被选中的按钮就返回0.
最简单的一个应用就是上面提到的组合框中选中了“女”点击确定后判断选择的性别:
图2 选择
然后弹出选择了女的标题框( 作为以下消息框的标题 )
图3 判断选择了女
那么“主角信息确定”按钮对应的消息响应函数代码为:
[cpp] view plaincopyprint?
void CDMarriageMatchDlg::OnBnClickedConfirm()
{
BOOL m_Value;
if( ( m_Value = UpdateData( TRUE ) ) ){
UINT nSex;
nSex = GetCheckedRadioButton( IDC_GG, IDC_MM );
switch ( nSex ){
case IDC_GG:
MessageBox( NULL, _T("GG"), MB_OK );
break;
case IDC_MM:
MessageBox( NULL, _T("MM"), MB_OK );
break;
default:
MessageBox( NULL, _T("GGMM"), MB_OK );
}
}
}
“主角信息确定”按钮消息响应(函数)是通过GetCheckedRadioButton函数来实现。UpdateData(TRUE )是用来判断消息是否更新成功,可不管这个语句的含义
Note Over。
PictureControl
一、创建MFC
1 首先创建一个MFC对话框应用程序(Dialog-based Application);
2 点击OK -- Next进入下一步,在这里我们创建一个Dialog-based Application,大部分选项按默认设置就行,不过最下面的“Use Unicodelibraries”最好去掉。如果勾选了这
个选项,程序代码就会使用16bit的Unicode字符集来编码,但是很多函数虽然使用 char* (ASCIIstings) 类型字符,而将字符串从 Unicode 转换到 ASCII 是非常麻烦的。使用 Unicode 在编译时可能会遇到下列错误:
cannot convert parameter 1 from 'CString' to 'const char *'
cannot convert from 'const char [11]' to 'LPCWSTR'这意味着在Unicode和Multi-byte字符串的转换中出现了问题。
故这里的去掉“Use Unicodelibraries”选项。
二、编写代码
1 打开工程文件,进入资源视图:testPicControl(工程名称)->testPicControl.rc->Dialog双击IDD_TESTPICCONTROL_DIALOG,可以看到一个初始的GUI界面,往里面添加两个 Button 和一个 Picture 控件
选中单个控件、右击选择属性(Properties),可以看到控件的ID号,这个号可以自行编辑,例如 Picture 控件的 ID 号我设置为 IDC_SHOWIMAGE,这个 ID 号在后面的图像显示函数中要用到。
2 在项目属性中加载lib文件:菜单Project -> Properties ->Configuration Properties -> Linker –> Input -> additional dependencies中加入 cxcore200.libcv200.lib highgui200.lib。
3 然后在testPicControl.h 的 #include"resource.h" 代码行下面加入如下代码:
#include "cv.h"
#include "highgui.h"
#define IMAGE_WIDTH 256
#define IMAGE_HEIGHT 256
#define IMAGE_CHANNELS 3
4 在类视图面板右击 CtestPicControlDlg,选择 Add –> Add Variable,添加一个 IplImage* 类型的变量 TheImage;再点击CtestPicControlDlg,在下面窗口的列表中双击 OnInitDialog,在“// TODO: Add extra initialization here”下面添加 TheImage 的初始化代码:
CvSize ImgSize;
ImgSize.height = IMAGE_HEIGHT;
ImgSize.width = IMAGE_WIDTH;
TheImage = cvCreateImage( ImgSize,IPL_DEPTH_8U, IMAGE_CHANNELS );
5 然后双击 OnPaint,在 if(IsIconic())…的 else 里添加以下代码,用来重绘窗口:
CDialog::OnPaint(); // 重绘对话框
CDialog::UpdateWindow(); // 更新windows窗口,如果无这步调用,图片显示还会出现问题 ShowImage( TheImage, IDC_ShowImg); // 重绘图片函数
接着在CtestPicControlApp 下面的成员列表中双击 InitInstance,在两个“// TODO: Place code here to handle whenthe dialog is…”下面添加:
cvReleaseImage( &dlg.TheImage );
即按下“OK”或“Cancel”时,释放TheImage占用的内存。
6 写读取和处理图片的功能函数。
回到资源视图,右键按钮 ReadImage,选择 Add Event Handler,建立按钮点击的消息响应程序:OnBnClickedReadimage,主要的响应操作包括弹出对话框选择图片文件、读入图片文件、对图片统一缩放至256*256的大小、显示图像,代码如下:
// TODO: 在此添加控件通知处理程序代码
CFileDialog dlg(
TRUE, _T("*.bmp"),NULL,
OFN_FILEMUSTEXIST |OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
_T("image files (*.bmp;*.jpg) |*.bmp; *.jpg | All Files (*.*) |*.*||"), NULL
); // 选项图片的约定
dlg.m_ofn.lpstrTitle = _T("Open Image"); // 打开文件对话框的标题名
if( dlg.DoModal() != IDOK ) // 判断是否获得图片
return;
CString mPath = dlg.GetPathName(); // 获取图片路径
IplImage* ipl = cvLoadImage( mPath, 1 ); // 读取图片、缓存到一个局部变量 ipl 中
if( !ipl ) // 判断是否成功读取图片
return;
if( TheImage ) // 对上一幅显示的图片数据清零
cvZero( TheImage );
ResizeImage( ipl ); // 对读入的图片进行缩放,使其宽或高最大值者刚好等于 256,再复制到 TheImage 中
ShowImage( TheImage, IDC_SHOWIMAGE); // 调用显示图片函数
cvReleaseImage( &ipl ); // 释放 ipl 占用的内存
7 在上面的代码中包含了两个新的成员函数 ResizeImage 和 ShowImage,前者的作用是对读入的不同大小的图像进行缩放,再通过设置 ROI 的方式将图像存入 256*256 的 TheImage 中;后者是将图像 TheImage 显示到图片显示控件 IDC_ShouImg 窗口的正中部位。为了实现这两个功能,首先在类视图面板右击CtestPicControlDlg,选择 Add –> AddFunction,创建两个函数:void ShowImage(IplImage* img, UINT ID ) 和 voidResizeImage(IplImage* img)。以下是这两个函数的实现代码:
void CtestPicControlDlg::ShowImage(IplImage*img, UINT ID)// ID 是Picture Control控件的ID号
{
CDC* pDC=GetDlgItem(ID)->GetDC();// 获得显示控件的 DC
HDC hDC=pDC->GetSafeHdc();// 获取 HDC(设备句柄) 来进行绘图操作
CRect rect;
GetDlgItem(ID)->GetClientRect(&rect);
int rw=rect.right-rect.left; // 求出picture control的宽和高
int rh=rect.bottom-rect.top;
int iw=img->width; // 读取图片的宽和高
int ih=img->height;
int tx = (int)(rw - iw)/2; // 使图片的显示位置正好在控件的正中
int ty = (int)(rh - ih)/2;
SetRect( rect, tx, ty, tx+iw, ty+ih );
CvvImage cimg;
cimg.CopyOf(img); // 复制图片
cimg.DrawToHDC(hDC,&rect);
ReleaseDC(pDC);
}
void CtestPicControlDlg::ResizeImage(IplImage*img)
{
// 读取图片的宽和高
int w = img->width;
int h = img->height;
// 找出宽和高中的较大值者
int max = (w > h)? w: h;
// 计算将图片缩放到TheImage区域所需的比例因子
float scale = (float) ( (float) max / 256.0f );
// 缩放后图片的宽和高
int nw = (int)( w/scale );
int nh = (int)( h/scale );
// 为了将缩放后的图片存入 TheImage 的正中部位,需计算图片在 TheImage 左上角的期望坐标值
int tlx = (nw > nh)? 0: (int)(256-nw)/2;
int tly = (nw > nh)? (int)(256-nh)/2: 0;
// 设置 TheImage 的 ROI 区域,用来存入图片 img
cvSetImageROI( TheImage, cvRect( tlx, tly, nw, nh) );
// 对图片 img 进行缩放,并存入到 TheImage 中
cvResize( img, TheImage );
// 重置 TheImage 的 ROI 准备读入下一幅图片
cvResetImageROI( TheImage );
}
代码中所用到的一些MFC相关知识(待续)
1 OnInitDialog();
2 CDialog::OnPaint(); // 重绘对话框
CDialog::UpdateWindow(); // 更新windows窗口,如果无这步调用,图片显示还会出现问题
3 CFileDialog dlg(TRUE, _T("*.bmp"), NULL,
OFN_FILEMUSTEXIST |OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
_T("image files (*.bmp;*.jpg) |*.bmp; *.jpg | All Files (*.*) |*.*||"), NULL
);
dlg.m_ofn.lpstrTitle = _T("Open Image"); // 打开文件对话框的标题名
if( dlg.DoModal() != IDOK ) // 判断是否获得图片
CString mPath = dlg.GetPathName();
4 CDC* pDC=GetDlgItem(ID)->GetDC();// 获得显示控件的 DC
HDC hDC=pDC->GetSafeHdc();// 获取 HDC(设备句柄) 来进行绘图操作
CRect rect;
GetDlgItem(ID)->GetClientRect(&rect);
SetRect( rect, tx, ty, tx+iw, ty+ih );
5 CvvImage cimg;
cimg.CopyOf(img); // 复制图片
cimg.DrawToHDC(hDC,&rect);
6 cvSetImageROI( TheImage, cvRect( tlx,tly, nw, nh) );
cvResize( img, TheImage );
cvResetImageROI( TheImage );
RadioButton
基础介绍:
radio button通常都是成组使用的,在一组里面是互斥的。
分组的原则是:
1、首先将RadioButton控件定好Tab顺序,具体方法:工具栏“格式”—>“Tab键顺序”选项选中,然后按照预定的顺序依次点击对话框上面的RadioButton按钮,Tab键顺序设定完成。
2、按照上面的Tab键顺序进行分组,然后设定每组第一个RadioButton的Group属性为TRUE,分组完成,即从当前设置Group属性为TRUE的RadioButton开始直到碰到下一个选上Group属性的RadioButton的前一个RadioButton为一个组。
3、为单选控件定义Control变量或Value变量,每组只能定义一个,通过设定值来确定哪一个RadioButton被选中,其中-1表示该组均不被选中,0表示该组第一个RadioButton被选中,1表示第二个……)。
环境:Visual Studio2010
以对话框为例,工程名称为:RadioButtonInstance。此工程中,共创建四组RadioButton,记着设定Tab键顺序以及为每组第一个RadioButton设置Group属性。初始化对话框为下图:
为该工程中的RadioButton设定下面若干变量:(为单选控件定义Control变量或Value变量,每组只能定义一个)
BOOL m_Radio1;//对应于组 1 1
BOOLm_Radio3; //对应于组 2 1
BOOLm_Radio7; //对应于组 3 1
BOOLm_Radio9; //对应于组 4 1
CButtonm_RBtGroup1; //对应于组 11
CButton m_RBtGroup2; //对应于组 2 1
CButtonm_RBtGroup3; //对应于组 3 1
CButtonm_RBtGroup4; //对应于组 4 1
添加变量
方法一:利用类向导方法添加变量(略)
方法二:直接编程如下
RadioButtonInstanceDlg.h文件中:
代码部分如下
classCRadioButtonInstanceDlg : public CDialogEx
{
// 构造
public:
CRadioButtonInstanceDlg(CWnd*pParent = NULL); // 标准构造函数
// 对话框数据
enum{ IDD = IDD_RADIOBUTTONINSTANCE_DIALOG };
protected:
virtualvoid DoDataExchange(CDataExchange*pDX); // DDX/DDV 支持
// 实现
protected:
HICONm_hIcon;
// 生成的消息映射函数
virtualBOOL OnInitDialog();
afx_msgvoid OnSysCommand(UINT nID, LPARAM lParam);
afx_msgvoid OnPaint();
afx_msgHCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
BOOL m_Radio1;
BOOLm_Radio3;
BOOLm_Radio7;
BOOLm_Radio9;
CButtonm_RBtGroup1;
CButtonm_RBtGroup2;
CButtonm_RBtGroup3;
CButtonm_RBtGroup4;
};
RadioButtonInstanceDlg.cpp文件中:
voidCRadioButtonInstanceDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Radio(pDX, IDC_RADIO1, m_Radio1);
DDX_Radio(pDX,IDC_RADIO3, m_Radio3);
DDX_Radio(pDX,IDC_RADIO7, m_Radio7);
DDX_Radio(pDX,IDC_RADIO9, m_Radio9);
DDX_Control(pDX,IDC_RADIO1, m_RBtGroup1);
DDX_Control(pDX,IDC_RADIO3, m_RBtGroup2);
DDX_Control(pDX,IDC_RADIO7, m_RBtGroup3);
DDX_Control(pDX,IDC_RADIO9, m_RBtGroup4);
}
问题一:如何更改RadioButton默认值???
方法1
在定义控件变量时,默认变量初值为-1,表示此组的任何RadioButton均不被选中,如果需要改变初始默认按钮的设置情况,只需要在对话框的构造函数中把变量初值设为相应的变量值即可。下面附代码和效果图
例如 构造函数:
CRadioButtonInstanceDlg::CRadioButtonInstanceDlg(CWnd* pParent )
:CDialogEx(CRadioButtonInstanceDlg::IDD, pParent)
{
m_hIcon= AfxGetApp()->LoadIcon(IDR_MAINFRAME);
//设置RadioButton初始默认值。如果此处不设置,那么默认值均为-1,即任何一个RadioButton均不被选中
m_Radio1=-1;
m_Radio3=3;
m_Radio7=1;
m_Radio9=0;
}
效果图如下:
方法2
只需要在对话框的OnInitDialog()方法中添加下面代码中绿色的部分即可。
BOOLCRadioButtonInstanceDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于…”菜单项添加到系统菜单中。
//IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX& 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX< 0xF000);
CMenu*pSysMenu = GetSystemMenu(FALSE);
if(pSysMenu != NULL)
{
BOOLbNameValid;
CStringstrAboutMenu;
bNameValid= strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if(!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon,TRUE); // 设置大图标
SetIcon(m_hIcon,FALSE); // 设置小图标
//TODO: 在此添加额外的初始化代码
//此种方法可以为任何一个RadioButton设置其“是否check”状态,未指定的均为不选上状态
((CButton*)GetDlgItem(IDC_RADIO1))->SetCheck(TRUE);//选上
((CButton*)GetDlgItem(IDC_RADIO2))->SetCheck(FALSE);// 不选上
((CButton*)GetDlgItem(IDC_RADIO4))->SetCheck(TRUE);//选上
((CButton*)GetDlgItem(IDC_RADIO8))->SetCheck(TRUE);//选上
((CButton*)GetDlgItem(IDC_RADIO9))->SetCheck(TRUE);//选上
returnTRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
效果图如下:
方法3
单选控件每组只能定义个Control变量或Value变量。首先为每一组RadioButton关联一个Control变量,名字分别为m_RBtGroup1、m_RBtGroup2、m_RBtGroup3、m_RBtGroup4。然后设定RadioButton初始状态。
代码如下:
BOOLCRadioButtonInstanceDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于…”菜单项添加到系统菜单中。
//IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX& 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX< 0xF000);
CMenu*pSysMenu = GetSystemMenu(FALSE);
if(pSysMenu != NULL)
{
BOOLbNameValid;
CStringstrAboutMenu;
bNameValid= strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if(!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon,TRUE); // 设置大图标
SetIcon(m_hIcon,FALSE); // 设置小图标
//TODO: 在此添加额外的初始化代码
// //此种方法只能为每一组的第一个RadioButton设置其“是否check”状态
m_RBtGroup1.SetCheck(FALSE);
m_RBtGroup2.SetCheck(TRUE);
m_RBtGroup3.SetCheck(TRUE);
m_RBtGroup4.SetCheck(TRUE);
returnTRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
效果图如下:
问题二:如何获取RadioButton当前状态???
获取RadioButton是否选中的状态方法如下:
方法一:可以使用GetCheck()获取状态。
例如:
((CButton *)GetDlgItem(IDC_RADIO2))->GetCheck();//返回1表示选上,0表示没选上
方法二:获取状态很简单,UpdateData(TRUE)后判断m_nRadio1的值即可。
例如:
UpdateData(TRUE);
CString m_R1;
m_R1.Format(_T("thevalue of m_Radio1 is %d"),m_Radio1);
AfxMessageBox(m_R1);
可以根据需要为RadioButton添加鼠标单击事件
问题三:如何为RadioButton添加单击消息函数???
方法一:双击RadioButton按钮,进入消息函数,然后添加相应的代码。
方法二:用ClassWizard生成各单选按钮的单击消息函数,然后添加相应的代码。
例如:
voidCRadioButtonInstanceDlg::OnBnClickedRadio1()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio1=0;//选中此组的第一个RadioButton
CStringm_R1;
m_R1.Format(_T("thevalue of m_Radio1 is %d"),m_Radio1);
AfxMessageBox(m_R1);
}
voidCRadioButtonInstanceDlg::OnBnClickedRadio2()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio1=1;//选中此组的第二个RadioButton
UpdateData(FALSE);
}
voidCRadioButtonInstanceDlg::OnBnClickedRadio3()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio3=0;//选中此组的第一个RadioButton
UpdateData(FALSE);
}
voidCRadioButtonInstanceDlg::OnBnClickedRadio4()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio3=1;//选中此组的第二个RadioButton
UpdateData(FALSE);
}
voidCRadioButtonInstanceDlg::OnBnClickedRadio5()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio3=2;//选中此组的第三个RadioButton
UpdateData(FALSE);
}
voidCRadioButtonInstanceDlg::OnBnClickedRadio6()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio3=3;//选中此组的第四个RadioButton
UpdateData(FALSE);
}
voidCRadioButtonInstanceDlg::OnBnClickedRadio7()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio7=0;//选中此组的第一个RadioButton
UpdateData(FALSE);
}
voidCRadioButtonInstanceDlg::OnBnClickedRadio8()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio7=1;//选中此组的第二个RadioButton
UpdateData(FALSE);
}
voidCRadioButtonInstanceDlg::OnBnClickedRadio9()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio9=0;//选中此组的第一个RadioButton
UpdateData(FALSE);
}
voidCRadioButtonInstanceDlg::OnBnClickedRadio10()
{
//TODO: 在此添加控件通知处理程序代码
m_Radio9=1;//选中此组的第二个RadioButton
UpdateData(FALSE);
}
其实MFC的单选控件radio button是可以通过关联成员变量来操控的,但是由于使用类向导有些代码是自动生成的,有时会出现莫名的错误。当然如果对MFC的机制很清楚,可以手动写代码关联。现在个人比较倾向于使用Cwnd的成员函数来操控单选控件。
一、设置Radio button控件选中不选中
IDC_RADIO1为控件的ID,如果需要设置IDC_RADIO1控制默认为选择或者不选中就需要在OnInitDialog()函数调用下面的函数。
((CButton *)GetDlgItem(IDC_RADIO1))->SetCheck(TRUE);//选上
((CButton *)GetDlgItem(IDC_RADIO1))->SetCheck(FALSE);//不选上
二、Radio button控件分组
每组的第一个单选按钮设置属性Group设置为true,从第1个Group属性为true的Radio button控件到下一个group属性为true的单选按钮的前一个单选按钮为一组(按照Tab顺序)。每一组可以设置一个成员变量与之关联。
每组的第一个单选按钮设置属性:Group,Tabstop,Auto;其余按钮设置属性Tabstop,Auto。如:
Radio1、Radio2、Radio3为一组,Radio4、Radio5为一组
设定Radio1属性:Group,Tabstop,Auto
设定Radio2属性:Tabstop,Auto
设定Radio3属性:Tabstop,Auto
设定Radio4属性:Group,Tabstop,Auto
设定Radio5属性:Tabstop,Auto
三、用CWnd成员函数同时设置组和默认选中按钮
使用CWnd成员函数CheckRadioButton既可以设置组也可以同时设置默认选中项。
void CheckRadioButton( int nIDFirstButton, intnIDLastButton, intnIDCheckButton )
参数:
nIDFirstButton 指定组中第一个单选按钮的整数标识符。nIDLastButton 指定组中最后一个单选按钮的整数标识符。nIDCheckButton 指定了要选中的单选按钮的整数标识符。
说明:
从一组按钮中选择(加入检查标记)一个单选按钮并清除(清除检查标记)同组中其它单选按钮。CheckRadioButton函数向指定的单选按钮发送一条BM_SETCHECK消息。
四、获取单选按钮状态
调用类CWnd的成员函数GetCheckedRadioButton可以获取Radio button的状态
int GetCheckedRadioButton( int nIDFirstButton, intnIDLastButton )
第一个参数nIDFirstButton是同一组中的第一个单选钮控件的ID
第二个参数nIDLastButton是同一组中最后一个单选钮控件的ID
成员函数GetCheckedRadioButton返回指定组中第一个所选中的单选钮(在正常情况下仅应当有一个单钮被选中)的ID,如果没有按钮被选中,则返回0。这里需要注意的是,成员函数GetCheckedRadioButton被没有要求两个参数nIDFirstButton和nIDLastButton所指定的控件一定位于同一组中。
若干个单选钮是否属于同一组是以其Tab顺序来排定的,而GetCheckedRadioButton函数是以ID顺序来检查按钮的选定状态的。因此,如果传递给函数GetCheckedRadioButton的第一个参数的值大于第二个参数的值时,其返回值总是为0,而事实上,由这两个参数指定的单选钮的TAB顺序可能恰恰相反。因此,一般情况下我们应该尽量保证同一组单选钮的资源ID是连续递增的。通常这些资源ID是在头文件Resource.h中定义的。如果你同一组的单选钮不是一次创建的,那么它们的资源ID可能不是连续递增的,甚至可能是相反的。这时我们可以手动的修改资源头文件中的宏定义,以保证如GetCheckedRadioButton之类的成员函数得到正确的结果。
同时,这也说明一点,即使用GetCheck一个一个控件的检查各单选钮的选中状态要安全得多。
int i_check = ((CButton*)GetDlgItem(IDC_RADIO1))->GetCheck();//获取IDC_RADIO1控件选择状态
返回值(i_check的值):0 按钮处于未选中状态1 按钮处于选中状态2 按钮状态不定(仅当按钮风格为BS_3STATE或BS_AUTO3STATE时)如果按钮是其它风格,则返回0。
五、关联成员变量
强调一点Radio button控件要与int型变量相连前必须对其进行分组。关于Radio button和int型变量相连的方法简单介绍如下。
对Radio button控件 Radio1,Radio2,Radio3 ……分组后,假设与int成员变量 m_nRadio 关联,如果您是用类向导完成关联,那么对话框的构造函数会把 m_nRadio 值初始化为0.
假设 Radio1,Radio2,Radio3 …… tab键顺序是由小到大,那么 m_nRadio 值为1 就是 Radio1 被选中,依次类推。切记控件与变量之间值的更新需要利用 UpdateData() 函数。
CheckBox
MFC中复选框checkbox控件,至少有三种方法对其进行操作,他们是利用Cbutton成员函数GetCheck和SetCheck,第二种是利用CWnd成员函数IsDlgButtonChecked,最后就是把checkbox复选框控件与BOOL型变量相关联。
一、用CButton成员函数
MFC中复选框CheckBox的基类是CButton。那么就可以用GetDlgItem来获取复选框的指针,再用Cbutton成员函数GetCheck和SetCheck来获取和设置选中状态。
得到复选框状态的函数:
CButton*pBtn = (CButton*)GetDlgItem(IDC_CHECK_MIXI);
intstate = pBtn->GetCheck();
当state == 0时表示该复选框没有被选中;
当state == 1时表示该复选框被选中;
当state == 2时表示不确定(applies only if the button hasthe BS_3STATE or BS_AUTO3STATE style);
设置复选框状态的函数:
CButton*pBtn = (CButton*)GetDlgItem(IDC_CHECK_MIXI);
pBtn->SetCheck(1);
SetCheck(1)表示设置复选框为“选中”状态;
SetCheck(0)表示设置复选框为“未选中”状态;
SetCheck(2)设置复选框为不确定状态(This value can be used onlyif the button has the BS_3STATE or BS_AUTO3STATE style.);
二、用CWnd成员函数
CButton是从CWnd继承来,那么就可以用IsDlgButtonChecked来获取和设置check box的状态。另外MSDN上海说明IsDlgButtonChecked还可以用于单选框Radio button。
UINTIsDlgButtonChecked( int nIDButton ) const;
nIDButton为控件ID
函数功能:
该函数可以确定某个按钮控制是否有选中标志,或者三态按钮控制是否为灰色的、选中的、或两者都不是。
返回值:
使用BS_AUTOCHECKBOX、BS_AUTORADIOBUTTON、BS_AUTO3STATE、BS_CHECKBOX、BS_RADIOBUTION或BS_3STATE样式创建的按钮的返回值可以是如下值之一:
BST_CHECKED:表示按钮被选中。
BST_INDETERMINATE:表示按钮是灰色的,即为不确定状态(只有具有BS_3STATE或BS_AUTO3STATE样式的按钮才使用该值)。
BST_UNCHECKED:表示该按钮未选中(unckecked)。如果该按钮用其他任何样式,那么返回值为零。
if (BST_CHECKED == IsDlgButtonChecked( IDC_CHECK1 ) )
{
//IDC_CHECK1是CheckBox控件。
//checkbox 被选中执行相关动作
}
三、关联BOOL型成员变量
复选框CheckBox控件添加一个控件类型的BOOL值变量如:BOOL m_delete;
voidcalcuArea::DoDataExchange (CDataExchange *pDX)
{
//此代码利用类向导可以自动生成
CAcUiDialog::DoDataExchange(pDX) ;
DDX_Check(pDX,IDC_CHECK1, m_delete);
}
TabControl
1. 新建一个MFC工程,取名xyTabControl,选择Dialog based,然后Finish。
2. 删除对话框上默认添加的三个控件。添加Tab Control控件并在Property属性中设置ID为IDC_TAB1,添加变量m_tabctrl,类型为CTabCtrl。
3. 在对话框的初始化函数OnInitDialog里面添加如下代码:
m_tabctrl.InsertItem(0,"memo0"); //添加参数一选项卡 m_tabctrl.InsertItem(1,"memo1"); //添加参数二选项卡
4.在对话框资源里面添加两个对话框资源, ID分别命名为IDD_PARA1,IDD_PARA2。字体为宋体, 字号为9, style为Child, Border为None, 调整高度宽度到适中尺寸。再分别为其添加对应的基于CDialog类CPara1, CPara2。
5. 在CxyTabControlDlg类中添加两个成员变量m_para1,m_para2, 分别是两个子对话框的实例. 代码如下:
CPara2 m_para2;
CPara1 m_para1;
6. 布置IDD_PARA1和IDD_PARA2对话框 如下图:
7. 在IDD_xyTabControl_DIALOG对话框的初始化函数OnInitDialog里面添加如下代码:
//关联对话框,并且将IDC_TABTEST控件设为父窗口
m_para1.Create(IDD_PARA1,GetDlgItem(IDC_TAB1));
m_para2.Create(IDD_PARA2,GetDlgItem(IDC_TAB1));
//获得IDC_TABTEST客户区大小
CRect rs;
m_tabctrl.GetClientRect(&rs);
//调整子对话框在父窗口中的位置 rs.top += 20;
rs.bottom -=20;
rs.left += 1;
rs.right -= 2;
//设置子对话框尺寸并移动到指定位置 m_para1.MoveWindow(&rs);
m_para2.MoveWindow(&rs);
//分别设置隐藏和显示 m_para1.ShowWindow(1);
m_para2.ShowWindow(0);
//设置默认的选项卡 m_tabctrl.SetCurSel(0);
8. 添加Tab Control控件的TCN_SELCHANGE事件响应函数OnSelchangeTabtest(NMHDR* pNMHDR, LRESULT* pResult) ,函数体代码如下: int CurSel =m_tabctrl.GetCurSel();
switch(CurSel)
{
case 0:
m_para1.ShowWindow(true);
m_para2.ShowWindow(false);
break;
case 1:
m_para1.ShowWindow(false);
m_para2.ShowWindow(true);
break;
default:
;
*pResult = 0;
}
9.好了,完成,这个小程序很简单,效果如下图:
TreeControl
1. InsertItem 添加节点
参数: 文字,图标,父节点
返回: HTREEITEM
示例: 添加一系列节点
HTREEITEM hItem= m_Tree.InsertItem("root",NULL,NULL);///root就是节点的标题
int i,j;
for(i=0;i<4;i++)
{
HTREEITEMhSubItem = m_Tree.InsertItem("item",NULL,NULL,hItem);
for(j=0;j<3;j++)
{
m_Tree.InsertItem("subitem",NULL,NULL,hSubItem);
}
}
InsertItem函数的第一个参数就是设置他的节点标题
2. ModifyStyle 设置风格
参数: 取消的风格,增加的风格
示例: 在对话框初始化时设置风格
BOOLCMfc1Dlg::OnInitDialog(){
//…
m_Tree.ModifyStyle(NULL,TVS_HASBUTTONS| TVS_HASLINES | TVS_LINESATROOT);
}
控件风格
含义
TVS_HASLINES
在父项与子项间连线以清楚地显示结构.
TVS_LINESATROOT
只在根部画线.
TVS_HASBUTTONS
显示带有"+"或"-"的小方框来表示某项能否被展开或已展开.
TVS_EDITLABELS
用户可以编辑表项的标题.
TVS_SHOWSELALWAYS
即使控件失去输入焦点,仍显示出项的选择状态.
TVS_DISABLEDRAGDROP
不支持拖动操作.
3. DeleteItem 删除节点
4. DeleteAllItems 删除全部节点
5. Expand 展开/收缩节点
参数: 节点HTREEITEM,展开/收缩
示例:
m_Tree.Expand(hItem,TVE_EXPAND);
/
6. CTreeCtrl的概述cc++vc
CTreeCtrl在三种不同情况下创建的方式
1、如果要在对话框窗口上创建树形控件,你需要在对话框类中定义一个CTreeCtrl类型的成员变量。
2、如果树形控件是一个子窗口,你可以使用CTreeCtrl::Create()来构建树形空间对象。
3、如果你使用了CViewTree对象,那么你需要使用CViewTree::GetTreeCtrl()获得对树形控件的引用
如果你想在你的控件中使用图像,需要通过CImageList::SetImageList()来设置一个图像列表。你也可以通过使用CTreeCtrl::SetIndent()设置子项缩进的宽度。一个最好的使用这些函数的时机是在CDialog::OnInitDialog()或CView::OnInitalUpdate()中。
可以通过调用CTreeCtrl::InsertItem()向树形控件中添加数据,每次添加一个数据项。这个函数将返回一个指向这个数据项的句柄,这个句柄在后面会使用到,例如在后面添加这个数据项的子数据项的时候。一个最好的使用这个函数的时机是在CDialog::OnInitDialog()或CView::OnInitalUpdate()中。
当用户和树形控件交互时,它将会发送不同的通知消息。你可以通过在控件窗口的消息映射表中添加ON_NOTIFY_REFLECT宏或在控件窗口的父窗口的消息映射表中添加ON_NOTIFY来指定一个函数处理每个你想处理的消息。
通过调用树形控件不同的Set成员函数去设置它的值,包括子数据项缩进宽度、文本、图像或者和控件相关的数据。
使用不同的Get成员函数来检查控件的内容。你也可以用允许你接受指定数据项的父项、子项和兄弟项的函数得到树形控件的内容。你甚至可以存储某一结点的子项。
当你在处理这个控件时,确定它确实被销毁。如果树形控件是在一个对话框中或视图中,它和CTreeCtrl对象会自动被销毁,如果不是,你需要保证销毁控件和CTreeCtrl对象
//
7. CTreeCtrl的一些操作
树形控件可以用于树形的结构,其中有一个根接点(Root)然后下面有许多子结点,而每个子结点上有允许有一个或多个或没有子结点。MFC中使用CTreeCtrl类来封装树形控件的各种操作。通过调用
BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );创建一个窗口,dwStyle中可以使用以下一些树形控件的专用风格:
TVS_HASLINES 在父/子结点之间绘制连线
TVS_LINESATROOT 在根/子结点之间绘制连线
TVS_HASBUTTONS 在每一个结点前添加一个按钮,用于表示当前结点是否已被展开
TVS_EDITLABELS 结点的显示字符可以被编辑
TVS_SHOWSELALWAYS 在失去焦点时也显示当前选中的结点
TVS_DISABLEDRAGDROP 不允许Drag/Drop
TVS_NOTOOLTIPS 不使用ToolTip显示结点的显示字符
在树形控件中每一个结点都有一个句柄(HTREEITEM),同时添加结点时必须提供的参数是该结点的父结点句柄,(其中根Root结点只有一个,既不可以添加也不可以删除)利用
HTREEITEMInsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT, HTREEITEMhInsertAfter = TVI_LAST );可以添加一个结点,pszItem为显示的字符,hParent代表父结点的句柄,当前添加的结点会排在hInsertAfter表示的结点的后面,返回值为当前创建的结点的句柄。下面的代码会建立一个如下形式的树形结构:
+--- Parent1
+---Child1_1
+---Child1_2
+---Child1_3
+--- Parent2
+--- Parent3
/*假设m_tree为一个CTreeCtrl对象,而且该窗口已经创建*/
HTREEITEMhItem,hSubItem;
hItem =m_tree.InsertItem("Parent1",TVI_ROOT);
在根结点上添加Parent1
hSubItem =m_tree.InsertItem("Child1_1",hItem);
//在Parent1上添加一个子结点
hSubItem =m_tree.InsertItem("Child1_2",hItem,hSubItem);
//在Parent1上添加一个子结点,排在Child1_1后面
hSubItem =m_tree.InsertItem("Child1_3",hItem,hSubItem);
hItem =m_tree.InsertItem("Parent2",TVI_ROOT,hItem);
hItem =m_tree.InsertItem("Parent3",TVI_ROOT,hItem);
如果你希望在每个结点前添加一个小图标,就必需先调用CImageList*SetImageList( CImageList * pImageList, int nImageListType );指明当前所使用的ImageList,nImageListType为TVSIL_NORMAL。在调用完成后控件中使用图片以设置的ImageList中图片为准。然后调用
HTREEITEMInsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent= TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST);添加结点,nImage为结点没被选中时所使用图片序号,nSelectedImage为结点被选中时所使用图片序号。下面的代码演示了ImageList的设置。
/*m_list 为CImageList对象
IDB_TREE 为16*(16*4)的位图,每个图片为16*16共4个图标*/
m_list.Create(IDB_TREE,16,4,RGB(0,0,0));
m_tree.SetImageList(&m_list,TVSIL_NORMAL);
m_tree.InsertItem("Parent1",0,1);
//添加,选中时显示图标1,未选中时显示图标0
此外CTreeCtrl还提供了一些函数用于得到/修改控件的状态。
HTREEITEMGetSelectedItem( );将返回当前选中的结点的句柄。BOOL SelectItem(HTREEITEM hItem );将选中指明结点。
BOOLGetItemImage( HTREEITEM hItem, int& nImage, int& nSelectedImage ) /BOOL SetItemImage( HTREEITEM hItem, int nImage, int nSelectedImage )用于得到/修改某结点所使用图标索引。
CStringGetItemText( HTREEITEM hItem ) /BOOL SetItemText( HTREEITEM hItem, LPCTSTRlpszItem );用于得到/修改某一结点的显示字符。
BOOL DeleteItem(HTREEITEM hItem );用于删除某一结点,BOOL DeleteAllItems();将删除所有结点。
此外如果想遍历树可以使用下面的函数:
HTREEITEMGetRootItem( );得到根结点。
HTREEITEMGetChildItem( HTREEITEM hItem );得到子结点。
HTREEITEMGetPrevSiblingItem/GetNextSiblingItem( HTREEITEM hItem );得到指明结点的上/下一个兄弟结点。
HTREEITEMGetParentItem( HTREEITEM hItem );得到父结点。
树形控件的消息映射使用ON_NOTIFY宏,形式如同:ON_NOTIFY( wNotifyCode, id, memberFxn ),wNotifyCode为通知代码,id为产生该消息的窗口ID,memberFxn为处理函数,函数的原型如同void OnXXXTree(NMHDR* pNMHDR, LRESULT*pResult),其中pNMHDR为一数据结构,在具体使用时需要转换成其他类型的结构。对于树形控件可能取值和对应的数据结构为:
TVN_SELCHANGED 在所选中的结点发生改变后发送,所用结构:NMTREEVIEW
TVN_ITEMEXPANDED 在某结点被展开后发送,所用结构:NMTREEVIEW
TVN_BEGINLABELEDIT 在开始编辑结点字符时发送,所用结构:NMTVDISPINFO
TVN_ENDLABELEDIT 在结束编辑结点字符时发送,所用结构:NMTVDISPINFO
TVN_GETDISPINFO 在需要得到某结点信息时发送,(如得到结点的显示字符)所用结构:NMTVDISPINFO
关于ON_NOTIFY有很多内容,将在以后的内容中进行详细讲解。
关于动态提供结点所显示的字符:首先你在添加结点时需要指明lpszItem参数为:LPSTR_TEXTCALLBACK。在控件显示该结点时会通过发送TVN_GETDISPINFO来取得所需要的字符,在处理该消息时先将参数pNMHDR转换为LPNMTVDISPINFO,然后填充其中item.pszText。但是我们通过什么来知道该结点所对应的信息呢,我的做法是在添加结点后设置其lParam参数,然后在提供信息时利用该参数来查找所对应的信息。下面的代码说明了这种方法:
char szOut[8][3]={"No.1","No.2","No.3"};
//添加结点
HTREEITEM hItem= m_tree.InsertItem(LPSTR_TEXTCALLBACK,…)
m_tree.SetItemData(hItem,0 );
hItem =m_tree.InsertItem(LPSTR_TEXTCALLBACK,…)
m_tree.SetItemData(hItem,1 );
//处理消息
voidCParentWnd::OnGetDispInfoTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO*pTVDI = (TV_DISPINFO*)pNMHDR;
pTVDI->item.pszText=szOut[pTVDI->item.lParam];
//通过lParam得到需要显示的字符在数组中的位置
*pResult = 0;
}
关于编辑结点的显示字符:首先需要设置树形控件的TVS_EDITLABELS风格,在开始编辑时该控件将会发送TVN_BEGINLABELEDIT,你可以通过在处理函数中返回TRUE来取消接下来的编辑,在编辑完成后会发送TVN_ENDLABELEDIT,在处理该消息时需要将参数pNMHDR转换为LPNMTVDISPINFO,然后通过其中的item.pszText得到编辑后的字符,并重置显示字符。如果编辑在中途中取消该变量为NULL。下面的代码说明如何处理这些消息:
//处理消息 TVN_BEGINLABELEDIT
voidCParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO*pTVDI = (TV_DISPINFO*)pNMHDR;
if(pTVDI->item.lParam==0);//判断是否取消该操作
*pResult= 1;
else
*pResult= 0;
}
//处理消息 TVN_BEGINLABELEDIT
voidCParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO*pTVDI = (TV_DISPINFO*)pNMHDR;
if(pTVDI->item.pszText==NULL);//判断是否已经取消取消编辑
m_tree.SetItemText(pTVDI->item.hItem,pTVDI->pszText);
//重置显示字符
*pResult = 0;
}
上面讲述的方法所进行的消息映射必须在父窗口中进行(同样WM_NOTIFY的所有消息都需要在父窗口中处理)。
/************************示例代码*************************/
image.Create(IDB_BITMAP,16,10,RGB(255,0,255));//CImageListimage;
m_Tree.SetImageList(&image,TVSIL_NORMAL);
//CTreeCtrlm_Tree;
HTREEITEMhItem=m_Tree.InsertItem(_TEXT("中国"),0,1,TVI_ROOT);
HTREEITEMhSub=m_Tree.InsertItem(_TEXT("河北"),0,2,hItem);
m_Tree.InsertItem(_TEXT("石家庄"),2,3,hSub);
m_Tree.InsertItem(_TEXT("唐山"),2,4,hSub);
m_Tree.InsertItem(_TEXT("邢台"),2,5,hSub);
hSub=m_Tree.InsertItem(_TEXT("河南"),0,3,hItem);
m_Tree.InsertItem(_TEXT("少林寺"),3,4,hSub);
m_Tree.InsertItem(_TEXT("嵩山"),3,5,hSub);
hSub=m_Tree.InsertItem(_TEXT("湖北"),0,4,hItem);
m_Tree.InsertItem(_TEXT("武汉"),4,6,hSub);
hSub=m_Tree.InsertItem(_TEXT("湖南"),0,5,hItem);
m_Tree.InsertItem(_TEXT("经济改革"),5,6,hSub);
hSub=m_Tree.InsertItem(_TEXT("山西"),0,6,hItem);
m_Tree.InsertItem(_TEXT("板面"),6,7,hSub);
8.成员函数
CTreeCtrl类提供了大量的成员函数.对于常用的函数,这里结合实际应用的需要,介绍如下:
向树形视图中插入新的表项.首先应提供一个TV_INSERTSTRUCT结构并在该结构中对插入项进行描述.如果要在树形视图中显示图象,则应该先创建一个CImageList对象并使该对象包含一个位图序列,然后调用SetImageList为树形视图设置位图序列.然后调用InsertItem函数把新项插入到树形视图中.函数的声明为
CImageList* SetImageList( CImageList * pImageList, intnImageListType );
参数pImageList指向一个CImageList对象,参数nImageListType一般应为TVSIL_NORMAL.
HTREEITEM InsertItem( LPTV_INSERTSTRUCT lpInsertStruct );
参数lpInsertStruct指向一个TV_INSERTSTRUCT结构.函数返回新插入项的句柄.
用DeleteItem来删除指定项,用DeleteAllItems删除所有项.函数的声明为
BOOL DeleteItem( HTREEITEM hItem );
BOOL DeleteAllItems( );
操作成功则函数返回TRUE,否则返回FALSE.
树形视图控件会根据用户的输入自动展开或折叠子项.但有时需要在程序中展开或折叠指定项,则应该调用Expand,该函数的声明为
BOOL Expand( HTREEITEM hItem, UINT nCode );
参数hItem指定了要展开或折叠的项.参数nCode是一个标志,指定了函数应执行的操作,它可以是TVE_COLLAPSE(折叠)、TVE_COLLAPSERESET(折叠并移走所有的子项)、TVE_EXPAND(展开)或TVE_TOGGLE(在展开和折叠状态之间翻转).
要查询或设置选择项,应调用GetSelectedItem或SelectItem.函数的声明为
HTREEITEM GetSelectedItem( );
BOOL SelectItem( HTREEITEM hItem );
要对指定的项查询或设置,可调用GetItem和SetItem.用这两个功能强大的函数,几乎可以查询和设置项的所有属性,包括表项的正文、图像及选择状态.函数的声明为
BOOL GetItem( TV_ITEM* pItem );
BOOL SetItem( TV_ITEM* pItem );
参数pItem是指向TV_ITEM结构的指针,函数是通过该结构来查询或设置指定项的,在调用函数前应该使该结构的hItem成员有效以指定表项.CTreeCtrl还提供了一系列函数可完成GetItem和SetItem的部分功能,其中GetItemState、GetItemText、GetItemData、GetItemImage和ItemHasChildren函数用于查询,SetItemState、SetItemText、SetItemData和SetItemImage函数用于设置.
在使用树形视图控件时,一个经常遇到的问题是对于一个已知表项,如何找到与该项有某种关系的项,例如,父项、子项、兄弟项、下一个或前一个可见的项.利用功能强大的GetNextItem函数,可以解决这个问题.该函数也可以用来搜索具有某种状态的表项.GetNextItem在遍历树形视图时是很有用的,它的声明为
HTREEITEM GetNextItem( HTREEITEM hItem, UINT nCode );
参数hItem指定了一个项.参数nCode是一个标志,标明了与指定项的关系,nCode可以是如表6.27所示的各种标志.如果找到相关的项,函数返回该项的句柄,否则函数返回NULL.
关系标志
标志
含义
TVGN_CARET
返回当前的选择项.
TVGN_CHILD
返回指定表项的子项.
TVGN_DROPHILITE
返回拖动操作的目标项.
TVGN_FIRSTVISIBLE
返回第一个可见项.
TVGN_NEXT
返回指定项的下一个兄弟项(Sibling Item).
TVGN_NEXTVISIBLE
返回指定项的后一个可见项.
TVGN_PARENT
返回指定项的父项.
TVGN_PREVIOUS
返回指定项的前一个兄弟项.
TVGN_PREVIOUSVISIBLE
返回指定项的前一个可见项.
TVGN_ROOT
返回位于最高层(根位置)的第一个表项.
CTreeCtrl类提供了一系列的成员函数来完成GetNextItem的某一项功能,包括GetRootItem、GetFirstVisibleItem、GetNextVisibleItem、GetPrevVisibleItem、GetChildItem、GetNextSiblingItem、GetPrevSiblingItem、GetParentItem、GetSelectedItem和GetDropHilightItem.
ProgressControl
1.简单应用
m_progress->GetPos(); //获取进度条的当前位置
m_progress->GetRange(int min,intmax); //获取进度条控件的范围的下限和上限
m_progress->OffsetPos(int nPos); //用指定的增量推进进度条控件的当前位置,重绘进度条反映新位置
m_progress->SetBkColor(COLORREFclrNew); //设定进度条的背景颜色
m_progress->SerPos(int nPos); //设定进度条控件的当前位置,重绘进度条反映新位置
m_progress->SetRange(int min,intmax); //设定进度条控件的范围的下限和上限
m_progress->SetRange32(int min,intmax); //设定进度条控件的范围的下限和上限
m_progress->SetStep(int nStep); //指定进度条控件的步进增量
m_progress->StepIt(); //通过步进增量,推进进度条控件的当前位置,重绘进度条反映新位置
应用:
CProgressCtrl *m_progress; //头文件中声明
在OnInitDialog初始化
{
m_progress = (CProgressCtrl*)GetDlgItem(IDC_PROGRESS1);
m_progress->SetRange(0,1000);
m_progress->SetPos(0);
UINT m_timer =(UINT) SetTimer(1,200,NULL);
}
采用在定时器的消息处理函数WM_TIMER内添加不断更新进度条界面的方法
OnTimer (nIDEvent)
{
pos = pos + 50;
if(pos>500)
pos = 0;
m_Progress->SetPos(pos);
}
或者采用在某种循环体内添加不断更新进展条界面的方法
2.实现像xp启动一样的不断滚动的进度条
//当程序运行到一个地方
const int TIMER_ID=1;
SetTimer(TIMER_ID,200,NULL);
//程序继续运行,执行完某一段
//KillTimer(TIMER_ID);
int m_iProgress;
void CDlgDlg::OnTimer(UINT nIDEvent)
{
CDialog::OnTimer(nIDEvent);
if(m_iProgress<100)
{
m_iProgress+=20;
}
CProgressCtrl *pProgressCtrl =(CProgressCtrl*)this->GetDlgItem(IDC_PROGRESS1);
pProgressCtrl->SetPos(m_iProgress);
if(m_iProgress==100)
{
m_iProgress=0;
}
}
追问:
不行,只能显示3格进度~
追答:
应该是KillTimer之前,进度条不停地从0到100,你再调一下
Monthcalendar Control
这个控件跟Date Time Picker控件蛮类似.实际应该是Date Time Picker把它整合进去了,只有当点一下才出来.而
Month control直接显示在页面上.当然了它还有其他一些不一样的设置信息了.
现在来讲下常用的两个用法:设置当前选中项, 获取当前选中日期
设置默认选中项
如果什么设置也不做默认选中的是当前日期.那怎么指定选中某个日期呢.
CMonthCalCtrl m_montCtrl;
DDX_Control(pDX, IDC_MONTHCALENDAR1, m_montCtrl);
m_montCtrl.SetCurSel(CTime(2008,8,8,0,0,0,0));//指定选中2008/8/8
获取当前选中日期
//当在控件中选不同的日期时可以用如下消息来处理
ON_NOTIFY(MCN_SELCHANGE, IDC_MONTHCALENDAR1,OnMcnSelchangeMonthcalendar1)
void CMFC_Ctrl_TimeDlg::OnMcnSelchangeMonthcalendar1(NMHDR*pNMHDR, LRESULT *pResult)
{
LPNMSELCHANGE pSelChange = reinterpret_cast
CTime month;
m_montCtrl.GetCurSel(month); //获取当前日期信息
//根据日期信息做其他操作,比如可以显示不同的页面信息
*pResult = 0;
}
DateTime Picker
使用DateTimePicker控件一般是获取其时间替代手工输入带来的不便,而DateTimePicker控件既可以获取日期(2010-03-05)也可以获取时间(16:27:33),要获取日期只需要更改控件属性的格式为长日期或短日期,要获取时间则将格式更改为时间即可!
在添加控件变量时,选择其变量类型为CDateTimeCtrl(类如m_DateCtrl);在程序中定义CTime对象用来保存获取的时间,然后可以将其转换为CString类型;具体实现:
1 CTime time;2 m_DateCtrl.GetTime(time);3 CString strTime=time.Format("%Y-%m-%d") //获取到的为日期如:2010-03-05
4
5 CTime time;6 m_DateCtrl.GetTime(time);7 CString strTime=time.Format("%H:%M:%S") //获取到的为时间如:16:27:33
VC2005中DateTimePicker控件的使用
11显示年月日时分秒的当前时间
2 CDateTimeCtrlm_DateItmeCtrl_Time; 3m_DateItmeCtrl_Time.SetFormat(_T("yyyy-MM-dd HH:mm:ss"));
4 CTimeTimeTemp=TimeTemp.GetCurrentTime(); 5m_DateItmeCtrl_Time.SetTime(&TimeTemp);
6
7其中HH表示24小时制,hh表示12小时制
8 2如果声明一个控件类型为CTime的变量,那么时间将显示1970-01-01 08:00:00.
9
10 3将DateTimePicker控件声明变量类型为COleDateTime
11 COleDateTime转CString
12 CString strTime;13 COleDateTime dtTime; 14 strTime =dtTime.Format(_T("%Y-%m-%d%H:%M:%S"));
4 CString转COleDateTime(因为VS2005时间日期控件关联的Value变量默认是COleDateTime类型)
1 CString strTime =_T("2009-08-1111:22:33");
2 COleVariant VariantTime;3 VariantTime = strTime;4 VariantTime.ChangeType(VT_DATE);5 COleDateTime DataTime = VariantTime;
SetTime时参数需要为指针
1 m_ctrlMachineDeliveryDate.SetTime(&CTime::GetCurrentTime()); //将控件显示当前日期
2 m_ctrlMachineInstallDate.SetTime(&CTime::GetCurrentTime()); 3
4 5
6 CTime tDeliveryDate; 7 m_ctrlMachineDeliveryDate.GetTime(tDeliveryDate); //获取发货日期
8 pMachineInfoSet->m_MACHINE_DELIVERYDATE =tDeliveryDate; 9
10 11
12 CTime tDeliveryDate;13 tDeliveryDate = pMachineInfoSet->m_MACHINE_DELIVERYDATE; //获取记录中的日期
14 m_ctrlMachineDeliveryDate.SetTime(&tDeliveryDate); //将记录中的日期赋值到DateTime控件中显示
// 设定时间日期控件允许选择的范围
1 CTime tCurrentTime= CTime::GetCurrentTime(); 2 CTimeSpan timespanOneMonth(30,0,0,0); //这里设置为当前日期推后30天
3 CTime tEndTime = tCurrentTime +timespanOneMonth; 4 m_ctrlADTipsDaysDate.SetRange(&tCurrentTime,&tEndTime); 5 /*
6 m_dtcTm:这个是DATE TIME PICKER控件变量
7 CTimebegin_tm(1970,1,1,8,0,0),end_tm(2038,1,19,3,14,7); 8 m_dtcTm.SetRange(&begin_tm,&end_tm); 9 m_tmClock=CTime::GetCurrentTime();//设置初始值为当前时间
10 m_dtcTm.SetTime(&m_tmClock);11 //m_dtcTm.SetFormat("yyyy-MM-dd HH'时'mm'分'"); //设置字符串格式
12 m_dtcTm.SetFormat("yyyy-MM-ddHH:mm:ss");13 */
Date Time Picker控件的使用例子
http://blog.163.com/zy_tommy/blog/static/86926777201032191719177/
今天要做一个获取时间,然后可以分别修改年月日,小时,分钟,秒的一个程序…
虽然用COleDateTime类可以实现。但是我还是特别想用DateTimePicker控件,因为用这个界面问题不用考虑,而且可以弹出对话框,进行修改….
关键问题是怎么把该控件里的数字进行保存,而且修改后,下次打开能显示新的时间,日期呢?
我是要在手持仪表里实现该功能,开发环境是EVC….
经验总结: MFC中的一些控件总是用起来不怎么爽,下面我结合自己的经验与大家分享一下Date Time Picker控件的操作。
首先,不管怎么样,控件是要往窗口上拖的,所以你得准备一个对话框。
然后,把Date Time Picker拖放到对话框上。
接下来设置它的属性,右键单击该控件,弹出属性对话框。
在General属性页中,给它取个名字,其它默认就可以了。
在Styles属性页中,Format下拉框中,选择Long Date。至于为什么要选这个,根据个人需要了,它的格式是:****年**月**日;而Short Date的格式如:2007-6-7。 Right Align是默认的对齐属性,我们不管它。
另外钩上Show None和Use Spin Control属性。Show None属性,用于将日期值设为空的操作,也就是我们以后从空间得到的是空值。Use Spin Control属性,就是给你提供一个调整时间的按钮,一个上(增加),一个下(减少)。
至于Allow Edit,我的看法是有了之前的设置,这个可以不要。因为它可以编辑整行,这不是我所需要的,最好只允许用户对数字操作。
第三个属性页,如果你有兴趣,可以试试,是一些扩展的属性。
接下来,我们需要给该控件绑定一个变量,如m_leveldate,注意,该变量的种类(category)选(Control),变量类型选CDateTimeCtrl。
假如SQL数据库里有一个datetime类型的字段,我们要把它显示到控件上。
首先,把该字段读出来,保存在CString strDate变量中。
接下来的问题就是如何将这个字符串显示到 Date TimePicker中了?我们可以用一个函数来解决这个问题。设为这个函数voidSetLevelDate(CString strdatetime),先在头文件中声明,再在CPP文件中实现。函数体如下:
{
CString year,month,day;
year=strdatetime.Left(4);
strdatetime=strdatetime.Right(strdatetime.GetLength()-strdatetime.Find("-")-1);
month=strdatetime.Left(strdatetime.Find ("-"));
day=strdatetime.Right(strdatetime.GetLength()-strdatetime.Find("-")-1);
inty=atoi(year); //将字符串转换为整形
int m=atoi(month);
int d=atoi(day);
CTime time(y,m,d,0,0,0);
m_leveldate.SetTime(&time);
}
这里需要注意的是,SQL数据库里的datetime类型的格式如:1900-01-0100:00:000,而我们只要年月日,所以得分别提取出来。
另外,MFC中对时间的处理,我们需要用到几个类,如:CTime和COleDateTime,具体怎么用可以到MSDN中查。
显示实现了,那如何将Date Time Picker中的日期读到字符串中呢?我们同样利用一个函数:CStringGetLevelDate(int nID),注意这个函数是有参数,并有返回值的。参数的作用是,传递该控件的ID,也就是命名,如上面图中的IDC_19_LEVELDATE。函数体实现如下:
{
CDateTimeCtrl *pWndTemp = (CDateTimeCtrl*)GetDlgItem(nID);
CTime tempTime;
pWndTemp->GetTime(tempTime);
CString strTemp1 =tempTime.Format("%Y-%m-%d");
return strTemp1;
}
同样,函数返回值的格式如:2007-6-7,这样可以方便的插入数据库中。
那么如何使得该控件传递空值呢?我们用这样一个函数voidSetLevelDateNull(),函数实现如下:
{
COleDateTimeletimeTime = COleDateTime::GetCurrentTime();
m_leveldate.SetTime(oletimeTime); //设置为当前时间
oletimeTime.SetStatus(COleDateTime::null);//复选框不打勾
m_leveldate.SetTime(oletimeTime); //使复选框不打勾生效
}
使用该函数,可以使复选框不打钩,后面的日期为灰色不可更改的。这时,如果我们用GetLevelDate(intnID)的话,将返回一个空字符串。
那么如何检测控件是否为空呢?可以用这个函数intCheckLevelDateNull(),该函数用来得到控件的状态,返回枚举类型的值,valid = 0,invalid = 1,NULL=2。当然我们也可以把函数的返回值类型设为整形。函数实现如下:
{
COleDateTimeoletimeTime;
m_leveldate.GetTime(oletimeTime);
COleDateTime::DateTimeStatus status = oletimeTime.GetStatus();
return status;
}
SliderControl
1.控件风格
控件风格
含义
TBS_HORZ
指定一个水平轨道条.该风格是默认的.
TBS_VERT
指定一个垂直轨道条.
TBS_AUTOTICKS
在范围设定后,自动为轨道条加上刻度.
TBS_NOTICKS
轨道条无刻度.
TBS_BOTTOM
在水平轨道条的底部显示刻度,可与TBS_TOP一起使用.
TBS_TOP
在水平轨道条的顶部显示刻度,可与TBS_BOTTOM一起使用.
TBS_RIGHT
在垂直轨道条的右侧显示刻度,可与TBS_LEFT一起使用.
TBS_LEFT
在垂直轨道条的左侧显示刻度,可与TBS_RIGHT一起使用.
TBS_BOTH
在轨道条的上下部或左右两侧都显示刻度.
TBS_ENABLESELRANGE
在轨道条中显示一个选择范围.
除上表的风格外,一般还要为轨道条指定WS_CHILD和WS_VISIBLE风格.要创建一个具有刻度的水平轨道条,一般应指定风格为WS_CHILD|WS_VISIBLE|TBS_HORZ| TBS_AUTOTICKS.对于用对话框模板创建的轨道条控件,可以在控件的属性对话框中指定上表中列出的控件风格。例如,在属性对话框中选择Autoticks,相当于指定了TBS_AUTOTICKS风格.
2.成员函数
用GetRange和SetRange来查询和设置轨道条的范围,缺省的范围是0-100.函数的声明为
void GetRange( int& nMin, int& nMax ) const;
void SetRange( int nMin, int nMax, BOOL bRedraw = FALSE );
参数nMin和nMax分别是最小和最大值,参数bRedraw为TRUE时将重绘控件.
用GetPos和SetPos来查询和设置轨道条的当前值.函数的声明为
int GetPos( ) const;
void SetPos( int nPos );
用GetLineSize和SetLineSize来查询和设置在按一下左箭头键或右箭头键时滑尺的移动量,该移动量的缺省值是1个单位.函数的声明为
int GetLineSize( ) const;
int SetLineSize( int nSize );
用GetPageSize和SetPageSize来查询和设置滑尺的块移动量,块移动量是指当按下PgUp或PgDown键时滑尺的移动量.函数的声明为
int GetPageSize( ) const;
int SetPageSize( int nSize );
用SetTicFreq设置轨道条的刻度的频度.缺省的频度是每个单位都有一个刻度,在范围较大时,为了使刻度不至于过密,需要调用该函数设置一个合理的频度.函数的声明为
void SetTicFreq( int nFreq );
参数nFreq说明了两个刻度之间的间隔.
用函数SetTic来在指定位置设置刻度.Windows自动显示的刻度是均匀的,利用该函数可以人为设置不均匀的刻度,该函数的声明为
BOOL SetTic( int nTic );
用函数ClearTics来清除所有的刻度.该函数的声明为
void ClearTics( BOOL bRedraw = FALSE );
所有的控件的创建基本都是同一套道路;
第一步:.h中创建一个CSliderCtrl 类的对象;
CSliderCtrl m_ctrlSlider;
第二步:.cpp中的void CMyDlg::DoDataExchange(CDataExchange*pDX)函数进行初始化控件;//初始化控件
void CMyDlg::DoDataExchange(CDataExchange* pDX)
{
//这个函数是控件与类成员交换数据用的
CDialog::DoDataExchange(pDX);
//滑块专用
DDX_Control(pDX, IDC_SLIDER1,m_ctrlSlider);
}
第三步:在BOOL CMyDlg::OnInitDialog()函数里面进行初始化的设置;//初始化设置
BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
………
m_ctrlSlider.SetRange(0,100); //设置滑块位置的最大值和最小值
m_ctrlSlider.SetPos(30); //设置滑块的默认当前位置
}
第三步:添加事件处理函数;
初始化完毕后最后添加一个事件处理函数当调节滑块位置的时候能得到相应的数据;
1 .h中添加函数声明
afx_msg void OnNMCustomdrawSlider1(NMHDR *pNMHDR, LRESULT *pResult);
2 添加消息:
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
………
ON_NOTIFY(NM_CUSTOMDRAW, IDC_SLIDER1, OnNMCustomdrawSlider1)
………
3 函数定义;//当调节滑块的时候通过(2)就会调用这个函数;
//主窗口滑块
void CMyDlg::OnNMCustomdrawSlider1(NMHDR*pNMHDR, LRESULT *pResult)
{
int nPos = m_ctrlSlider.GetPos(); //获得滑块的当前位置
//另外做一个编辑框显示所调节的数据;
CString str="";
str.Format("%d%%",nPos);
SetDlgItemText(IDC_EDIT13,str);
}
完成
//==================================================================================
滑动条控件 (CSliderCtrl)使用说明
滑动条控制(Slider Control)也叫轨道条控制,其主要是用一个带有轨道和滑标的小窗口以及窗口上的刻度,来让用户选择一个离散数据或一个连续的数值区间。通过鼠标或键盘来进行数据的选择操作,这在WIN98/95中的很多应用程序中都可以看到,如控制面板中的鼠标等,滑动条既可以是水平方式的也可以是垂直方式的。滑动条控制的风格如下:
TBS_HORZ 滑动条是水平方向的
TBS_VERT 滑动条是垂直方向的
TBS_LEFT 滑动条位于窗口左侧
TBS_RIGHT 滑动条位于窗口右侧
TBS_TOP 滑动条位于窗口顶部
TBS_BOTTOM 滑动条位于窗口底部
TBS_BOTH 滑动条位于窗口两侧
TBS_AUTOTICKS滑动条具有刻度,默认
TBS_NOTICKS 滑动条不具有刻度
滑动条的刻度条在每一个数值位置显示一个刻度标记,如果在滑动条上显示某一数值选择区间,则应使用风格TBS_ENABLESELRANGE,此时选择区间两个不再是刻度标记,而是一个小的三角形符号。另外,使用风格TBS_NOTHUMB会使滑标消隐起来。
滑动条控制在MFC类库中被封装为CSliderCtrl控制,其主要操作是设置刻度范围、绘制刻度标记、设置选择范围和当前滑标位置等。当用户进行交互操作时,滑动条控制将向其父窗口发送消息WM_HSCROLL,所以在应用程序中应重载父窗口的OnHScroll()成员函数,以便对消息进行正确处理系统发送的通知代码、滑标位置和指向CSliderCtrl对象的指针等。由于考虑到和水平卷动杆公用同一个成员函数,OnHScroll()函数参数表中的指针变量被定义为CScrollBar*类型,由于实际上消息是由滑动条产生的,所以在程序中必须把这个指针变量强制转换为CSliderCtrl*类型。滑动条和卷动杆的消息代码和含义都非常类似如TB_BOTTOM等,所以这种处理方法比较合理。SetRange()函数用来设置范围,SetPos()函数用来设置当前位置。
(二)滑动条控制的对象结构
滑动条控制的建立方法
CsliderCtrl&SliderCtrl 建立滑动条控制对象结构
Create 建立滑动条控制对象并绑定对象
滑动条控制类CSliderCtrl::Create的调用格式如下:
BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
其中参数dwStyle用来确定滑动条控制风格;参数rect用来确定滑动条控制的大小和位置;参数pParentWnd用来确定滑动条控制的父窗口指针;参数nID用来确定滑动条控制的控制符ID值。
2、滑动条控制的类属性
滑动条控制对象的类属性包括取得滑动条大小GetLineSize、设置滑动条大小SetLineSize、取得滑动条页大小 GetPageSize、设置滑动条页大小SetPageSize、取得滑动条最大位置GetRangeMax、取得滑动条最小位置 GetRangeMin、取得滑动条范围GetRange、设置滑块最小位置SetRangeMin、设置滑块最大位置SetRangeMax、设置滑动条范围SetRange、取得滑块当前位置GetSelection、设置滑块当前位置SetSelection、取得滑动条当前位置GetPos和设置滑动条当前位置SetPos等。
3、滑动条控制的操作方法
滑动条控制的操作方法包括清除滑动条当前选择ClearSel、验证滑动条当前位置是否在最大最小位置之间VerifyPos和清除当前刻度标志ClearTics。
滑动条控制的应用技巧示例
1、利用应用程序向导AppWizard生成基于对象框的应用程序CSlidDlg;
2、在对话框中设置滑动条控制,其ID为IDC_SLIDER;
3、在对话框初始代码中增加控制的范围和位置:
(1)在SlidDlg.h中设置数据成员,用来表示滑动条的当前值:
//SlidDlg.h
class CSlidDlg:public Cdialog
{ ......//其它代码
public:
int m_nCur;
......//其它代码
}
(2)在SlidDlg.cpp中设置初始状态
BOOL CSlidDlg::OnInitDialog()
{ Cdialog::OnInitDialog();
......//其它代码
//TODO:Add extra initialization here
CSliderCtrl*pSlidCtrl=(CSliderCtrl*)GetDlgItem(IDC_SLLIDER);
pSlidCtrl->SetRange(1,5,TRUE);//设置滑动条范围
pSlidCtrl->SetPos(2);//设置滑动条位置
......//其它代码
return TRUE;
}
(3)完善滑动条的消息处理,利用类向导ClassWizard增加对话框窗口的WM_HSCROLL消息处理函数,并取得滑标所指位置值:
void CSlidDlg::OnHScroll(UINTnSBCode,UINT nPos,CScrollBar *pScrollBar)
{ //TODO:Add your message handler?
Cdialog::OnHScroll(nSBCode,nPos,pScrollBar);
CSliderCtrl*pSlidCtrl=(CSliderCtrl*)GetDlgItem(IDC_SLLIDER);
m_nCur=pSlidCtrl->GetPos();//取得当前位置值
ScrollBarControl
滚动条控件简介
滚动条大家也很熟悉了,Windows窗口中很多都有滚动条。前面讲的列表框和组合框设置了相应属性后,如果列表项显示不下也会出现滚动条。滚动条分为水平滚动条(HorizontalScroll Bar)和垂直滚动条(Vertical ScrollBar)两种。滚动条中有一个滚动块,用于标识滚动条当前滚动的位置。我们可以拖动滚动块,也可以用鼠标点击滚动条某一位置使滚动块移动。
从滚动条的创建形式来分,有标准滚动条和滚动条控件两种。像列表框和组合框设置了WS_HSCROLL 或WS_VSCROLL风格以后出现的滚动条,不是一个独立的窗口,而是这些窗口的一部分,这就是标准滚动条。而滚动条控件是一个独立的窗口,它可以获得焦点,响应某些操作。
滚动条控件的创建
MFC 也为滚动条控件的操作提供了类,即为CScrollBar类。
滚动条控件的创建依然有两种方式,一种是直接在Toolbox中将滚动条控件拖入对话框模板,然后添加控件变量使用,另一种就是用CScrollBar类的Create成员函数动态创建。这两种方式适用于不同的场合。
CScrollBar类的成员函数Create的函数原型如下:
virtual BOOL Create(
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID
);
此函数与其他控件类的Create函数原型基本相同。参数dwStyle指定滚动条控件的风格,rect指定滚动条控件的位置和尺寸,pParentWnd为指向滚动条控件父窗口的指针,nID指定滚动条控件的ID。下面鸡啄米简单介绍几个主要的滚动条控件风格,更加具体的可以查阅MSDN。
SBS_HORZ:指定滚动条为水平滚动条。如果没有指定SBS_BOTTOMALIGN或SBS_TOPALIGN风格,则滚动条的高度、宽度和位置由Create函数的rect参数给出。
SBS_VERT:指定滚动条为垂直滚动条。如果没有指定SBS_RIGHTALIGN或SBS_LEFTALIGN风格,则滚动条的高度、宽度和位置由Create函数的rect参数给出。
SBS_TOPALIGN:与SBS_HORZ配合使用。滚动条的上边缘与Create函数的rect参数指定矩形的上边缘对齐。滚动条高度为系统滚动条的默认高度。
SBS_BOTTOMALIGN:与SBS_HORZ配合使用。滚动条的下边缘与Create函数的rect参数指定矩形的下边缘对齐。滚动条高度为系统滚动条的默认高度。
SBS_LEFTALIGN:与SBS_VERT配合使用。滚动条的左边缘与Create函数的rect参数指定矩形的左边缘对齐。滚动条宽度为系统滚动条的默认宽度。
SBS_RIGHTALIGN:与SBS_VERT配合使用。滚动条的右边缘与Create函数的rect参数指定矩形的右边缘对齐。滚动条宽度为系统滚动条的默认宽度。
dwStyle参数可以是以上风格中某几个的组合,另外一般也会用到WS_CHILD、WS_VISIBLE风格。例如,创建一个水平滚动条控件,dwStyle参数应该为WS_CHILD|WS_VISIBLE|SBS_HORZ,创建垂直滚动条控件时dwStyle参数应该为WS_CHILD|WS_VISIBLE|SBS_VERT。
CScrollBar类的主要成员函数
BOOL GetScrollInfo(LPSCROLLINFO lpScrollInfo, UINT nMask = SIF_ALL);
获取的滚动条的参数信息,该信息为SCROLLINFO结构体的形式。参数lpScrollInfo为指向SCROLLINFO结构体变量的指针。SCROLLINFO结构体的定义如下:
C++代码
1. typedef struct tagSCROLLINFO {
2. UINT cbSize; // 结构的尺寸(字节为单位)
3. UINT fMask; // 说明结构中的哪些参数是有效的,可以是屏蔽值的组合,如SIF_POS|SIF_PAGE,若为SIF_ALL则整个结构都有效
4. int nMin; // 滚动范围最大值,当fMask 中包含SIF_RANGE 时有效
5. int nMax; // 滚动范围最小值,当fMask 中包含SIF_RANGE 时有效
6. UINT nPage; // 页尺寸,用来确定比例滚动框的大小,当fMask中包含SIF_PAGE时有效
7. int nPos; // 滚动框的位置,当fMask 中包含SIF_POS 有效
8. int nTrackPos; // 滚动时滚动框的位置,当fMask 中包含SIF_TRACKPOS 时有效,该参数只能查询,不能设置,最好不要用该参数来查询拖动时滚动框的位置
9. } SCROLLINFO, *LPSCROLLINFO;
10. typedef SCROLLINFO CONST *LPCSCROLLINFO;
参数nMask 的含义与SCROLLINFO 结构体中的fMask一样。该函数在获取信息成功则返回TRUE,否则返回FALSE。
BOOL SetScrollInfo(LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE);
用于设置滚动条的各种参数信息。参数lpScrollInfo为指向SCROLLINFO结构体变量的指针,参数bRedraw表示是否需要重绘滚动条,如果为TRUE,则重绘。该函数操作成功则返回TRUE,否则返回FALSE。
int GetScrollPos( ) const;
获取滚动块的当前位置。如果失败则返回0。
int SetScrollPos(int nPos, BOOL bRedraw = TRUE);
将滚动块移动到指定位置。参数nPos指定了滚动块的新位置,参数bRedraw 表示是否需要重绘滚动条,如果为TRUE,则重绘。函数返回滚动框原来的位置,若操作失败则返回0。
void GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos) const;
获取滚动条的滚动范围。参数lpMinPos指向滚动条滚动范围的最小值,参数lpMaxPos指向滚动条滚动范围的最大值。
void SetScrollRange(int nMinPos, int nMaxPos, BOOL bRedraw = TRUE);
用于指定滚动条的滚动范围。参数nMinPos 和nMaxPos 分别指定了滚动范围的最小值和最大值,两者的差不得超过32767。当两者都为0 时,滚动条将被隐藏。参数bRedraw 表示是否需要重绘滚动条,如果为TRUE,则重绘。
OnHScroll()与OnVScroll()函数
无论是标准滚动条,还是滚动条控件,滚动条的通知消息都是用WM_HSCROLL 和WM_VSCROLL消息发送出去的。对这两个消息的默认处理函数是CWnd::OnHScroll和CWnd::OnVScroll,一般需要在派生类中对这两个函数进行重载,以实现滚动功能。也就是说,假设在一个对话框中放入了一个水平滚动条,我们可以在对话框类中重载OnHScroll函数,并在OnHScroll函数中实现滚动功能。
这两个函数的声明如下:
afx_msg void OnHScroll(UINT nSBCode,UINT nPos,CScrollBar* pScrollBar);
afx_msg void OnVScroll(UINT nSBCode,UINT nPos,CScrollBar* pScrollBar);
参数nSBCode是通知消息码,主要通知码及含义的介绍下面已列出。nPos 是滚动框的位置,只有在nSBCode为SB_THUMBPOSITION或SB_THUMBTRACK时,该参数才有意义。如果通知消息是滚动条控件发来的,那么pScrollBar 是指向该控件的指针,如果是标准滚动条发来的,则pScrollBar 为NULL。
SB_BOTTOM/SB_RIGHT:滚动到底端(右端)
SB_TOP/SB_LEFT:滚动到顶端(左端)
SB_LINEDOWN/SB_LINERIGHT:向下(向右)滚动一行(列)
SB_LINEUP/SB_LINELEFT:向上(向左)滚动一行(列)
SB_PAGEDOWN/SB_PAGERIGHT:向下(向右)滚动一页
SB_PAGEUP/SB_PAGELEFT:向上(向左)滚动一页
SB_THUMBPOSITION:滚动到指定位置
SB_THUMBTRACK:滚动框被拖动。可利用该消息来跟踪对滚动框的拖动
SB_ENDSCROLL:滚动结束
CScrollBar类应用实例
讲完了基础知识,鸡啄米还是给大家一个简单的实例。例子非常简单,就是在一个对话框中加入一个水平滚动条控件和一个编辑框控件,无论滚动条控件是在滚动还是静止,编辑框中都显示滚动块的当前位置。以下是具体开发步骤:
创建一个基于对话框的MFC工程,名称设置为“Example26”。
在自动生成的对话框模板IDD_EXAMPLE26_DIALOG中,删除“TODO: Place dialog controls here.”静态文本控件、“OK”按钮和“Cancel”按钮。添加一个Horizontal Scroll Bar控件,ID设置为IDC_HORI_SCROLLBAR。再添加一个静态文本控件和一个编辑框,静态文本控件的Caption属性设为“滚动块当前位置:”,编辑框的ID设为IDC_HSCROLL_EDIT,Read Only属性设为True。此时的对话框模板如下图:
为滚动条IDC_HORI_SCROLLBAR添加CScrollBar类型的控件变量m_horiScrollbar。
在对话框初始化时,我们需要设置滚动条的滚动范围和初始位置,并在编辑框中显示初始位置,那么需要修改CExample26Dlg::OnInitDialog()函数为:
C++代码
1. BOOL CExample26Dlg::OnInitDialog()
2. {
3. CDialogEx::OnInitDialog();
5. // Add "About…" menu item tosystem menu.
7. // IDM_ABOUTBOX must be in the systemcommand range.
8. ASSERT((IDM_ABOUTBOX & 0xFFF0) ==IDM_ABOUTBOX);
9. ASSERT(IDM_ABOUTBOX < 0xF000);
11. CMenu* pSysMenu =GetSystemMenu(FALSE);
12. if (pSysMenu != NULL)
13. {
14. BOOL bNameValid;
15. CString strAboutMenu;
16. bNameValid =strAboutMenu.LoadString(IDS_ABOUTBOX);
17. ASSERT(bNameValid);
18. if (!strAboutMenu.IsEmpty())
19. {
20. pSysMenu->AppendMenu(MF_SEPARATOR);
21. pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX, strAboutMenu);
22. }
23. }
25. // Set the icon for this dialog. The framework does this automatically
26. // when the application's main window is not a dialog
27. SetIcon(m_hIcon, TRUE); // Set big icon
28. SetIcon(m_hIcon, FALSE); // Set small icon
30. // TODO: Add extra initializationhere
31. // 设置水平滚动条的滚动范围为1到100
32. m_horiScrollbar.SetScrollRange(1,100);
33. // 设置水平滚动条的初始位置为20
34. m_horiScrollbar.SetScrollPos(20);
35. // 在编辑框中显示20
36. SetDlgItemInt(IDC_HSCROLL_EDIT, 20);
38. return TRUE; // return TRUE unless you set the focus to a control
39. }
C++代码
1. void CExample26Dlg::OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
2. {
3. // TODO: Add your message handler codehere and/or call default
4. int pos = m_horiScrollbar.GetScrollPos(); // 获取水平滚动条当前位置
6. switch (nSBCode)
7. {
8. // 如果向左滚动一列,则pos减1
9. case SB_LINEUP:
10. pos -= 1;
11. break ;
12. // 如果向右滚动一列,则pos加1
13. case SB_LINEDOWN:
14. pos += 1;
15. break ;
16. // 如果向左滚动一页,则pos减10
17. case SB_PAGEUP:
18. pos -= 10;
19. break ;
20. // 如果向右滚动一页,则pos加10
21. case SB_PAGEDOWN:
22. pos += 10;
23. break ;
24. // 如果滚动到最左端,则pos为1
25. case SB_TOP:
26. pos = 1;
27. break ;
28. // 如果滚动到最右端,则pos为100
29. case SB_BOTTOM:
30. pos = 100;
31. break ;
32. // 如果拖动滚动块滚动到指定位置,则pos赋值为nPos的值
33. case SB_THUMBPOSITION:
34. pos = nPos;
35. break ;
36. // 下面的m_horiScrollbar.SetScrollPos(pos);执行时会第二次进入此函数,最终确定滚动块位置,并且会直接到default分支,所以在此处设置编辑框中显示数值
37. default :
38. SetDlgItemInt(IDC_HSCROLL_EDIT,pos);
39. return ;
40. }
42. // 设置滚动块位置
43. m_horiScrollbar.SetScrollPos(pos);
45. CDialogEx::OnHScroll(nSBCode, nPos,pScrollBar);
46. }
SpinButton Control
CSpinButtonCtrl
CObject
└CCmdTarget
└CWnd
└CSpinButtonCtrl
控件风格
含义
UDS_HORZ
指定一个水平旋转按钮.若不指定该风格则创建一个垂直的旋转按钮.
UDS_WRAP
当旋转按钮增大到超过最大值时,自动重置为最小值,当减小至低于最小值时,自动重置为最大值.
UDS_ARROWKEYS
当用户按下向下或向上箭头键时,旋转按钮值递增或递减.
UDS_SETBUDDYINT
旋转按钮将自动更新伙伴控件中显示的数值,如果伙伴控件能接受输入,则可在伙伴控件中输入新的旋转按钮值.
UDS_NOTHOUSANDS
伙伴控件中显示的数值每隔三位没有千位分隔符.
UDS_AUTOBUDDY
自动使旋转按钮拥有一个伙伴控件.
UDS_ALIGNRIGHT
旋转按钮在伙伴控件的右侧.
UDS_ALIGNLEFT
旋转按钮在伙伴控件的左侧.
除上表的风格外,一般还要为旋转按钮指定WS_CHILD和WS_VISIBLE风格.创建一个有伙伴的垂直旋转按钮控件,一般应指定的风格为WS_CHILD|WS_VISIBLE|UDS_AUTOBUDDY| UDS_SETBUDDYINT.对于用对话框模板创建的旋转按钮控件,可以在控件的属性对话框中指定上表中列出的控件风格。例如,在属性对话框中选择Auto buddy,相当于指定了UDS_AUTOBUDDY风格.
2.成员函数
通过CSpinButtonCtrl的成员函数,可以对旋转按钮进行查询和设置:
用GetRange和SetRange来查询和设置旋转按钮值的范围,缺省时值的范围是1-100.这两个函数的声明为
void GetRange( int &lower, int& upper ) const;
void SetRange( int nLower, int nUpper );
第一个参数是最小值,该值不能小于UD_MINVAL,第二个参数是最大值,该值不能大于UD_MAXVAL.值的范围不能超过UD_MAXVAL.
用GetPos和SetPos来查询和设置旋转按钮的当前值.函数的声明为
int GetPos( ) const;
int SetPos( int nPos );
用GetBase和SetBase来查询和设置旋转按钮值的计数制.函数的声明为
UINT GetBase( ) const;
int SetBase( int nBase );
如果参数nBase是10,则伙伴控件中显示的数值是十进制的,如果nBase是16,则是十六进制的.
用GetBuddy和SetBuddy来查询和设置旋转按钮的伙伴.上面已讲了在对话框模板中设置伙伴控件的方法,如果是用Create手工创建旋转按钮,则可以用SetBuddy来设置伙伴.函数的声明为
CWnd* GetBuddy( ) const;
CWnd* SetBuddy( CWnd* pWndBuddy );
参数pWndBuddy是指向伙伴控件对象的CWnd型指针.
可以用GetAccel和SetAccel来查询和设置旋转按钮的加速值.在平时,在旋转按钮上按一下只会增/减一个单位,而当按住按钮超过一定时间时,递增或递减的幅度将会加大到指定的加速值,从而加快了增减的速度.如果对缺省的加速值不满意,可以用SetAccel设置新的加速值.可以有一套以上的加速值.函数的声明为
UINT GetAccel( int nAccel, UDACCEL* pAccel ) const;
BOOL SetAccel( int nAccel, UDACCEL* pAccel );
参数nAccel指定了UDACCEL结构数组的大小.参数pAccel指向一个UDACCEL结构数组.UDACCEL结构含有加速值的信息,其定义如下
typedef struct {
int nSec; //加速值生效需要的时间(以秒为单位)
int nInc; //加速值
} UDACCEL;
旋转按钮常被认为是一个简化的滚动条.除了表6.22列出的通知消息外,旋转按钮特有的滚动通知消息是通过WM_HSCROLL和WM_VSCROLL消息发出的.消息处理函数OnHScroll或OnVScroll分别用来处理水平或垂直旋转按钮的事件通知.由于伙伴控件中的内容会自动随旋转按钮变化,所以旋转按钮的通知消息意义不大.如果非要处理通知消息,一个典型的OnVscroll函数如下所示:
void CMyDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CSpinButtonCtrl* pSpin=(CSpinButtonCtrl*)pScrollBar;
int nPosition;
if(pSpin= =&m_Spin) //判断是否是该旋转按钮发来的消息
{
nPosition=m_Spin.GetPos( ); //获取旋转按钮的当前值
. . . . . .
}
. . . . . .
}
一个“旋转按钮控件”(也称为上下控件)是一对箭头按钮,用户点击它们来增加或减小一个值,比如一个滚动位置或显示在相应控件中的一个数字。与一个旋转按钮控件相联系的值被称为它的当前位置。一个旋转控件通常是与一个相伴的控件一起使用的,称为“伙伴窗口”。
CSpinButtonCtrl类提供了Windows通用旋转按钮控件的功能。这个控件(也就是CSpinButtonCtrl类)只对运行在Windows95和Windows NT3.51或更高版本下的程序来说是可用的。
对用户来说,一个旋转按钮控件和它的伙伴窗口看起来通常就象一个单一的控件。你可以指定一个旋转按钮控件自动将它自己定位在它的伙伴窗口的旁边,并且它自动将它的伙伴窗口的标题设置为它的当前位置。可以将一个旋转按钮控件与一个编辑控件一起使用,以提示用户进行数字输入。
点击向上箭头使当前位置向最大值方向移动,而点击向下箭头使当前位置向最小值的方向移动。缺省的,最小值是100,最大值是0。任何时候,最小值的设置都大于最大值(例如,当使用缺省值时),点击向上箭头减少位置值,而点击向下箭头则增加它。
一个没有伙伴窗口的旋转按钮控件就象简化了的滚动条。例如,一个tab控件有时显示一个旋转按钮控件来使它的用户能够滚动其它的tab进入视。
有关使用CSpinButtonCtrl的更多信息,参见“Visual C++程序员指南”中的“控件主题”和“使用CSpinButtonCtrl”。
#include
请参阅:CSliderCtrl
CSpinButtonCtrl类成员
Construction
CSpinButtonCtrl
构造一个CSpinButtonCtrl对象
Create
创建一个旋转按钮控件并将它连接到一个CSpinButtonCtrl对象
Attributes
SetAccel
为一个旋转按钮控件设置加速
GetAccel
获取一个旋转按钮控件的加速信息
SetBase
为一个旋转按钮控件设置基数
GetBase
获取一个旋转按钮控件的当前基数
SetBuddy
为一个旋转按钮控件设置伙伴窗口
GetBuddy
获取指向当前伙伴窗口的指针
SetPos
设置控件的当前位置
GetPos
获取一个旋转按钮控件的当前位置
SetRange
设置一个旋转按钮控件的上限和下限(范围)
GetRange
获取一个旋转按钮控件的上限和下限(范围)
SetRange32
设置旋转按钮控件的32位范围
GetRange32
获取旋转按钮控件的32位范围
下面是我自己写的一个示例:
在我的程序里我用到了两个函数,SetRange()和SetBuddy().
SetRange()函数的作用是设定spin控件向上与向下按钮所能调整的最大和最小值.
SetBuddy()函数选择一个与spin搭伙的控件,一般选用的是edit
1/建立一个mfc对话框类工程,名称为SpinTest1
2/在对话框窗口里加入一个Edit(ID号为IDC_EDIT_VALUE)和一个spin(ID号为IDC_SPIN_ABC)控件.
为IDC_SPIN_ABC控件建一个关联变量m_abc
3/在CSpinTest1Dlg类的OnInitDialog()里添加下面代码
m_abc.SetRange(1900,2000);
m_abc.SetBuddy((CWnd*)GetDlgItem(IDC_EDIT_VALUE));
4/你可以根据需要为Edit控件设定一个初始值,这样这个spin控件就基本可以使用了
说明:
有关这个控件,我在网上查了很多的资料,结果都说的很复杂,例如,在没有设定SetRange()范围之前,如何运行程序,会发现点击向上键头的按钮,数值是减少的,反之是增加的,这个问题网上有人还专门用了一个OnDeltaPos*()函数来解决,而我通过实践证明,只要设定了范围,就可以校正这个问题(当然这个只适用于数值数据的方面),网上还有有关点击了按钮之后在edit控件里显示字符的程序,如果做这样的程序可能会用到OnDeltaPos*()函数,下面我引用一下有关这个函数的添加方法:
旋转按钮控件的通知消息
旋转按钮控件的通知消息只有一个:UDN_DELTAPOS,它表示控件的位置将要改变。
用ClassWizard可以映射此消息,在此消息的处理函数中有个NM_UPDOWN结构需要进行
说明,其结构如下:
typedef struct_NM_UPDOWN{
NMHDR hdr; //通知代码的其他信息 int iPos; //当前位置 int iDelta; //位置的增减量,单击向上箭头此值为负数 }NMUPDOWN,FAR* LPNMUPDOWN;
应用实例:用Spin控件完成对字符的增减
响应UDN_DELTAPOS消息
旋转按钮控件的消息UDN_DELTAPOS
表示控件的位置将要改变
原理 , 重载Spin控件的UDN_DELTAPOS消息 , 将会多出如下函数:
void CTestDlg::OnDeltaposSpin3(NMHDR*pNMHDR, LRESULT* pResult)
{
NM_UPDOWN* pNMUpDown= (NM_UPDOWN*)pNMHDR;
if(pNMUpDown->iDelta == 1) // 如果此值为1 , 说明点击了Spin的往下箭头
{
//对字符做相关处理 , 例如将"a" 变为 "b"
}
else if(pNMUpDown->iDelta== -1) // 如果此值为-1 , 说明点击了Spin的往上箭头
{
//对字符做相关处理 , 例如将"b" 变为 "a"
}
*pResult = 0;
}
详细做法:
1: 假设你编辑框为IDC_EDIT , SPIN按钮为IDC_SPIN .
2: Ctrl + W , 进入Class Wizard , 在Object IDs列选择IDC_SPIN , 在Messages列选择UDN_DELTAPOS, 点击"Add Function" 按钮.将弹出"OnDeltaposSpin"的对话框 ,点OK 添加这个函数.然后点"EditCode" , 进入新添加的函数里 .
3: 将会新添加一个函数.
void CTestDlg::OnDeltaposSpin(NMHDR*pNMHDR, LRESULT* pResult) . . .
4: 添加如下代码:
void CTestDlg::OnDeltaposSpin(NMHDR*pNMHDR, LRESULT* pResult)
{
NM_UPDOWN* pNMUpDown= (NM_UPDOWN*)pNMHDR;
CWnd* pWnd =(CWnd*)GetDlgItem(IDC_EDIT); //得到指向编辑框的窗口指针
CString strValue ;
pWnd->GetWindowText(strValue); //得到编辑框中的内容
if(pNMUpDown->iDelta ==1) //如果点击的是Spin中的往上按钮
{
if(!strValue.IsEmpty())
{
strValue.SetAt(0 , strValue[0] + 1); //编辑框首字母加1
pWnd->SetWindowText(strValue); //保存修改
}
}
elseif(pNMUpDown->iDelta == - 1) //如果点击的是Spin中往下按钮
{
if(!strValue.IsEmpty())
{
strValue.SetAt(0 , strValue[0] - 1); //编辑框首字母减1
pWnd->SetWindowText(strValue); //保存修改
}
}
*pResult = 0;
}
使用上述做法,后来遇到一个问题:当一直按住上或者下时,增大16个后就不再增大,后来发现,将条件改成大于0和小于0就可以了,大于0表示向上按钮。
SplitButton Control
VS2008中可以看到MFC有一个叫Split Button的控件,要想看它的效果,瞧下QQ那聊天窗口的"发送", "消息记录"这两个按钮就知道了.实际上就是还有点像Combo Box了.不过它的实现应该是button加menu.所以它的消息处理可以分开成button的处理和menu的处理
往dialog上拖一个split button后,再给它绑定一个变量.
CSplitButton m_sbSend;
DDX_Control(pDX, IDC_SPLIT1, m_sbSend);
因为说了split button实际上是button跟menu的组合.所以你得整个menu先.不是什么特殊的菜单,就是主页面常用的那种菜单.
m_split.SetDropDownMenu(IDR_MENU1,0); //添加split button的下拉菜单.第一个参数就是菜单的资源ID,第二个参数就是子项索引,就是菜单根项从左之右数过去了.我们这里只能用到它的一个子菜单.
消息处理
split button中按钮部分的消息处理跟一般按钮一样.
ON_BN_CLICKED(IDC_SPLIT1,OnBnClickedSplit1)
void CMFCControlDlg::OnBnClickedSplit1()
{
AfxMessageBox(_T("click splitbutton"));
}
下拉菜单的处理
split button中下拉菜单的处理也跟一般菜单类似
ON_COMMAND(ID_Split_Menu1, SendMsg)
void CMFCControlDlg::SendMsg()
{
AfxMessageBox(_T("Click dropdownmenu of split button"));
}
PropertyGrid
HotKey
1. 添加WM_HOTKEY窗口消息
在 VC ++ 6.0 和VS2005中,大多数的窗口消息可以从 ClassWizard 中找到,但是一些不常用的消息
在 ClassWizard 中并没有封装 ,WM_HOTKEY 就是其中一个,因此用户必须手动添加。添加代码如下:
afx_msg LRESULT OnHotKey(WPARAMwp,LPARAM lp);
ON_MESSAGE(WM_HOTKEY,&CVS_2005_DlgDlg::OnHotKey)
LRESULT CVS_2005_DlgDlg::OnHotKey(WPARAMwp,LPARAM lp)
{
WinExec("notepad",1);//打开记事本
return 1;
}
2. 注册热键
BOOL CVS_2005_DlgDlg::OnInitDialog()
{
…
UINT fsModifiers = 0;
fsModifiers |= MOD_CONTROL;
fsModifiers |= MOD_ALT;
RegisterHotKey(GetSafeHwnd(), 1, fsModifiers,VK_F8); // 注册热键
…
}
这样,每当按下Ctrl+Alt+F8时,就会调出Windows记事本程序。
hot key control用法:
A "hot key control" is awindow that enables the user to create a hot key.
A "hot key" is a keycombination that the user can press to perform an action quickly.
(For example, a user can create a hotkey that activates a given window and brings it
to the top of the Z order.) The hot keycontrol displays the user's choices and ensures
that the user selects a valid keycombination.
1)加入控件变量:
CHotKeyCtrl m_hotKeyCtrl;
DDX_Control(pDX, IDC_HOTKEY1,m_hotKeyCtrl);
2)添加一按钮和按钮处理程序,用户设置完HotKey后按此按钮即设置了新的HotKey
afx_msg void OnBnClickedButton2();
ON_BN_CLICKED(IDC_BUTTON2,&CVS_2005_DlgDlg::OnBnClickedButton2)
voidCVS_2005_DlgDlg::OnBnClickedButton2()
{
//#define HOTKEYF_SHIFT 0x01
//#define HOTKEYF_CONTROL 0x02
//#define HOTKEYF_ALT 0x04
//#define HOTKEYF_EXT 0x08
WORD VirtualKeyCode=0,fsModifiers = 0;
m_hotKeyCtrl.GetHotKey(VirtualKeyCode,fsModifiers);
//#define MOD_ALT 0x0001
//#define MOD_CONTROL 0x0002
//#define MOD_SHIFT 0x0004
//#define MOD_WIN 0x0008
//GetHotKey() 与RegisterHotKey()用的标志位不一样,需要转换
UINT fsModifiers2 = 0;
if (fsModifiers & HOTKEYF_SHIFT)
{
fsModifiers2 |=MOD_SHIFT;
}
if (fsModifiers & HOTKEYF_CONTROL)
{
fsModifiers2 |=MOD_CONTROL;
}
if (fsModifiers & HOTKEYF_ALT)
{
fsModifiers2 |=MOD_ALT;
}
RegisterHotKey(GetSafeHwnd(), 1, fsModifiers2,VirtualKeyCode); // 注册热键
}
vbKeyLButton 1 鼠标左键
-------------------------------------------------------------------
vbKeyRButton 2 鼠标右键
-------------------------------------------------------------------
vbKeyCancel 3 CANCEL 键
-------------------------------------------------------------------
vbKeyMButton 4 鼠标中键
-------------------------------------------------------------------
vbKeyBack 8 Backspace 键
-------------------------------------------------------------------
vbKeyTab 9 TAB 键
-------------------------------------------------------------------
vbKeyClear 12 CLEAR 键
-------------------------------------------------------------------
vbKeyReturn 13 Enter 键
-------------------------------------------------------------------
vbKeyShift 16 Shift 键
-------------------------------------------------------------------
vbKeyConterol 17 Ctrl 键
-------------------------------------------------------------------
vbKeyMenu 18 菜单键
-------------------------------------------------------------------
vbKeyPause 19 PAUSE 键
-------------------------------------------------------------------
vbKeyCapital 20 CAPS LOCK 键
-------------------------------------------------------------------
vbKeyEscape 27 ESC 键
-------------------------------------------------------------------
vbKeySpace 32 SPACEBAR 键
-------------------------------------------------------------------
vbKeyPageUp 33 PAGEUP 键
-------------------------------------------------------------------
vbKeyPageDown 34 PAGEDOWN 键
-------------------------------------------------------------------
vbKeyEnd 35 END 键
-------------------------------------------------------------------
vbKeyHome 36 HOME 键
-------------------------------------------------------------------
vbKeyLeft 37 LEFT ARROW 键←
-------------------------------------------------------------------
vbKeyUp 38 UP ARROW 键↑
-------------------------------------------------------------------
vbKeyRight 39 RIGHT ARROW 键→
-------------------------------------------------------------------
vbKeyDown 40 DOWN ARROW 键↓
-------------------------------------------------------------------
vbKeySelect 41 SELECT 键
-------------------------------------------------------------------
vbKeyPrint 42 PRINT SCREEN 键
-------------------------------------------------------------------
vbKeyExecute 43 EXECUTE 键
-------------------------------------------------------------------
vbKeySnapshot 44 SNAP SHOT 键
-------------------------------------------------------------------
vbKeyInser 45 INSERT 键
-------------------------------------------------------------------
vbKeyDelete 46 DELETE 键
-------------------------------------------------------------------
vbKeyHelp 47 HELP 键
-------------------------------------------------------------------
vbKey0 48 0 键
-------------------------------------------------------------------
vbKey1 49 1 键
-------------------------------------------------------------------
vbKey2 50 2 键
-------------------------------------------------------------------
vbKey3 51 3 键
-------------------------------------------------------------------
vbKey4 52 4 键
-------------------------------------------------------------------
vbKey5 53 5 键
-------------------------------------------------------------------
vbKey6 54 6 键
-------------------------------------------------------------------
vbKey7 55 7 键
-------------------------------------------------------------------
vbKey8 56 8 键
-------------------------------------------------------------------
vbKey9 57 9 键
-------------------------------------------------------------------
vbKeyA 65 A 键
-------------------------------------------------------------------
vbKeyB 66 B 键
-------------------------------------------------------------------
vbKeyC 67 C 键
-------------------------------------------------------------------
vbKeyD 68 D 键
-------------------------------------------------------------------
vbKeyE 69 E 键
-------------------------------------------------------------------
vbKeyF 70 F 键
-------------------------------------------------------------------
vbKeyG 71 G 键
-------------------------------------------------------------------
vbKeyH 72 H 键
-------------------------------------------------------------------
vbKeyI 73 I 键
-------------------------------------------------------------------
vbKeyJ 74 J 键
-------------------------------------------------------------------
vbKeyK 75 K 键
-------------------------------------------------------------------
vbKeyL 76 L 键
-------------------------------------------------------------------
vbKeyM 77 M 键
-------------------------------------------------------------------
vbKeyN 78 N 键
-------------------------------------------------------------------
vbKeyO 79 O 键
-------------------------------------------------------------------
vbKeyP 80 P 键
-------------------------------------------------------------------
vbKeyQ 81 Q 键
-------------------------------------------------------------------
vbKeyR 82 R 键
-------------------------------------------------------------------
vbKeyS 83 S 键
-------------------------------------------------------------------
vbKeyT 84 T 键
-------------------------------------------------------------------
vbKeyU 85 U 键
-------------------------------------------------------------------
vbKeyV 86 V 键
-------------------------------------------------------------------
vbKeyW 87 W 键
-------------------------------------------------------------------
vbKeyX 88 X 键
-------------------------------------------------------------------
vbKeyY 89 Y 键
-------------------------------------------------------------------
vbKeyZ 90 Z 键
-------------------------------------------------------------------
vbKeyNum0 96 0 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyNum1 97 1 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyNum2 98 2 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyNum3 99 3 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyNum4 100 4 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyNum5 101 5 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyNum6 102 6 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyNum7 103 7 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyNum8 104 8 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyNum9 105 9 键 (在数字小键盘上)
-------------------------------------------------------------------
vbKeyMultiply 106 乘号(*) 键
-------------------------------------------------------------------
vbKeyAdd 107 加号(+) 键
-------------------------------------------------------------------
vbKeySeparator 108 Enter键(在数字小键盘上)
-------------------------------------------------------------------
vbKeySubtract 109 减号(-) 键
-------------------------------------------------------------------
vbKeyDecimal 110 小数点(.) 键
-------------------------------------------------------------------
vbKeyDivide 111 除号(/) 键
-------------------------------------------------------------------
vbKeyF1 112 F1 键
-------------------------------------------------------------------
vbKeyF2 113 F2 键
-------------------------------------------------------------------
vbKeyF3 114 F3 键
-------------------------------------------------------------------
vbKeyF4 115 F4 键
-------------------------------------------------------------------
vbKeyF5 116 F5 键
-------------------------------------------------------------------
vbKeyF6 117 F6 键
-------------------------------------------------------------------
vbKeyF7 118 F7 键
-------------------------------------------------------------------
vbKeyF8 119 F8 键
-------------------------------------------------------------------
vbKeyF9 120 F9 键
-------------------------------------------------------------------
vbKeyF10 121 F10 键
-------------------------------------------------------------------
vbKeyF11 122 F11 键
-------------------------------------------------------------------
vbKeyF12 123 F12 键
-------------------------------------------------------------------
vbKeyF13 124 F13 键
-------------------------------------------------------------------
vbKeyF14 125 F14 键
-------------------------------------------------------------------
vbKeyF15 126 F15 键
-------------------------------------------------------------------
vbKeyF16 127 F16 键
-------------------------------------------------------------------
vbKeyNumlock 144 NUM LOCK 键
AnimationControl
有时要在页面上显示些动态画面,可以使用该控件.
CAnimateCtrl m_animate;
DDX_Control(pDX, IDC_ANIMATE1,m_animate);
m_animate.Open(_T("D:\\Temp\\boxed-correct.avi"));//加载一个avi文件
m_animate.CenterWindow(); //播放窗口显示位置
m_animate.Play(0,-1,-1); //开始播放,三个参数的意思分别是:第一个参数表示开始播放的位置,0表示从开头播放. 第二个参数表示结束位置,-1表示播放完整个视频.第三个参数表示重复播放的次数,-1表示重复无数次. 播放的位置你可以联想下看视频时下面的进度条,可以拉动选择播放指定的内容.
如果要停止播放则
m_animate.Stop();
昨晚到现在一直寻思着在MFC的Dialog上显示个GIF图片,无奈如此的有难度,经过寻找找到个好用的控件,拿来用了,但是官网给的不是很好使。也缺乏一个文件,我这里一起整理出来了。说个过程
1:一共需要5个文件,其中3个h,2个cpp文件,如图:
2:分别导入到自己的工程,接下来才是操作
3:在自己的Dlg的资源设计上,添加一个static的静态文本框,然后给这个文本框赋予id卫IDC_GIF_ANIMATION,然后右键点击这个文本框找到-》建立类向导-》成员变量-》在下面找到“IDC_GIF_ANIMATION”,双击-》变量名字输入“m_Animation”,目录选择“Control”,类型选择CStatic。
4:在Dlg的h中找到CStaticm_Animation,修改成 CGifAnimationm_Animation;,并且在这个h中添加#include "GifAnimation.h"
5:在Dlg的cpp中初始化函数中添加:
m_Animation.LoadAnimatedGif("./res/1.gif");
m_Animation.Play();
6:编译即可。
动画控件的局限:
动画控件并不能播放所有的AVI文件,只有满足下列条件的AVI文件才能被播放:
AVI文件必须是无声的,不能有声道。
AVI文件必须是未压缩的,或是用RLE算法压缩的。
AVI的调色板必须保持不变。
动画控件最大的局限性在于它只能显示系统调色板中缺省的颜色,因此如果用动画控件来播放一个256色的AVI文件,那么播放效果看起来就象一个16色的动画一样,很不理想。
总之,动画控件只能播放一些简单的,颜色数较少的AVI动画。
CommandButton Control
这个名字取得挺忽悠人.其实该控件没太多新东西,就在原有的Button上加了一点新特性.仍然属于button.
它用起来不同于一般button的地方主要是3个方面.
1.除了button上面的caption显示的文字外还多了个note文字,相当起于进一步解释作用的文字,并是用小号的字显示出来.
2.可以在button前面显示一个icon图标,默认是指向右边的箭头
3.鼠标没放过去之前不像个button,像个static text控件一样的文本信息.鼠标移上去后才变得像button.
除了上面说的之外其他操作跟一般button一样.
所以主要来看下怎么设置note,和icon
CButton*pBtn = (CButton*)GetDlgItem(IDC_COMMAND1);
pBtn->SetNote(_T("something todescription")); //设置note的内容
pBtn->SetIcon(m_hIcon); //设置icon,当然也可以使用默认的icon.
NetwordAddress Control
打开看到那控件的样子就完全是个Edit control.不过该控件对应的类也确实是继承自类CEdit.
先拖个控件.然后绑定个变量
CNetAddressCtrl m_netName;
DDX_Control(pDX,IDC_NETADDRESS, m_netName);
m_netName.SetAllowType(NET_STRING_IPV4_ADDRESS);//只要输入IPV4格式的IP .如果让输入IPv6就是NET_STRING_IPV6_ADDRESS,输入网址就是NET_STRING_NAMED_ADDRESS
不过输入的时候反正不管,只有完了后再验证.并且要自己写点代码验证.假如点OK后验证下输的对不
voidCMFCControlDlg::OnOK()
{
NC_ADDRESS m_na;
NET_ADDRESS_INFOm_nai;
m_na.pAddrInfo= &m_nai;
HRESULTrslt = m_netName.GetAddress(&m_na);
if (rslt!= S_OK)
m_netName.DisplayErrorTip();
else
AfxMessageBox(_T("The format is correct"));
}
IPAddress Control
1.独立的获取本机IP地址和计算机名
void CMyDlg::OnIPAddress()
{
//此段代码:独立的获取本机IP地址和计算机名
WORDwVersionRequested;
WSADATAwsaData;
charname[255];
CStringip;
PHOSTENThostinfo;
wVersionRequested= MAKEWORD(2, 0);
if(WSAStartup(wVersionRequested, &wsaData) == 0)
{
if(gethostname(name, sizeof(name)) == 0)
{
if((hostinfo= gethostbyname(name)) != NULL)
{
ip = inet_ntoa(*(struct in_addr*)*hostinfo->h_addr_list);
}
}
WSACleanup( );
}
//AfxMessageBox(name);//name里是本机名
//AfxMessageBox(ip); //ip中是本机IP
m_IPAddress= ip; // m_IPAddress是IP控件对应的变量,ip是Edit控件对应的变量
//m_IP.SetAddress(255, 86, 255, 68); // 直接设置控件里显示的值
2.把IP Address控件里的值转化为CString格式
/*
//下面代码实现:把IP Address控件里的值转化为 CString格式
unsignedchar *pIP;
CStringstrIP;
DWORDdwIP;
m_IP.GetAddress(dwIP);// m_IP为IP Address控件对应的变量
pIP= (unsigned char*)&dwIP;
strIP.Format("%u.%u.%u.%u",*(pIP+3), *(pIP+2), *(pIP+1), *pIP);
MessageBox(strIP);*/
/*
//下面代码实现:把IP Address控件里的值转化为 CString格式
BYTEf0, f1, f2, f3;
m_IP.GetAddress(f0,f1, f2, f3);
CStringm_addr;
m_addr.Format("%d%s%d%s%d%s%d",f0, ".", f1, ".", f2, ".", f3);
MessageBox(m_addr);*/
/*
//下面代码实现:把IP Address控件里的值转化为 CString格式
BYTEIPByte[4];
m_IP.GetAddress(IPByte[0],IPByte[1], IPByte[2], IPByte[3]);
CStringstrIP = "";
chartemp1[10], temp2[10], temp3[10], temp4[10];
itoa(IPByte[0],temp1, 10);
itoa(IPByte[1],temp2, 10);
itoa(IPByte[2],temp3, 10);
itoa(IPByte[3],temp4, 10);
strIP+= temp1;
strIP+= ".";
strIP+= temp2;
strIP+= ".";
strIP+= temp3;
strIP+= ".";
strIP+= temp4;
MessageBox(strIP);*/
/*
//下面代码实现:把IP Address控件里的值转化为 CString格式
CStringstrx;
m_IP.GetWindowText(strx);
MessageBox(strx);*/
//此段代码:用获取的IP地址值,显示到对话框里IP Address控件中
/*CString strIP;
GetDlgItemText(IDC_EDIT_IPAddress,strIP);
m_IP.SetWindowText(strIP);*/
m_IP.SetWindowText(ip);// 把IP地址(CString类型)直接显示到IP Address控件中
UpdateData(FALSE);
}
3.从IP Address控件获得IP地址,并交给程序(如socket中的connect函数)处理。
方法一:
//m_Ip为IP Address控件的变量名
m_Ip.GetAddress(IPByte[0],IPByte[1],IPByte[2],IPByte[3]); 获得IP Address控件中的4部分数字
//------------------把IP地址转化为字符
CString strI;="";
char temp1[10],temp2[10],temp3[10],temp4[10];
itoa(IPByte[0],temp1,10);
itoa(IPByte[1],temp2,10);
itoa(IPByte[2],temp3,10);
itoa(IPByte[3],temp4,10);
strIp+=temp1;
strIp+=".";
strIp+=temp2;
strIp+=".";
strIp+=temp3;
strIp+=".";
strIp+=temp4;
itoa函数介绍
定义在stdlib中的, _CRTIMP char * __cdecl _itoa(int, char *, int);
我看的源代码中是这么调用的:
_itoa(i,str3,10);
功能:把整形转换为字符类型,
含义: i:需要转换的整形
str3:字符串缓冲区
10:十进制方式
方法二(简单):
m_IP.GetAddress(f0,f1,f2,f3);//m_IP是ip控件的控制变量
m_addr.Format("%d%s%d%s%d%s%d",f0,".",f1,".",f2,".",f3);
使用VC提供的Format函数,省去了整数转化为字符串的操作。
CustomControl
VC里的Custom Control的使用很简单,用鼠标拖动到窗体上,然后设置其“Class”属性为已注册的窗口类,比哪Edit、Button等等。因为像Edit、Button等控件可以很容易的画到窗体上,而对继承于CWnd的自定义窗口类则没有办法直接用鼠标画到窗体上,这时Custom Control就用上了,只要类是使用RegisterClass注册后就能被使用,如:
[cpp] viewplaincopy
WNDCLASSwc;
wc.lpszClassName= _T("MyWindow"); // matches class name in client
wc.hInstance =hInstance;
wc.lpfnWndProc =::DefWindowProc;
wc.hCursor =::LoadCursor(NULL, IDC_ARROW);
wc.hIcon =0;
wc.lpszMenuName= NULL;
wc.hbrBackground= (HBRUSH) ::GetStockObject(LTGRAY_BRUSH);
wc.style =CS_GLOBALCLASS; // To be modified
wc.cbClsExtra =0;
wc.cbWndExtra =0;
::RegisterClass(&wc)!= 0
这时保存在CustomControl的Class属性上输入MyWindow后,程序运行正常。
其实我们可以不用这么“麻烦”的来使用自定义类。只一个SubclassWindow就搞定了。使用方法:
1:定义我们需要类,比如从CWnd继承一个新的类MyWindow、并编写自己的代码实现。
2:在窗体上放任何一个控件(目的是使用这个控件的大小及位置),比如可以放一个Static在窗体上。为控件添加一个变量,变量类型为MyWindow,然后在代码的合适的地方(比如OnInitDialog)添加如下的代码就可以了:
[cpp] viewplaincopy
this->m_MyWindow.SubclassWindow(this->GetDlgItem(IDC_Static)->GetSafeHwnd());
直接运行程序,绝不会出现CustomControl那种运行不起来的现象。
MFCButton Control
SysLinkControl
MFC中有一个 SysLink Control 的控件,用于在 MFC 应用程序上添加超链接。下面说一下简单的使用方法:
1、首先建立一个基于对话框的MFC工程,添加一个 Syslink Control 控件;
2、在该控件的 Caption 属性里添加带 href 的锚定标记MFC
3、接着,为控件添加NM_CLICK 事件,在事件函数里面添加如下:
voidCAesAboutDlg::OnNMClickSyslink3(NMHDR *pNMHDR, LRESULT *pResult)
{
PNMLINK pNMLink = (PNMLINK) pNMHDR;
if (wcscmp(pNMLink->item.szUrl,_T("http:\/\/weibo.com\/lisonglisong")) == 0)
{
// 主要执行语句
ShellExecuteW(NULL, L"open",pNMLink->item.szUrl, NULL, NULL, SW_SHOWNORMAL);
}
*pResult = 0;
}
注意,_T()中转义字符的使用。
MFCColorButton Control
添加了一个到了一个对话框,试运行一下,发现效果不错
可以选择RGB所有的颜色,可以说功能很强大了,而且自动是弹出式的,这些功能的实现都不用编写一行代码
可谓是十分方便
但悲催的是,搜索这个控件的用法,是在少之又少,
唯一一个比较有效的还是一个着重讨论改变这个控件外观的…….
查看MSDN帮助文档,满眼英文,而且讲得很少,几乎没起到什么作用
经历了一个半小时琢磨,终于自己弄出了一个很基础功能的用法
----------------------------------------------------------------------------------------------------------------------------------------------
首先,该按钮不是一个类,这是基本概念了,
设置一个变量,CMFCColorButton的实例,
1.在资源视图右击该控件
右击添加变量
2.
3.切换到类视图,在CPenDialog类中
[cpp] view plaincopy
多出这一行代码说明添加变量成功,这个
[cpp] view plaincopy
m_ColorPicker
已经关联到那个颜色选择控件了
4.在初始化对话框时初始化该控件
[cpp] view plaincopy
BOOL CPenDialog::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: 在此添加额外的初始化
m_ColorPicker.EnableAutomaticButton(_T("目前"), m_SelectedColor);
//m_ColorPicker.SetColor(m_SelectedColor);
m_ColorPicker.EnableOtherButton(_T("其余颜色"));
m_ColorPicker.SetColor((COLORREF)-1);
m_ColorPicker.SetColumnsNumber(5);
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
5.获取选择的颜色
在类视图中添加代码
voidCPenDialog::OnBnClickedMfccolorbutton1()
{
// TODO: 在此添加控件通知处理程序代码
m_SelectedColor = m_ColorPicker.GetColor();
}
m_SelectedColor是自己声明的一个存储选取颜色的变量
MFCEditBrowse Control
映射一个变量;然后设置他的文本即可;
控件变量m_Control.SetWindowTextA("C:\\Program Files");
如果是字符串变量设置完需要用 UpdataData(False) 更新
m_stControl ="C:\\Program Files";
UpdataData(False);
MFC 使用MFC EditBrowse Control控件选择文件或者文件夹
从工具箱中拖拽一个MFC EditBrowse Control到窗体中,
通过设置“Browse Mode”属性指定“文件浏览”还是“文件夹浏览”
可以通过添加对象的方式将其与一个CString selectedPath对象关联选择的路径。
也可以通过
CStringselectedPath;
GetDlgItemText(IDC_MFCEDITBROWSE1,selectedPath);
获得选择的文件或者文件夹路径
MFCVSListBox Control
一在资源窗口中,选中资源右键-》资源包括-》输入如下代码
#ifndef_AFXDLL
#include"afxribbon.rc"
#endif
二新建一个基于对话框的项目
将工具箱中的MFCVSListBox Control直接拖到窗口中
利用向导为该控件绑定一个变量
CVSListBox m_List_ctlMain
向导自动添加头文件
#include"afxvslistbox.h"
三增加输入规则
1 删除时,先弹出提示对话框,询问用户是否删除
2 在新建和编辑List中的内容时,检查是否存在重复内容
在微软提供的例子中重写关于按钮的消息
classCCustomEditListBox :public CVSListBox
{
virtual void OnBrowse()
{
int nSel = GetSelItem();
MessageBox(_T("Browse item…"));
if (nSel == GetCount()) // New item
{
nSel = AddItem(_T("New text"));
SelectItem(nSel);
}
else
{
SetItemText(nSel, _T("Updated text"));
}
}
};
头文件和实现文件放在一个文件中
根据微软提供的例子按照这种写法进行仿写
类的继承关系
class CVSListBox:public CVSListBoxBase
class CVSListBoxBase:public CStatic
在CVSListBoxBase类中有
virtual BOOLOnBeforeRemoveItem(int/*iItem*/) { returnTRUE; }
新建一个继承自CVSListBox的类CCustomEditListBox
以下代码实现删除时,先弹出提示对话框,询问用户是否删除
class CCustomEditListBox:public CVSListBox
{
protected:
BOOL OnBeforeRemoveItem(intnItem)
{
CString strText = GetItemText(nItem);
CString strPrompt=_T("");
strPrompt.Format(_T("确定要删除【%s】吗?"),strText);
if ( MessageBox(strPrompt,_T("提示"),MB_ICONQUESTION|MB_OKCANCEL)==IDOK)
{
return TRUE;
}
return FALSE;
}
};
以下代码实现在新建和编辑List中的内容时,检查是否存在重复内容
classCCustomEditListBox :public CVSListBox
{
boolIsExist(CStringstrText)
{
for (int i=0;i<GetCount();i++)
{
CString strContent=GetItemText(i);
if (strContent == strText )
{
return true;//已经存在
}
}
return false;
}
voidSetItemText(int iIndex, const CString& strText)
{
if (IsExist(strText))
{
CString strPrompt=_T("");
strPrompt.Format(_T("【%s】已经存在"),strText);
AfxMessageBox(strPrompt);
EditItem(iIndex);
m_wndEdit.SetWindowText(strText);
m_wndEdit.SetSel(0,-1);//全选
// m_wndEdit.SetSel(-1);//光标处于文字的末端
return;
}
if (GetSafeHwnd() == NULL || m_pWndList ==NULL)
{
ASSERT(FALSE);
return;
}
ASSERT_VALID(m_pWndList);
m_pWndList->SetItemText(iIndex,0,strText);
}
};
代码中使用CVSListBox类中的成员变量
CListCtrl*m_pWndList;
CVSListBoxEditCtrl m_wndEdit;
还可以重写其他事件
virtualvoid OnAfterAddItem(int/*iItem*/){}
virtualvoid OnAfterRenameItem(int/*iItem*/){}
virtualvoid OnAfterMoveItemUp(int/*iItem*/){}
virtualvoid OnAfterMoveItemDown(int/*iItem*/){}
如需其他功能例如多列,输入掩码,只允许输入数字等等功能就需要对CListCtrl和CVSListBoxEditCtrl 进行扩展。
MFCMaskedEdit Control
名称
说明
CMFCMaskedEdit::CMFCMaskedEdit
默认构造函数。
CMFCMaskedEdit::~CMFCMaskedEdit
析构函数。
名称
说明
验证用户输入的禁用。
CMFCMaskedEdit::EnableGetMaskedCharsOnly
指定 GetWindowText 方法是否只检索掩码字符。
初始化掩码编辑控件。
CMFCMaskedEdit::EnableSelectByGroup
指定掩码是编辑控件选择用户输入的特定组,或者所有用户输入。
CMFCMaskedEdit::EnableSetMaskedCharsOnly
指定文本是否仅验证掩码字符,或者所有掩码。
CMFCMaskedEdit::GetThisClass
用于由框架获取指向与此选件类类型的 CRuntimeClass 对象。
retrieves验证起始于掩码文本编辑控件。
指定用户可以输入有效字符的字符串。
显示在掩码一个提示编辑控件。
名称
说明
调用由结构验证指定的字符对应的掩码字符。
执行以下步骤使用 CMFCMaskedEdit 控件在您的应用程序:
嵌入一 CMFCMaskedEdit 对象添加到windows选件类。
调用 CMFCMaskedEdit::EnableMask 方法指定掩码。
调用 CMFCMaskedEdit::SetValidChars 方法指定有效字符列表。
调用 CMFCMaskedEdit::SetWindowText 方法对掩码指定默认文本编辑控件。
调用 CMFCMaskedEdit::GetWindowText 方法检索已验证的文本。
如果不调用一个或多个方法初始化掩码、有效字符和默认文本,掩码编辑控件的行为就象标准编辑控件的行为。
下面的示例演示如何设置mask (例如电话号码)使用 EnableMask 方法创建掩码屏蔽编辑控件,SetValidChars 方法指定用户可以输入有效字符的字符串,并且,显示在掩码一个提示的 SetWindowText 方法编辑控件。 此示例是 新的控件示例的一部分。
C++
CMFCMaskedEdit m_wndMaskEdit1;
CMFCMaskedEdit m_wndMaskEdit2;
CMFCMaskedEdit m_wndMaskEdit3;
CMFCMaskedEdit m_wndMaskEdit4;
CMFCMaskedEdit m_wndMaskEdit5;
CString m_strValue1;
CString m_strValue2;
CString m_strValue3;
CString m_strValue4;
CString m_strValue5;
…
BOOL CPage4::OnInitDialog()
{
CMFCPropertyPage::OnInitDialog();
// Mask 1: phone number
m_wndMaskEdit1.EnableMask(_T(" ddd ddd dddd"), // The mask string
_T("(___) ___-____"), // Literal, "_" char = character entry
_T(' ')); // Default char
m_wndMaskEdit1.SetValidChars(NULL); // Valid string characters
m_wndMaskEdit1.SetWindowText(_T("(123) 123-1212"));
// Mask 2: State, Zip Code
m_wndMaskEdit2.EnableMask(_T(" cc ddddd-dddd"), // The mask string
_T("State: __, Zip: _____-____"), // Literal, "_" char = character entry
_T(' ')); // Backspace replace char
m_wndMaskEdit2.SetValidChars(NULL); // Valid string characters
m_wndMaskEdit2.SetWindowText(_T("State: NY, Zip: 12345-6789"));
// Mask 3: serial code
m_wndMaskEdit3.EnableMask(_T(" AAAA AAAA AAAA AAAA"), // The mask string
_T("S/N: ____-____-____-____"), // Literal, "_" char = character entry
_T('_')); // Backspace replace char
m_wndMaskEdit3.SetValidChars(NULL); // Valid string characters
m_wndMaskEdit3.SetWindowText(_T("S/N: FPR5-5678-1234-8765"));
// Mask 4: 0xFFFF
m_wndMaskEdit4.EnableMask(_T(" AAAA"), // The mask string
_T("0x____"), // Literal, "_" char = character entry
_T('_')); // Backspace replace char
m_wndMaskEdit4.SetValidChars(_T("1234567890ABCDEFabcdef")); // Valid string characters
m_wndMaskEdit4.SetWindowText(_T("0x01AF"));
// Mask 5: digits only
m_wndMaskEdit5.DisableMask(); // Don't use the mask
m_wndMaskEdit5.SetValidChars(_T("1234567890")); // Valid string characters
m_wndMaskEdit5.SetWindowText(_T("1234567890"));
return TRUE; // return TRUE unless you set the focus to a control
}
void CPage4::OnButtonGet()
{
m_wndMaskEdit1.GetWindowText(m_strValue1);
m_wndMaskEdit2.GetWindowText(m_strValue2);
m_wndMaskEdit3.GetWindowText(m_strValue3);
m_wndMaskEdit4.GetWindowText(m_strValue4);
m_wndMaskEdit5.GetWindowText(m_strValue5);
UpdateData(FALSE);
}
CObject
CCmdTarget
CWnd
CEdit
CMFCMaskedEdit
标头: afxmaskededit.h
MFCFontComboBox Control
CMFCFontComboBox
实现:
拖放该控件至对话框模板
初始化字体:
在OnInitialUpdate里添加:
((CMFCFontComboBox*)GetDlgItem(IDC_MFCFONTCOMBO1))->SelectFont(_T("新宋体"));
处理单击事件:
添加消息原型:
afx_msg void OnCbnSelchangeMfcfontcombo1();
消息映射:
ON_CBN_SELCHANGE(IDC_MFCFONTCOMBO1,&CToolView::OnCbnSelchangeMfcfontcombo1)
消息实现:
void CToolView::OnCbnSelchangeMfcfontcombo1()
{
// TODO: 在此添加控件通知处理程序代码
CMFCFontInfo*pInfoFont;
pInfoFont=((CMFCFontComboBox*)GetDlgItem(IDC_MFCFONTCOMBO1))->GetSelFont();
CString str;
str=pInfoFont->m_strName;
AfxMessageBox(str);
}
MFCMenuButton Control
可以在按钮上加上菜单的功能,这样用对话框就能实现菜单栏的功能
1、准备工作:准备3个对话框,一个用于主窗口,ID为ID_MAIN_DIALOG,另外两个用于菜单的弹出,ID分别命名为ID_ONE_DIALOG,ID_TWO_DIALOG,为后两个对话框添加类,类名为COneDlg和CTwoDlg
2、在主窗口上添加MFC MenuButton控件,为它添加一个变量:m_MenuButton
3、在资源部分加入一个菜单资源,ID为IDR_MENU,设计好弹出菜单样式,为每个弹出选项设ID,这里设为ID_ONE和ID_TWO
4、将MenuButton控件和上面的菜单资源绑定,参考下面代码:
CMenu* pMenu =new CMenu;
pMenu->LoadMenu(IDR_MENU); // 载入菜单资源
m_MenuButton.m_hMenu= pMenu->GetSubMenu(0)->GetSafeHmenu(); // 将CMFCMenuButton和Menu IDR_MENU1关联
5、设置事件,这里注意设置MenuButton的ON_CLICKED的消息,而不是在菜单选项那里添加事件,在消息函数中添加类似下面的代码即可(这里的工作是弹出两个对话框)
switch(m_settingMenu.m_nMenuResult)
{
case ID_ONE:
{
COneDlg oneDlg;
oneDlg.DoModal();
break;
}
case ID_TWO:
{
CTwoDlg twoDlg;
twoDlg.DoModal();
break;
}
default:
break;
}
MFCPropertyGrid Control
简要分析
PropertyGrid, 做工具一定要用这东西…..
把要编辑的对象看成类的话, 所有要编辑的属性就是成员
嗯嗯, 最近看了几眼Ogitor, 它对于PropertyGrid的使用就很不错
所有要编辑的对象(灯光, 模型, 粒子等等)都有一个共同的基类, 每当选中一个可编辑对象时, 右边的属性框里就显示出当前对象的属性…(公司那个编辑器要多土就有多土-_-)
尽管Ribbon界面看起来很酷, 我还是对MFC提不起兴趣来…
.net里的PropertyGrid更方便, 一点一点来:
属性自动绑定:
[cpp] view plaincopy
ref class Human
{
public:
Human()
{
this->Name = "(None)";
this->Age = 0;
this->IsMale = false;
}
property String^ Name;
property int Age;
property bool IsMale;
};
只需要一句
[cpp] view plaincopy
它就能自动识别出Human类中的property, 并且自动关联到PropertyGrid中:
对属性进行分类并加注释:
[cpp] view plaincopy
ref class Human
{
public:
Human()
{
this->Name = "(None)";
this->Age = 0;
this->IsMale = false;
this->SkinColor = Color::Yellow;
}
[CategoryAttribute("常规"), DescriptionAttribute("名字")]
property String^ Name;
[CategoryAttribute("常规"), DescriptionAttribute("年龄")]
property int Age;
[CategoryAttribute("外观"), DescriptionAttribute("性别")]
property bool IsMale;
[CategoryAttribute("外观"), DescriptionAttribute("肤色")]
property Color SkinColor;
};
太爽啦~颜色自己就能识别……..
弄个Image类型居然还能自己选择文件…NB啊
除了基本类型之外, Font, Size,Color等复杂类型也可以支持, 那么自定义类型呢?
如果只是像上面那样放上的话, 只会得到个灰色不可编辑的东西~
要想让PropertyGrid能够展开Vector3属性, 指定一下TypeConverter就可以了:
[cpp] view plaincopy
[TypeConverterAttribute(ExpandableObjectConverter::typeid)]
ref struct Vector3
{
property float X;
property float Y;
property float Z;
virtual String^ ToString() override
{
return String::Format("({0}, {1}, {2})", this->X, this->Y, this->Z);
}
};
对于枚举类型, PropertyGrid会自动显示成下拉框. 把性别改成枚举看看:
[cpp] view plaincopy
另外, 还可以弹出自定义的编辑界面, 比如随时间变化的曲线啦(经常用来做效果…)
这个, 暂时没需求, 不实现了, 有兴趣的参考:Getting the Most Out of the .NET FrameworkPropertyGrid Control
简单教程
用vs2010建立一个基于对话框的MFC工程,拖一个CMFCPropertyGridCtrl进去,大小调整好。(注:首先采用的是静态创建的办法,大部分需要的属性在对话框编辑界面就可以编辑。)然后为该控件更改ID为IDC_MFCPROPERTYGRID_TEST,并且使用ClassWizard为控件添加一个变量m_propertyGrid。
接下来更改控件的一些属性。
其实这不就是个CMFCPropertyGridCtrl控件么。Border神马的基础东西就不说了。
"DescriptionRows Count"指的是下面的描述部分有几行。
"EnableDescription Area"表示是否启动下面的描述功能
"EnableHeader"表示是否启动表头
"MarkModified Properties"表示是否着重显示更改项
可以按照需求来进行设置。这里先使用默认的设置。先编译运行一下,比较简陋。
好,接下来该添加东西进去了。
在OnInitDialog中添加如下代码,我会一行一行解释。
CMFCPropertyGridProperty * pProp1 = new CMFCPropertyGridProperty(
_T("天朝适合生存吗?"),
_T("51CTO不谈政治"),
_T("这是描述部分"));
m_propertyGrid.AddProperty(pProp1);
第一行是建立一个新的项目,也是最普通的项目,CMFCPropertyGridProperty。与这种项目同级的还有CMFCPropertyGridColorProperty、CMFCPropertyGridFontProperty以及CMFCPropertyGridFileProperty,等会都可以试一下。调用构造函数时传入的三个参数分别是条目名称、默认选项及描述文字。运行一下就知分晓。
饿滴神啊,肿么这个样子。不过该有的全有,只需要设置一下就行。这里得提一笔,微软似乎非常喜欢把第一列的宽度设置为“只能容得下一个普通的5号小宋体的宽度”,不光是CMFCPropertyGrid,连CListCtrl也是如此,需要动点特殊的手段才能调整过来。在这段代码的前面加这么几句:
HDITEM item;
item.cxy=120;
item.mask=HDI_WIDTH;
m_propertyGrid.GetHeaderCtrl().SetItem(0, new HDITEM(item));
如此再运行,就会比较好看了。
好,接下来我们看一下右边的value部分还能换成神马东西。
如同vs2010里提供的“属性”栏一样,这CMFCPropertyGridCtrl应该也支持下拉菜单,好,就来添加下拉菜单看看。修改刚才的代码:
CMFCPropertyGridProperty* pProp2 = new CMFCPropertyGridProperty(
_T("我是不是帅哥?"),
_T("看选项"),
_T(""));
pProp2->AddOption(_T("是"));
pProp2->AddOption(_T("肯定是"));
pProp2->AddOption(_T("绝对是"));
pProp2->AllowEdit(FALSE); //不允许对选项进行编辑
m_propertyGrid.AddProperty(pProp2);
然后运行,就会如愿以偿地出现下拉框了。
接下来是另外三个同级的项目:
CMFCPropertyGridColorProperty * pProp3 = new CMFCPropertyGridColorProperty(
_T("颜色"), RGB(0, 111, 200));
m_propertyGrid.AddProperty(pProp3);
CMFCPropertyGridFileProperty * pProp4 = new CMFCPropertyGridFileProperty(
_T("打开文件"), TRUE, _T("D:\\test.txt"));
m_propertyGrid.AddProperty(pProp4);
LOGFONT font = {NULL};
CMFCPropertyGridFontProperty * pProp5 = new CMFCPropertyGridFontProperty(
_T("选择字体"), font);
m_propertyGrid.AddProperty(pProp5);
注:每一种类型的项目都有2个或3个重载函数,可以自己根据需求慢慢挖掘,在这里就不赘述了。
运行效果如下:
这么些不同种类的东西乱七八糟堆在一起,是不是有点不科学?那么就引入下一个概念:分组。回到第一张图,vs2010的“属性”栏分了三个组,分别是Apperance、Behavior和Misc,看起来就清晰多了,我们也可以。
好,来重新构建一下我们的代码:
CMFCPropertyGridProperty * group1 = new CMFCPropertyGridProperty(_T("分组1"));
CMFCPropertyGridProperty * group2 = new CMFCPropertyGridProperty(_T("分组2"));
group1->AddSubItem(pProp1);
group1->AddSubItem(pProp2);
group2->AddSubItem(pProp3);
group2->AddSubItem(pProp4);
group2->AddSubItem(pProp5);
m_propertyGrid.AddProperty(group1);
m_propertyGrid.AddProperty(group2);
编译运行效果如下:
至此,静态创建CMFCPropertyGridCtrl的方法就结束了。
还有一种方法是动态创建,与CStatic、CEdit等控件无二,在创建之后也可以利用自带的函数修改控件的属性,如:
CMFCPropertyGridCtrl * propertyGrid = new CMFCPropertyGridCtrl;
propertyGrid->Create(WS_CHILD | WS_BORDER | WS_VISIBLE, CRect(400, 100, 600, 200), this, WM_USER + 100);
propertyGrid->EnableHeaderCtrl(TRUE); //使用表头
propertyGrid->SetVSDotNetLook(); //使用样式
propertyGrid->MarkModifiedProperties(); //着重显示更改过的部分
例子分析
MFCPropertyGridCtrl
是VC 2008 pack中的类,实现了如下功能:
(1)界面分面两栏:一栏为属性,一栏为值
如这个图
[cpp] view plaincopy
Parameters
[in] rect
A bounding rectangle that specifies the size and position of the window, in client coordinates of pParentWnd.
[in] pParentWnd
Pointer to the parent window. Must not be NULL.
Remarks
To create a property grid control, first call CMFCPropertyGridCtrl::CMFCPropertyGridCtrl to construct a property grid object. Then call CMFCPropertyGridCtrl::Create.
void EnableHeaderCtrl(BOOL bEnable=TRUE,LPCTSTR
lpszLeftColumn=_T("Property"),LPCTSTR lpszRightColumn=_T("Value") );
功能:是否显示表头。
bEnable=TRUE显示表头,如下图
bEnable=FALSE,不显示表头,如下图
显而易见,后两个参数指定了表头名。
void EnableDescriptionArea(BOOL bEnable=TRUE );
功能:是事启用描述区
如下图所示
参见例子
MFCFeaturePackSample/NewControls/Page5.cpp /BOOL CPage5::OnInitDialog()
可研究CMFCPropertyGridCtrl控件。理解vs2008后的MFC新界面系统。
主要CMFCPropertyGridCtrl 数据成员
m_ToolTip
CToolTipCtrl
Tooltip control
m_wndHeader
CMFCHeaderCtrl
Property list header control
如图fig-11
m_IPToolTip
CMFCPropertyGridToolTipCtrl
Inplace tooltip control
m_wndScrollVert
CScrollBar
Vertical scroll bar
m_cListDelimeter
TCHAR
Customized list delimeter character
m_rectList
CRect
Properies area
m_lstProps
List of top-level properties
m_pSel
CMFCPropertyGridProperty
Current selection
Custom colors
m_clrBackground
m_clrText
m_clrGroupBackground
m_clrGroupText
………………………
功能:disable,双击,组disable
更改。
CMFCPropertyGridProperty由{name, value}组成。
界面绘制主要代码
[cpp] view plaincopy
OnPaint()
{
OnFillBackground
OnDrawList
}
void CMFCPropertyGridCtrl::OnFillBackground(CDC* pDC, CRect rectClient)
{
……
//CBrush m_brBackground;用指定好的画刷绘制背景
pDC->FillRect(rectClient, &m_brBackground);
……
}
}
void CMFCPropertyGridCtrl::OnDrawList(CDC* pDC)
{
//画中间的分隔线
pDC->MoveTo(nXCenter, m_rectList.top - 1);
pDC->LineTo(nXCenter, m_rectList.bottom);
//得到属性项的链表
const CList
for (POSITION pos = lst.GetHeadPosition(); pos != NULL;)
{
CMFCPropertyGridProperty* pProp = lst.GetNext(pos);
//画每个属性项
if (!OnDrawProperty(pDC, pProp))
{
break;
}
}
}
BOOL CMFCPropertyGridCtrl::OnDrawProperty(CDC* pDC, CMFCPropertyGridProperty* pProp) const
{
if (!pProp->IsEnabled())
{
clrTextOld = pDC->SetTextColor(afxGlobalData.clrGrayedText);
}
CRect rectName = pProp->m_Rect;
CRgn rgnClipName;
CRect rectNameClip = rectName;
rectNameClip.bottom = min(rectNameClip.bottom, m_rectList.bottom);
rgnClipName.CreateRectRgnIndirect(&rectNameClip);
pDC->SelectClipRgn(&rgnClipName);
pProp->OnDrawName(pDC, rectName);
CRect rectValue = pProp->m_Rect;
rectValue.left = nXCenter + 1;
CRgn rgnClipVal;
CRect rectValClip = rectValue;
rectValClip.bottom = min(rectValClip.bottom, m_rectList.bottom);
rgnClipVal.CreateRectRgnIndirect(&rectValClip);
pDC->SelectClipRgn(&rgnClipVal);
pProp->OnDrawValue(pDC, rectValue);
}
void CMFCPropertyGridProperty::OnDrawName(CDC* pDC, CRect rect)
{
int nTextHeight = pDC->DrawText(m_strName, rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_END_ELLIPSIS);
//计算出文字是被截断
m_bNameIsTruncated = pDC->GetTextExtent(m_strName).cx > rect.Width();
}
void CMFCPropertyGridProperty::OnDrawValue(CDC* pDC, CRect rect)
{
CString strVal = FormatProperty();
pDC->DrawText(strVal, rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_END_ELLIPSIS);
}
COLORREF m_clrLine; // Color of the grid lines
BOOL m_bAlphabeticMode; // Use property list in alphabetic (non-"tree") mode
没有皮肤这种概念。所有风格都靠代码画出来。
CMFCPropertyGridCtrl 代码特点。拆分成多个函数。每个函数的代码行数都少,可读性强。
The GetTextExtentPoint32 function computes the width and height of the specified string of text.
///主要事件响应代码///
左键单击
void CMFCPropertyGridCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
SetFocus();
CRect rectClient;
GetClientRect(rectClient);
CMFCPropertyGridProperty::ClickArea clickArea;
CMFCPropertyGridProperty* pHit = HitTest(point, &clickArea);
//与当前的属性项比较,不同则属性项变更
BOOL bSelChanged = pHit != GetCurSel();
SetCurSel(pHit);
if (pHit == NULL)
{
return;
}
switch (clickArea)
{
case CMFCPropertyGridProperty::ClickExpandBox:
pHit->Expand(!pHit->IsExpanded()); //展开/收起子项
break;
case CMFCPropertyGridProperty::ClickName:
pHit->OnClickName(point);
break;
case CMFCPropertyGridProperty::ClickValue:
if (pHit->m_bEnabled)
{
if (EditItem(pHit, &point) && pHit->m_pWndInPlace != NULL)
{
if (pHit->m_rectButton.PtInRect(point))
{
CString strPrevVal = pHit->FormatProperty();
if (::GetCapture() == GetSafeHwnd())
{
ReleaseCapture();
}
pHit->OnClickButton(point);
if (strPrevVal != pHit->FormatProperty())
{
OnPropertyChanged(pHit);
}
}
else if (!bSelChanged || pHit->IsProcessFirstClick())
{
pHit->OnClickValue(WM_LBUTTONDOWN, point);
}
}
}
break;
default:
break;
}
}
//输入鼠标点,判断鼠标点在哪个属性项的矩形边界内,进而测试出鼠标点下的属性项。
CMFCPropertyGridProperty* CMFCPropertyGridCtrl::HitTest(CPoint pt, CMFCPropertyGridProperty::ClickArea* pnArea, BOOL bPropsOnly) const
{
for (POSITION pos = lst.GetHeadPosition(); pos != NULL;)
{
CMFCPropertyGridProperty* pProp = lst.GetNext(pos);
ASSERT_VALID(pProp);
CMFCPropertyGridProperty* pHit = pProp->HitTest(pt, pnArea);
if (pHit != NULL)
{
return pHit;
}
}
}
void CMFCPropertyGridCtrl::SetCurSel(CMFCPropertyGridProperty* pProp, BOOL bRedraw)
{
m_pSel = pProp;
OnChangeSelection//是一个空的虚函数
OnChangeSelection(m_pSel, pOldSelectedItem);
if (pOldSelectedItem != NULL)
{
//CMFCPropertyGridProperty中的空的虚函数
pOldSelectedItem->OnKillSelection(pProp);
}
鼠标单击某项,编辑响应过程
[cpp] view plaincopy
void CMFCPropertyGridCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
case CMFCPropertyGridProperty::ClickValue:
if (pHit->m_bEnabled)
{
if (EditItem(pHit, &point) && pHit->m_pWndInPlace != NULL)
}
BOOL CMFCPropertyGridCtrl::EditItem(CMFCPropertyGridProperty* pProp, LPPOINT lptClick)
{
ASSERT_VALID(this);
ASSERT_VALID(pProp);
if (!EndEditItem())
{
return FALSE;
}
if (pProp->IsGroup() && !pProp->m_bIsValueList)
{
return FALSE;
}
if (pProp->OnEdit(lptClick))
{
pProp->Redraw();
SetCurSel(pProp);
SetCapture();
}
return TRUE;
}
CMFCPropertyGridProperty
{
BOOL m_bEnabled;//是否允许编辑
CWnd* m_pWndInPlace; // Pointer to InPlace editing window
CComboBox* m_pWndCombo; // Pointer to combbox
CSpinButtonCtrl* m_pWndSpin; // Pointer to spin button
CMFCPropertyGridCtrl* m_pWndList; // Pointer to the PropertyList window
CMFCPropertyGridProperty* m_pParent; // Parent property (NULL for top-level properties)
enum ClickArea
{
ClickExpandBox,
ClickName,
ClickValue,
ClickDescription
};
};
禁止扩展
[cpp] view plaincopy
编辑完
[cpp] view plaincopy
出现编辑框
[cpp] view plaincopy
void CMFCPropertyGridCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
if (EditItem(pHit, &point) && pHit->m_pWndInPlace != NULL)
}
BOOL CMFCPropertyGridCtrl::EditItem(CMFCPropertyGridProperty* pProp, LPPOINT lptClick)
{
if (pProp->OnEdit(lptClick))
}
BOOL CMFCPropertyGridProperty::OnEdit(LPPOINT /*lptClick*/)
{
m_pWndInPlace = CreateInPlaceEdit(rectEdit, bDefaultFormat);
}
CMFCPropertyGridProperty子类
CCheckBoxProp
CPasswordProp
CSliderProp
CBoundedNumberPairProp
CBoundedNumberSubProp
CIconListProp
CComboBoxExProp
COwnerDrawDescrProp
CTwoButtonsProp
CCustomDlgProp
响应点击属性项Combox框中的下拉项事件
[cpp] view plaincopy
ON_CBN_SELENDOK(AFX_PROPLIST_ID_INPLACE, &CMFCPropertyGridCtrl::OnSelectCombo)
void CMFCPropertyGridCtrl::OnSelectCombo()
{
…………..
ASSERT_VALID(m_pSel);
m_pSel->OnSelectCombo();
}
void CMFCPropertyGridProperty::OnSelectCombo()
{
ASSERT_VALID(this);
ASSERT_VALID(m_pWndCombo);
ASSERT_VALID(m_pWndInPlace);
int iSelIndex = m_pWndCombo->GetCurSel();
if (iSelIndex >= 0)
{
CString str;
m_pWndCombo->GetLBText(iSelIndex, str);
m_pWndInPlace->SetWindowText(str);
OnUpdateValue();
}
}
CComboBox ClassA
Each message-map entry takes the following form:
ON_Notification( id, memberFxn )
where id specifies the child-window ID of the combo-box control sending the notification and memberFxn is the name of the parent member function you have written to handle the notification.
The parent's function prototype is as follows:
afx_msg void memberFxn( );
ON_CBN_SELENDOK The user selects an item and then either presses the ENTER key or clicks the DOWN ARROW key to hide the list box of a combo box. This notification message is sent before the CBN_CLOSEUP message to indicate that the user's selection should be considered valid. The CBN_SELENDCANCEL or CBN_SELENDOK notification message is sent even if the CBN_CLOSEUP notification message is not sent (as in the case of a combo box with the CBS_SIMPLE style).
例子程序
截图
MFCShellList Control
DisplayFolder("C:\\");
MFCShellTree Control
VS2010新增加(相较于VC6)了一个CMFCShellTreeCtrl类,说实话,这个类确实很好,但是有一点你会发现,在展开某些节点的时候可能会很慢很慢。这严重影响了效率。为什么呢?很长一段时间,一直百思不得其解!甚至抓狂!原来问题出现在一个很小的函数上。
HRESULT CMFCShellTreeCtrl::EnumObjects(HTREEITEM hParentItem, LPSHELLFOLDER pParentFolder, LPITEMIDLIST pidlParent)
{
ASSERT_VALID(this);
ASSERT_VALID(afxShellManager);
LPENUMIDLIST pEnum = NULL;
HRESULT hr = pParentFolder->EnumObjects(NULL, m_dwFlags, &pEnum); if (FAILED(hr) || pEnum == NULL)
{ return hr;
}
LPITEMIDLIST pidlTemp;
DWORD dwFetched = 1; // Enumerate the item's PIDLs: while (SUCCEEDED(pEnum->Next(1, &pidlTemp, &dwFetched)) && dwFetched)
{
TVITEM tvItem;
ZeroMemory(&tvItem, sizeof(tvItem)); // Fill in the TV_ITEM structure for this item: tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN; // AddRef the parent folder so it's pointer stays valid: pParentFolder->AddRef(); // Put the private information in the lParam: LPAFX_SHELLITEMINFO pItem = (LPAFX_SHELLITEMINFO)GlobalAlloc(GPTR, sizeof(AFX_SHELLITEMINFO));
ENSURE(pItem != NULL);
pItem->pidlRel = pidlTemp;
pItem->pidlFQ = afxShellManager->ConcatenateItem(pidlParent, pidlTemp);
pItem->pParentFolder = pParentFolder;
tvItem.lParam = (LPARAM)pItem;
CString strItem = OnGetItemText(pItem);
tvItem.pszText = strItem.GetBuffer(strItem.GetLength());
tvItem.iImage = OnGetItemIcon(pItem, FALSE);
tvItem.iSelectedImage = OnGetItemIcon(pItem, TRUE);
问题出现在这里,接下来要检查文件的属性,判断是否有子文件夹。正常情况下我们其实是不需要这么多属性的,前两个足够了SFGAO_HASSUBFOLDER 和 SFGAO_FOLDER 。将后面的其他属性全部屏蔽,再运行程序时,就会发现很快了。
// Determine if the item has children: /*DWORD dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_DISPLAYATTRMASK | SFGAO_CANRENAME | SFGAO_FILESYSANCESTOR;*/
DWORD dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER ;
pParentFolder->GetAttributesOf(1, (LPCITEMIDLIST*) &pidlTemp, &dwAttribs);
tvItem.cChildren = (dwAttribs & (SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR)); // Determine if the item is shared: if (dwAttribs & SFGAO_SHARE)
{
tvItem.mask |= TVIF_STATE;
tvItem.stateMask |= TVIS_OVERLAYMASK;
tvItem.state |= INDEXTOOVERLAYMASK(1); //1 is the index for the shared overlay image } // Fill in the TV_INSERTSTRUCT structure for this item: TVINSERTSTRUCT tvInsert;
tvInsert.item = tvItem;
tvInsert.hInsertAfter = TVI_LAST;
tvInsert.hParent = hParentItem;
InsertItem(&tvInsert);
dwFetched = 0;
}
pEnum->Release(); return S_OK;
}
2014/10/16 :今天有网友提问如何在MFC程序中使用CMFCShellTreeCtrl,其实很简单。首先新建一个基于CMFCShellTreeCtrl的MFC类,然后将新建的类和控件绑定。CMFCShellTreeCtrl有几个比较重要的虚函数:
/* 获取Item的文字*/
virtual CString OnGetItemText(LPAFX_SHELLITEMINFOpItem);
/* 获取Item的图标 */
virtual intOnGetItemIcon(LPAFX_SHELLITEMINFO pItem, BOOL bSelected);
/* 枚举目录下所有的文件 */
virtual HRESULT EnumObjects(HTREEITEMhParentItem, LPSHELLFOLDER pParentFolder, LPITEMIDLIST pidlParent);
其中第3个函数就是上文提到的可以修改的函数。但是有一点需要注意的是,MFC框架内使用了一个全局变量:
externCShellManager* afxShellManager;
这个变量并没有被MFC框架导出,所以在我们自己的源码中是不能引用这个变量的。解决这个问题也很简单。
afxShellManager被定义在afxshellmanager.cpp文件中,而且要求全局范围内只有一个变量。
CShellManager* afxShellManager = NULL;
CShellManager::CShellManager() {
// 实际上要求CShellManager在全局范围内只有一个变量
ENSURE(afxShellManager== NULL);
afxShellManager= this;
…
}
所以我们在MFC框架生成的App类的InitInstance函数中能够看到:
CShellManager pShellManager = new CShellManager;
delete pShellManager;
我们只需要将此处局部性质的pShellManager提高到全局范围内,然后在程序中使用它。
我们说使用它,不是说将所有使用afxShellManager变量的地方全部改为pShellManager,而是仅限于我们自己的项目代码,MFC框架的源代码是不能被更改的,而且是不应被更改的。
MFCLink Control
MFCPropertyGrid Control
手机扫一扫
移动阅读更方便
你可能感兴趣的文章