第六章 菜单
一、创建菜单项
资源管理器→menu项→双击IDR_MAINFRAME打开菜单编辑器
pop-up类型的菜单是弹出式菜单,不能进行命令响应。
ID号的命名规则:
IDM菜单资源
IDC光标资源
IDI图标资源
二、为菜单项添加响应函数
打开ClassWizard【类向导】,
-
区别Message中的COMMAND和UPDATE_COMMAND_UI
COMMAND处理该菜单对应的功能。
UPDATE_COMMAND_UI处理菜单应对的用户界面。
-
菜单项响应顺序:view→doc→mainframe→App
-
Windows消息的分类
1、标准消息:除 WM_COMMAND 之外,所有以 WM_开头的消息都是标准消息。
CWnd类,都可以接收到这类消息。2、命令消息:来自菜单、加速键或工具栏按钮的消息。这类消息都以 WM_COMMAND 形式呈现。
在 MFC 中,通过菜单项的标识(ID)来区分不同的命令消息;在 SDK 中,通过消息的wParam 参数识别。从 CCmdTarget 派生的类,都可以接收到这类消息。3、通告消息:由控件产生的消息,例如按钮的单击、列表框的选择等。目的是为了向其父窗口(通常是对话框)通知事件的发生。这类消息也是以 WM_COMMAND 形式呈现。从CCmdTarget 派生的类,都可以接收到这类消息。
实际上,CWnd 类派生于 CCmdlTarget类。也就是说凡是从 CWnd 派生的类,它们既可以接收标准消息,也可以接收命令消息和通告消息。而对于那些从 CCmdTarget 派生的类,则只能接收命令消息和通告消息,不能接收标准消息。 -
菜单消息响应过程:mainframe类接收消息→view首先处理,若不行→doc→mainframe→App
三、基本菜单操作
1、了解菜单结构
程序的主菜单属于框架窗口,因此要在框架类的OnCreate中编程。
2、标记菜单
获取程序菜单栏:CWnd类 CMenu* GetMenu( ) const;
获取子菜单:CMenu类CMenu* GetSubMenu( int nPos ) const;
参数是子菜单的索引号,注意分隔栏也占据索引号。
添加或移除标记:CMenu类UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck );
Parameters
nIDCheckItem:Specifies the menu item to be checked, as determined by nCheck.
nCheckSpecifies :how to check the menu item and how to determine the position of the item in the menu. The nCheck parameter can be a combination of MF_CHECKED or MF_UNCHECKED with MF_BYPOSITION or MF_BYCOMMAND flags.
* MF_BYCOMMAND 指定第一个参数通过ID
* MF_BYPOSITION 指定第一个参数通过索引号
* MF_CHECKED 设置复选标记
* MF_UNCHECKED 移走复选标记
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
3、默认菜单项(粗体)
注意子菜单只能有一个默认菜单项。
CMenu类BOOL SetDefaultItem( UINT uItem, BOOL fByPos = FALSE );
GetMenu()->GetSubMenu(0)->SetDefaultItem(2,MF_BYPOSITION);
4、图形标记菜单
CMenu类:BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked );
pBmpUnchecked是取消选中时的位图;pBmpchecked是选中状态时的位图。
获取系统的信息度量:int GetSystemMetrics( int nIndex);
其中,参数用于指定希望获取哪部分的系统信息。SM_CXMENUCHECK和SM_CYMENUCHECK用于获取菜单项标记图形的默认尺寸。
CString类的Format函数类似C中的printf。
CBitmap m_bitmap;//头文件全局变量
m_bitmap.LoadBitmap(IDB_BITMAP1);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0, MF_BYPOSITION, &m_bitmap, &m_bitmap);
Ctring str;
str.Format("x = %d , y = %d", GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));
MessageBox(str);
5、禁用菜单项
CMenu类:UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );
nEnable:
MF_BYCOMMAND Specifies that the parameter gives the command ID of the existing menu item. This is the default.
MF_BYPOSITION Specifies that the parameter gives the position of the existing menu item. The first item is at position 0.
MF_DISABLED Disables the menu item so that it cannot be selected but does not dim it.
MF_ENABLED Enables the menu item so that it can be selected and restores it from its dimmed state.
MF_GRAYED Disables the menu item so that it cannot be selected and dims it.
常把MF_GRAYED和MF_DISABLED放在一起使用。
发现不能正常禁用?
// NOTE: m_bAutoMenuEnable is set to FALSE in the constructor of
// CMainFrame so no ON_UPDATE_COMMAND_UI or ON_COMMAND handlers are
// needed, and CMenu::EnableMenuItem() will work as expected.
即在CMainFrame构造函数中添加m_bAutoMenuEnable = FALSE;
6、移除和装载菜单
CWnd类:BOOL SetMenu( CMenu* pMenu );
CMenu* pMenu 指向一个新的菜单对象,若值为NULL,则移除当前菜单。
/*移除菜单*/
SetMenu(NULL);
/*装载菜单资源*/
Cmenu menu;//1、框架类成员变量2、若是局部变量,结尾一定要加Detach函数
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();//menu是局部变量的情况,将菜单句柄与菜单对象分离
四、MFC菜单命令更新机制
菜单项状态的维护依赖于CN_UPDATE_COMMAND_UI消息,该消息智能用于菜单项,不能用于永久显示的顶级菜单(即弹出式菜单)。
在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间添加宏以捕获消息
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)//自动添加的
ON_WM_CREATE()
ON_UPDATE_COMMAND_UI(ID_FILE_NEW, &CMainFrame::OnUpdateFileNew)
END_MESSAGE_MAP()
void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)//消息响应函数
{
// TODO: 在此添加命令更新用户界面处理程序代码
pCmdUI->Enable();
}
/*CCmdUI类决定一个菜单项是否可以使用(Enable),是否有标记(SetCheck),改变菜单项文本(SetText)*/
/*CCmdUI类有成员变量m_nID保存对象ID和m_nIndex保存对象索引*/
如果只是利用禁用菜单项,菜单项变灰禁用,但工具栏按钮依然能够使用。而如果使用命令更新机制,则都不能使用。
五、快捷菜单
(上下文菜单、右键菜单)
为CView类添加一个WM_CONTEXTMENU消息处理函数,当鼠标右键单击窗口时,就会调用该函数。
显示快捷菜单函数: BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );
void CmenuView::OnContextMenu(CWnd* pWnd, CPoint point)
{
// TODO: 在此处添加消息处理程序代码
CMenu popmenu;
popmenu.LoadMenu(IDR_POPMENU1); //读取资源
popmenu.GetSubMenu(0)->TrackPopupMenu(
TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, point.x, point.y, this);
}
/*
TPM_LEFTALIGN定位弹出菜单,使其左侧与 x 指定的坐标对齐。*/
六、动态菜单操作
1、针对子菜单的动态操作
1、把一个新的子菜单添加到菜单栏末尾。CMenu类:BOOL AppendMenu ( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL);
CMenu类:CreateMenu()创建一个弹出菜单,并与CMenu对象关联
CMenu m_menu;//成员变量
m_menu.CreateMenu();
GetMenu()->AppendMenu(MF_POPUP, (UINT)m_menu.m_hMenu, "TEST");
2、两个子菜单中间插入子菜单。CMenu类: BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
用法类似AppendMenu
3、删除子菜单或菜单项。CMenu类:BOOL DeleteMenu( UINT nPosition, UINT nFlags );
//删除子菜单
GetMenu()->DeleteMenu(0,MF_BYPOSITION );
//删除菜单项
GetMenu()->GetSubMenu(1)->DeleteMenu(0,MF_BYPOSITION );
2、针对菜单项的动态操作
1、插入菜单项同样使用AppendMenu和InsertMenu
m_menu.AppendMenuA(MF_STRING, 111, "hello");//往弹出菜单TEST中添加菜单项
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING, 113, "hello1");//往文件子菜单末尾添加菜单项
GetMenu()->GetSubMenu(0)->InsertMenu(1, MF_BYPOSITION | MF_STRING , 112 , "VC编程");
3、为 动态添加的菜单项 添加响应函数
//在头文件resource.h中创建菜单资源ID
#define IDM_HELLO 111
//源文件中
m_menu.AppendMenuA(MF_STRING, IDM_HELLO, "hello");
//遵循消息映射机制:增加3处代码实现消息响应
//1、在响应这个菜单命令的类中添加响应函数原型,位于声明消息映射宏之上,(第11行所示)
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnHello();
DECLARE_MESSAGE_MAP()
//2、在源文件的消息映射表中添加消息映射,消息映射宏是ON_COMMAND宏,(第17行所示)
//注意:消息映射表中,代码结尾不加分号
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_COMMAND(IDM_HELLO,OnHello)
END_MESSAGE_MAP()
//3、添加消息响应函数定义
void CMainFrame::OnHello()
{
MessageBox("Hello");
}
第七章 对话框(一)
1、基本知识
控件:通常作为对话框的子窗口创建
对话框的种类:
模态对话框:对话框显示时,程序暂停执行,直到关闭对话框。
非模态:显示对话框时,可以执行其他任务。
2、创建和显示
CDialog对话框资源类
1.新建一个MFC类与该对话框关联:双击新建的对话框。
新建对话框资源后有两个按钮,功能一样(关闭对话框),返回值不同。
OK IDOK OnOK
Cancel IDCANCEL OnCancel
2.DoDataExchange()
:完成对话框数据的交换和校验。
3.在视类中
**CDialog::DoModal
**创建并显示模态对话框
**CDialog::EndDialog
**关闭模态对话框
3、创建动态按钮
1.视类→工具箱→对话框编辑器 创建一个按钮。
2.为该按钮添加功能
类向导→选择该对象对应的BN_CLICKED(按钮被单击的消息)消息
在CTestDlg类添加
private:
CButton m_btn;//动态按钮对象
BOOL isbtncreate;//判断动态按钮是否被创建,在构造函数中初始化为0
void CTestDlg::OnClickedBtnAdd()
{
// TODO: 在此添加控件通知处理程序代码
//动态创建按钮
//static BOOL isbtncreate = 0;
if (isbtncreate == 0)//判断动态按钮是否被创建,
{
//CButton::Create创建按钮
m_btn.Create(_T("new"), WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, CRect(0, 0, 100, 100), this, 123);
isbtncreate = 1;
}
else
{
m_btn.DestroyWindow();//销毁窗口
isbtncreate = 0;
}
}
初始化变量的两种方法
1.创建成员变量,并在构造函数中初始化;
2.创建静态局部变量同时初始化。
4、对话框控件
1.静态文本控件
IDC_STATIC不能响应消息,可通过修改ID来响应消息
**CWnd::GetDlgItem
**获取对话框子控件的指针
**CWnd::GetWindowText
**获取窗口文本
**CWnd::SetWindowText
**设置窗口文本
void CTestDlg::OnClickedNumber1()
{
// TODO: 在此添加控件通知处理程序代码
CString str;
if (GetDlgItem(IDC_NUMBER1)->GetWindowText(str),str == "数值1")
//逗号表达式,返回最后一个表达式的值
{
GetDlgItem(IDC_NUMBER1)->SetWindowText("Number1");
}
else
{
GetDlgItem(IDC_NUMBER1)->SetWindowText("数值1");
}
}
最后在属性中选中通知(Notify)选项,否则不能向其父窗口发送鼠标事件。
2.编辑框控件
🌟GetDlgItem()获取编辑框框窗口对象的指针
1.法一:GetDlgItem→Get(Set)WindowText
void CTestDlg::OnClickedBtnAdd()
{
// TODO: 在此添加控件通知处理程序代码
//编辑框控件法一: CWnd::GetDlgItem()->Get(Set)WindowText();获取(设置)指定窗口文本
int num1, num2, num3;
char ch1[10], ch2[10], ch3[10];
GetDlgItem(IDC_EDIT1)->GetWindowText(ch1, 10);//(指向存放字符串内存的指针,最大字符数目)
GetDlgItem(IDC_EDIT2)->GetWindowText(ch2, 10);
num1 = atoi(ch1);//atoi字符串->数值
num2 = atoi(ch2);
num3 = num1 + num2;
_itoa_s(num3, ch3, 10);//itoa数值->字符串,这里的10代表10进制
GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);//将ch3中的字符串显示在编辑框中
}
2.法二:Get(Set)DlgItemText
=GetDlgItem+Get(Set)WindowText
void CTestDlg::OnClickedBtnAdd()
{
// TODO: 在此添加控件通知处理程序代码
//编辑框控件法二:CWnd::Get(Set)DlgItemText()获取指定控件上的文本
int num1 = 0, num2 = 0, num3 = 0;
char ch1[10], ch2[10], ch3[10];
GetDlgItemText(IDC_EDIT1, ch1,10);
GetDlgItemText(IDC_EDIT2, ch2,10);//=GetDlgItem + GetWindowText
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
_itoa_s(num3, ch3, 10);
SetDlgItemText(IDC_EDIT3, ch3);
}
改写:
void CTestDlg::OnClickedBtnAdd()
{
// TODO: 在此添加控件通知处理程序代码
//编辑框控件法二:CWnd::Get(Set)DlgItemText()获取指定控件上的文本
int num1 = 0, num2 = 0, num3 = 0;
// char ch1[10], ch2[10], ch3[10];
CString str1, str2, str3;
GetDlgItemText(IDC_EDIT1, str1);
GetDlgItemText(IDC_EDIT2, str2);
num1 = atoi(str1);
num2 = atoi(str2);
num3 = num1 + num2;
// str3 = (CString)num3;
//错误 C2440 “类型强制转换” : 无法从“int”转换为“CString”
//Format是CString类的一个成员函数,它通过格式操作使任意类型的数据转换成一个字符串。
str3.Format("%d",num3);
SetDlgItemText(IDC_EDIT3, str3);
}
3.**CWnd::Get(Set)DlgItemInt
**返回指定控件的文本并将其转换为一个整数值。
void CTestDlg::OnClickedBtnAdd()
{
int num1 = 0, num2 = 0, num3 = 0;
num1 = GetDlgItemInt(IDC_EDIT1);
num2 = GetDlgItemInt(IDC_EDIT2);
num3 = num1 + num2;
SetDlgItemInt(IDC_EDIT3, num3);
}
4.控件与整型变量相关联(注意DoDataExchange和UpdateData一起用)
步骤:项目→类向导→成员变量→IDC_EDIT1→添加变量→类别(值)→变量名称→变量类型→其他(最大值、最小值等)
系统为我们创建的代码:
/*1、头文件定义*/
// 编辑框与数值变量相关联
int m_num1;
int m_num2;
int m_num3;
/*2、构造函数初始化*/
CTestDlg::CTestDlg(CWnd* pParent /*=nullptr*/)
: CDialog(IDD_DIALOG1, pParent)
//数值变量初始化
, m_num1(0)
, m_num2(0)
, m_num3(0)
{
}
/*3、DoDataExchange将对话框控件与类成员变量相关联,要配合UpDateDate使用*/
void CTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//控件与数值变量关联
DDX_Text(pDX, IDC_EDIT1, m_num1);//DDX_前缀用于数据交换
DDV_MinMaxInt(pDX, IDC_EDIT1, 0, 100);//设定数值范围,DDV_数据校验
DDX_Text(pDX, IDC_EDIT2, m_num2);
DDX_Text(pDX, IDC_EDIT3, m_num3);
}
自己编写的代码:
void CTestDlg::OnClickedBtnAdd()
{
//法四:控件与数值变量相关联
UpdateData();//和DoDataExchange配合使用,参数为TRUE(缺省):函数获取对话框数据
m_num3 = m_num1 + m_num2;
UpdateData(FALSE);//参数为FALSE:以变量的值来初始化对话框控件
}
自动消息提示框:
5.控件与控件变量相关联
系统自动添加
/*1、头文件定义*/
//编辑框与控件变量相关联
CEdit m_edit1;
CEdit m_edit2;
CEdit m_edit3;
/*2、DoDataExchange将对话框控件与类成员变量相关联,要配合UpDateDate使用*/
void CTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//控件与控件变量关联
DDX_Control(pDX, IDC_EDIT1, m_edit1);
DDX_Control(pDX, IDC_EDIT2, m_edit2);
DDX_Control(pDX, IDC_EDIT3, m_edit3);
手动添加
//法五:控件与控件变量相关联
int num1 = 0, num2 = 0, num3 = 0;
char ch1[10], ch2[10], ch3[10];
m_edit1.GetWindowText(ch1, 10);
m_edit2.GetWindowText(ch2, 10);
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
_itoa_s(num3, ch3, 10);
m_edit3.SetWindowText(ch3);
6.**SendMessage()
**发送消息
int num1 = 0, num2 = 0, num3 = 0;
char ch1[10], ch2[10], ch3[10];
::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd, WM_GETTEXT, 10, (LPARAM)ch1);//获取编辑框窗口对象的指针
::SendMessage(m_edit2.m_hWnd, WM_GETTEXT, 10, (LPARAM)ch2);//第一个参数直接利用控件变量获取句柄
//Windows系统中,获取文本的消息是WM_GETTEXT,将系统指定窗口的文本复制到调用者提供的一个缓存中
//wParam指定复制的字符数,lParam保存窗口文本的缓存地址
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
_itoa_s(num3, ch3, 10);
m_edit3.SendMessage(WM_SETTEXT, 0, (LPARAM)ch3);
//WM_SETTEXT设置窗口文本的消息,lParam指定设置窗口文本的字符串地址
7.**SendDlgItemMessage()
**直接给对话框的子控件发送消息
//法七:
int num1 = 0, num2 = 0, num3 = 0;
char ch1[10], ch2[10], ch3[10];
SendDlgItemMessage(IDC_EDIT1, WM_GETTEXT, 10, (LPARAM)ch1);
SendDlgItemMessage(IDC_EDIT2, WM_GETTEXT, 10, (LPARAM)ch2);
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
_itoa_s(num3, ch3, 10);
SendDlgItemMessage(IDC_EDIT3, WM_SETTEXT, 0, (LPARAM)ch3);
补充:
//设置复选内容
SendDlgItemMessage(IDC_EDIT3, EM_SETSEL, 1, 3);
// EM_GETSEL获得编辑框中的复选内容
//EM_SETSEL设置……,EM表示Edit Control Message
//wParam接收复选内容开始位置,lParam ……结束位置.
//wParam=0,lParam=-1,表示所有文本
m_edit3.SetFocus();//设置光标
5、对话框的伸缩
CWnd::SetWindowPos
**BOOL SetWindowPos (const CWnd**\* *pWndInsertAfter*, **int** *x*, **int** *y*, **int** *cx*, **int** *cy*, **UINT** *nFlags***)//** 设置窗口位置、大小、z次序等
CWnd::GetDlgItem
This method retrieves a pointer to the specified control or child window in a dialog box or other window. The pointer returned is usually cast to the type of control identified by nID.
CWnd GetDlgItem (int* nID )const;
CWnd::SetDlgItemText
void SetDlgItemText(int nID**, LPCTSTR** lpszString**);//** 将nID中的文本设置为lpszString
CWnd::GetWindowRect
void GetWindowRect(LPRECT lpRect**)const;//** 获得窗口尺寸
CRect::IsRectNull
CRect::IsRectEmpty
void CTestDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString str;
if (GetDlgItemText(IDC_BUTTON1,str),str == "收缩<<")
{
SetDlgItemText(IDC_BUTTON1, "括展>>");
}
else
{
SetDlgItemText(IDC_BUTTON1, "收缩<<");
}
static CRect rectlarge;//原对话框尺寸
static CRect rectsmall;//切割后的尺寸
if (rectlarge.IsRectNull())//判断矩形区域是否为空
{
CRect rectseparator;//分割线的尺寸
GetDlgItem(IDC_SEPARATOR)->GetWindowRect(rectseparator);//获得分割线的尺寸
GetWindowRect(rectlarge);//获得原对话框的尺寸
rectsmall.top = rectlarge.top;
rectsmall.left = rectlarge.left;
rectsmall.right = rectlarge.right;
rectsmall.bottom = rectseparator.bottom;//将对话框底部的尺寸更改
}
if (str == "收缩<<")
{
SetWindowPos(NULL, 0, 0, rectsmall.Width(), rectsmall.Height(), SWP_NOMOVE | SWP_NOZORDER);//设置窗口的位置和大小
}
else
{
SetWindowPos(NULL, 0, 0, rectlarge.Width(), rectlarge.Height(), SWP_NOMOVE | SWP_NOZORDER);
}
}
6、输入焦点的传递
void CTestDlg::OnBnClickedOk()//注释掉默认的OnOK
{
GetNextDlgTabItem(GetFocus())->SetFocus();//按照TAB顺序循环查找【格式】->【TAB顺序】
// CDialog::OnOK();
}
WNDPROC prevproc;//窗口过程类型,接收先前的窗口过程。窗口过程函数是全局函数,里面的函数不能再用CWnd的成员函数,只能用API函数
LRESULT CALLBACK WinEnterProc//windowproc函数
(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
if (uMsg == WM_CHAR && wParam == 0x0d)
{
// ::SetFocus(::GetNextWindow(hwnd, GW_HWNDNEXT));
// ::SetFocus(::GetWindow(hwnd, GW_HWNDNEXT));
::SetFocus(::GetNextDlgTabItem(::GetParent(hwnd), hwnd, 0));//SetFocus设置焦点,GetFocus得到焦点
//GetNextWindow、GetWindow获得下一个窗口的句柄
//GetNextDlgTabItem按照TAB顺序转移焦点
// return 1;
}
else//按下的按键不是回车,让先前的窗口过程来处理
{
return prevproc(hwnd,uMsg,wParam,lParam);
}
}
BOOL CTestDlg::OnInitDialog()//对话框及其子控件创建完成将要显示之前调用。虚函数。
CDialog::OnInitDialog();
// TODO: 在此添加额外的初始化
//SetWindowLong修改指定窗口的属性,本例修改已指定的过程函数
prevproc = (WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd, GWL_WNDPROC/*设置新的窗口过程地址*/, (LONG)WinEnterProc/*新的窗口过程地址*/);
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
7、默认按钮的说明
收缩按钮设置为默认按钮,即在其属性对话框中选择default button选项,就不会再由CTestDlg类的OnOK函数来响应。
当用户按下回车键时,windows将查看对话框中是否存在默认按钮, 如果没有默认按钮,就会调用虚拟的OnOK函数, 即没有默认ok按钮。这个默认OnOK函数的ID是IDOK。而不是IDC_OK.
第八章 对话框2
1、逃跑按钮
逃跑按钮创建过程
1、创建新类(专注于逃跑功能)
- 存储地址的变量
- OnMouseMove()
2、CTestDlg.h控件与控件变量关联,即创建了新类的对象
3、CTestDlg.cpp的OnInitDialog()中两个对象存储对方首地址
4、鼠标移动OnMouseMove()接收WM\_MOUSEMOVE消息,执行程序
2、属性表单的创建
一个属性表单是由一个或多个属性页组成,属性页对应MFC中的CPropertyPage类,一个属性页的标题就是选项卡的名称。
步骤:
1、创建属性页
2、添加控件
组框Group Box
单选按钮Radio Button
复选框Check Box
列表框控件List Box
静态文本控件Static Box
组合框Combo Box
simple,能输入,总是显示列表框
dropdown,能输入只有单击下拉列表后,列表框才弹出
droplist,只读,只能从下来列表选择内容
3、为对话框资源创建新类
1.创建新类步骤:参考1,在“添加MFC类”中添加类名与ID号一致,基类中选择相应的类型
属性页CPropertyPage
属性表单CPropertySheet
2.在属性表单(CPropSheet)中,创建三个属性页对象,并在构造函数中初始化。用AddPage将属性页添加进属性表单,构造函数指明了2中实例化对象的方法(ID/字符串)
CPropSheet::AddPage将属性页添加到属性表单中
OnPropertysheet::DoModal()创建模态属性表单
/*定义*/
//自定义成员变量
PROP1 m_prop1;
PROP2 m_prop2;
PROP3 m_prop3;
/*初始化*/
CPropSheet::CPropSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
:CPropertySheet(nIDCaption, pParentWnd, iSelectPage)//用ID号来构造属性表单
{
//初始化属性页
AddPage(&m_prop1);//CPropSheet::AddPage将属性页添加到属性表单中
AddPage(&m_prop2);
AddPage(&m_prop3);
}
CPropSheet::CPropSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
:CPropertySheet(pszCaption, pParentWnd, iSelectPage)//用字符串来构造属性表单
{
AddPage(&m_prop1);
AddPage(&m_prop2);
AddPage(&m_prop3);
}
3.在视类中创建菜单命令响应函数,用字符串实例化属性表单对象,并用DoModal创建模态属性表单
void CpropView::OnPropertysheet()
{
// TODO: 在此添加命令处理程序代码
CPropSheet propsheet("属性表单");//使用字符串构造属性表单对象
// propsheet.SetWizardMode();//创建向导样式时添加
propsheet.DoModal();//创建模态属性表单
}
3、向导的创建
1、更改向导底部按钮CPropertySheet::SetWizardButtons
BOOL PROP1::OnSetActive()//右键PROP1类->类向导->虚函数->OnSetActive->编辑代码
{
// TODO: 在此添加专用代码和/或调用基类
CPropertySheet* psheet = (CPropertySheet*)GetParent();//属性页的父窗口属性表单,并将CWnd*转换为CPropertySheet*
psheet->SetWizardButtons(PSWIZB_NEXT);//CPropertySheet::SetWizardButtons设置对话框上的按钮
return CPropertyPage::OnSetActive();
}
PROP2,PROP3过程类似,SetWizardButtons参数不同。
1、设置第一个属性页
将第一个单选按钮设置为Group属性以添加成员变量。第一个单选按钮设置为Group后,之后的按钮和这个按钮属于同一组,直到遇到下一个Group。同一组内的控件关联的成员变量的值依次为1、2、3…
单击【下一步】,调用OnWizardNext()虚函数
/*PROP1.h*/
int m_occupation;
/*PROP1.cpp*/
//构造函数
PROP1::PROP1()
: CPropertyPage(IDD_PROP1)
, m_occupation(-1)
{
}
//数据交换
void PROP1::DoDataExchange(CDataExchange* pDX)
{
CPropertyPage::DoDataExchange(pDX);
DDX_Radio(pDX, IDC_RADIO1, m_occupation);//单选按钮和成员变量之间的数据交换
}
//判断
LRESULT PROP1::OnWizardNext()//单击【下一步】,调用OnWizardNext()虚函数
{
// TODO: 在此添加专用代码和/或调用基类
UpdateData();//系统通过调用UpdateData()来调用DoDataExchange以完成控件与变量的数据交换
//TRUE从控件得到成员变量的值。 FAUSE用成员变量的值初始化控件
if (m_occupation == -1)//用户没有选择职业
{
MessageBox("请选择职业!");
return -1;//OnWizardNext()返回0,进入下一个属性
//返回-1,禁止属性页变更
}
if (m_workaddress == "" )//工作地点为空
{
MessageBox("请选择工作地点!");
return -1;
}
return CPropertyPage::OnWizardNext();//进入下一个页面
}
BOOL PROP1::OnInitDialog()
{
CPropertyPage::OnInitDialog();
// TODO: 在此添加额外的初始化
CListBox* listbox = (CListBox*)GetDlgItem(IDC_LIST1);
listbox->AddString("北京");//CListBox::AddString添加字符串到列表框
listbox->AddString("天津");
listbox->AddString("上海");
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
2、设置第二个属性页
操作类似第一个属性页
LRESULT PROP2::OnWizardNext()
{
// TODO: 在此添加专用代码和/或调用基类
UpdateData();
if (m_football || m_basketball || m_volleyball || m_swim)
{
return CPropertyPage::OnWizardNext();
}
else
{
MessageBox("请选择那你的兴趣爱好!");
return -1;//返回-1,禁止属性页变更
}
}
3、设置第三个属性页
CComboBox::AddString向组合框的列表框中添加字符串选项
CComboBox::SetCurSel选择列表框中的一个字符串,并将其显示在该组合框的编辑框中
BOOL PROP3::OnInitDialog()
{
CPropertyPage::OnInitDialog();
//组合框控件由一个编辑框和一个列表框组成
//CComboBox::AddString向组合框的列表框中添加字符串选项
//CComboBox::SetCurSel(set current selection)选择列表框中的一个字符串,并将其显示在该组合框的编辑框中
CComboBox* combobox = (CComboBox*)GetDlgItem(IDC_COMBO1);
combobox->AddString("1000元以下");
combobox->AddString("1000元-2000元");
combobox->AddString("2000元-3000元");
combobox->AddString("3000元以上");//取消属性框中的排序功能以关闭自动排序
combobox->SetCurSel(0);
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
CComboBox::GetCurSel得到当前选项的索引值
CComboBox::GetLBText得到对应索引值的文本并存储在字符串中
/*提取用户的薪资选项,并存储在m_strsalary中*/
BOOL PROP3::OnWizardFinish()
{
// TODO: 在此添加专用代码和/或调用基类
int index;
CComboBox* combobox = (CComboBox*)GetDlgItem(IDC_COMBO1);
index = combobox ->GetCurSel();//得到当前选项的索引值
combobox->GetLBText(index, m_strsalary);//得到对应索引值的文本并存储在字符串m_strsalary中
return CPropertyPage::OnWizardFinish();
}
4、将结果在视类显示
memset初始化数组
Invalidate();//让视类窗口无效,从而引起重绘,然后在OnDraw中完成信息的输
/*propView.h*/
//自定义成员变量
int m_ioccupation; //职业
CString m_strworkaddress;//工作地点
BOOL m_blike[4];//爱好
CString m_strsalary;//薪资水平
/*propView.cpp*/
//构造函数中初始化
CpropView::CpropView() noexcept
{
// TODO: 在此处添加构造代码
m_ioccupation = -1; //职业,对于一个组,未选为-1
m_strworkaddress = "";//工作地点
memset(m_blike , 0 , sizeof(m_blike));//爱好,将m_blike内存的sizeof(m_blike)个字节设为0
m_strsalary = "";//薪资水平
}
/*一般情况下,CPropertySheet 类的 DoModal 函数的返回值是 IDOK 或 IDCANCEL。
但是如果属性表单已经被创建为向导了,那么该函数的返回值将是 ID_WIZFINISH 或 IDCANCEL.
因此,在程序中应该对属性表单对象的 DoModal 函数的返回值进行判断,
如果返回的是【完成】按钮的 ID:ID_WIZFINISH,那么才进行输出处理。*/
void CpropView::OnPropertysheet()//菜单项响应函数
{
// TODO: 在此添加命令处理程序代码
CPropSheet propsheet("属性表单");//使用字符串构造属性表单对象
propsheet.SetWizardMode();//设置向导样式
// propsheet.DoModal();//创建模态属性表单
if (ID_WIZFINISH == propsheet.DoModal())//属性表单被创建向导,DoModal()返回ID_WIZFINISH
{
m_ioccupation = propsheet.m_prop1.m_occupation;//向导.属性页.属性页成员
m_strworkaddress = propsheet.m_prop1.m_workaddress;
m_blike[0] = propsheet.m_prop2.m_football;
m_blike[1] = propsheet.m_prop2.m_basketball;
m_blike[2] = propsheet.m_prop2.m_volleyball;
m_blike[3] = propsheet.m_prop2.m_swim;
m_strsalary = propsheet.m_prop3.m_strsalary;
Invalidate();//让视类窗口无效,从而引起重绘,然后在OnDraw中完成信息的输出
}
}
//窗口重绘
void CpropView::OnDraw(CDC* pDC)
{
CpropDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
CFont font;
font.CreatePointFont(100, "宋体",pDC);//创建字体
CFont* pfont;
pfont = pDC->SelectObject(&font);//将字体选入设备描述表,并保存旧字体
//职位
CString position;
position = "你的职业:";
switch (m_ioccupation)
{
case 0:
position += "程序员";
break;
case 1:
position += "系统工程师";
break;
case 2:
position += "项目经理";
break;
}
pDC->TextOut(0, 0, position);//将文本展示在视类窗口
TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
//工作地点
CString workplace;
workplace = "你的工作地点:";
workplace += m_strworkaddress;
pDC->TextOut(0, tm.tmHeight, workplace);
//兴趣爱好
CString hobby;
hobby = "你的爱好:";
if (m_blike[0])
hobby += "足球 ";
if (m_blike[1])
hobby += "篮球 ";
if (m_blike[2])
hobby += "排球 ";
if (m_blike[3])
hobby += "游泳 ";
pDC->TextOut(0, tm.tmHeight*2, hobby);
//薪资
CString salary;
salary = "你的薪资水平:";
salary += m_strsalary;
pDC->TextOut(0, tm.tmHeight*3, salary);
pDC->SelectObject(pfont);
}
第九章 定制应用程序外观
4、工具栏编程ToolBar
1.在工具栏上添加、删除按钮
创建按钮:按钮和菜单项用一样的ID
按钮之间的分隔符:用鼠标把按钮向右拖动一段距离
删除按钮:将按钮脱出工具栏
2.创建工具栏
分析系统创建的工具栏
cmainframe头文件中
CToolBar m_wndToolBar;
cmainframe.cpp中
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
......
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("未能创建工具栏\n");
return -1; // 未能创建
}
....
// TODO: 如果不需要可停靠工具栏,则删除这三行
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);//工具栏对象的成员函数EnableDocking,表示工具栏对象可以停靠
//CBRS_ALIGN_ANY允许停靠在任意位置
EnableDocking(CBRS_ALIGN_ANY);//CFrameWnd对象的EnableDocking成员函数,表示主框架窗口可以被停靠
DockControlBar(&m_wndToolBar);//工具栏停靠在主框架窗口上
创建自定义工具栏(参照系统工具栏)
1.新建ToolBar资源
2.构造ToolBar对象
3.调用Create函数创建工具栏,并将其与ToolBar对象关联
4.LoadToolBar加载工具栏
5.设置停靠位置
头文件
CToolBar m_newtoolbar;//2.构造ToolBar对象
源文件
if (!m_newtoolbar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_RIGHT | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_newtoolbar.LoadToolBar(IDR_TOOLBAR1))
//3.调用Create函数创建工具栏,并将其与ToolBar对象关联
//4.LoadToolBar加载工具栏
{
TRACE0("未能创建工具栏\n");
return -1; // 未能创建
}
//5.设置停靠位置
m_newtoolbar.EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_newtoolbar);
改进方法**CFrameWnd::ShowControlBar
**隐藏或显示指定的控制条
void CMainFrame::OnViewNewtoolbar()
{
/*
if (!m_newtoolbar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_RIGHT | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_newtoolbar.LoadToolBar(IDR_TOOLBAR1))
{
TRACE0("未能创建工具栏\n");
return -1; // 未能创建
m_newtoolbar.EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_newtoolbar);
*/
ShowControlBar(&m_newtoolbar, !m_newtoolbar.IsWindowVisible(), 1);
}
void CMainFrame::OnUpdateViewNewtoolbar(CCmdUI* pCmdUI)
{
// 在菜单项前添加标记,见第六章
pCmdUI->SetCheck(m_newtoolbar.IsWindowVisible());
}
5、状态栏StatusBar
步骤:
1.在资源视图String Table中添加响应的ID
2.在源文件中将响应的ID添加进indicator(状态行指示器)中
系统创建的状态栏
CStatusBar m_wndStatusBar;//定义默认状态栏对象
static UINT indicators[] =
{
ID_SEPARATOR, // 状态行指示器
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
// IDS_TIMER,
// IDS_PROGRESS,
};
//OnCreate()中
if (!m_wndStatusBar.Create(this))//创建状态栏对象
{
TRACE0("未能创建状态栏\n");
return -1; // 未能创建
}
m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));//设置状态指示器(数组设置状态栏内容,数组元素个数)
在OnTimer()中添加
//在状态栏显示动态变化的系统时间
CTime time;//接收系统时间
CString str;//系统时间文本
CClientDC dc(this);//调用GetTextExtent()
CSize sz;//接收时间字符串大小
int indicatorindex = 0;//indicator索引
time = CTime::GetCurrentTime();//获取系统时间保存进time中
str = time.Format("%H:%M:%S");//将time中的时间格式化
indicatorindex = m_wndStatusBar.CommandToIndex(IDS_TIMER);//获取ID在indicator中的索引
sz = dc.GetTextExtent(str);//获取文本显示宽度
m_wndStatusBar.SetPaneInfo(indicatorindex, IDS_TIMER, SBPS_NORMAL, sz.cx);//设置状态栏右侧小窗口属性
m_wndStatusBar.SetPaneText(indicatorindex, str);//将str显示到indicator数组对应的小窗口
6、进度栏
# CProgressCtrl::Create//进度栏类CProgressCtrl
BOOL Create( DWORD dwStyle**,** const RECT& rect**,** CWnd* pParentWnd**,** UINT nID**);//** 创建进度栏
# CProgressCtrl::SetPos
int SetPos( int nPos**);//** 设置进度栏当前进度
# CStatusBar::GetItemRect
void GetItemRect(int nIndex**,LPRECT** lpRect**)const;//** 得到状态栏小窗格大小
🌟补充消息响应过程
1.头文件声明(定义)函数afx_msg LRESULT OnProgress(WPARAM, LPARAM);
2.消息与函数关联ON_MESSAGE(UM_PROGRESS, &OnProgress)
3.实现函数LRESULT CMainFrame::OnProgress(WPARAM wParam, LPARAM lParam)
void CMainFrame::OnPaint()//窗口大小发生变化->窗口重绘->重新获得rect
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CFrameWnd::OnPaint()
CRect rect;
m_wndStatusBar.GetItemRect(5, &rect);//获得窗格大小并储存在rect中
if (!m_progress.m_hWnd)//如果句柄没有值,说明对象没有创建
{
m_progress.Create(WS_VISIBLE | WS_CHILD | PBS_SMOOTH, rect, &m_wndStatusBar, 123);//创建进度栏,WS_CHILD:The window is a child window.
}
else
{
m_progress.MoveWindow(rect);//移动窗格,The CRect object or RECT structure that specifies the new size and position.
}
m_progress.SetPos(50);//设置进度
}
7、在状态栏上显示鼠标当前位置
void CstyleView::OnMouseMove(UINT nFlags, CPoint point)
//视类中捕获鼠标移动的消息WM_MOUSEMOVE
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CString str;
str.Format("x = %d y = %d",point.x, point.y);//格式化鼠标位置
((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
/*上述代码中,首先格式化鼠标当前位置的信息。然后为了把该信息显示在
状态栏的第一个窗格上,需要获取状态栏对象。而状态栏对象是在框架类窗口中定义的,同时,
框架类窗口是视类窗口的父窗口,因此在视类对象中通过调用 GetParent 函数就可以得到
视类的父窗口,即框架窗口。因为该函数返回的是一个CWnd 类型的指针,而这里需要的
是 CMainFrame 类型的指针,所以需要进行一个转换。然后利用框架窗口对象去调用该对
象内部的状态栏成员变量:m_wndStatusBar,以得到状态栏对象,*/
/*因为上述代码用到了框架类的类型,所以要在头文件中包含框架类头文件*/
CView::OnMouseMove(nFlags, point);
}
十、绘图控制
1、简单绘图
CPen pen
SelectObject
GetStockObject
FromHandle
SetPixel
Rectangle
Ellipse
/*头文件*/
//自定义成员变量
private:
UINT m_nDrawType;//标记用户选择那种图形
CPoint m_ptOrigin;//保存鼠标左键按下的点
/*构造函数*/
CGraphicView::CGraphicView() noexcept
{
// TODO: 在此处添加构造代码
m_nDrawType = 0;//初始化绘图图形类型
m_ptOrigin = 0;
}
/*视类消息处理程序*/
void CGraphicView::OnDot()//点
{
// TODO: 在此添加命令处理程序代码
m_nDrawType = 1;
}
void CGraphicView::OnLine()//直线
{
// TODO: 在此添加命令处理程序代码
m_nDrawType = 2;
}
void CGraphicView::OnRectangle()//矩形
{
// TODO: 在此添加命令处理程序代码
m_nDrawType = 3;
}
void CGraphicView::OnEllipse()//椭圆
{
// TODO: 在此添加命令处理程序代码
m_nDrawType = 4;
}
void CGraphicView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_ptOrigin = point;//保存鼠标左键按下的位置
CView::OnLButtonDown(nFlags, point);
}
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)//鼠标左键抬起,实现绘图功能
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);//绘图操作首先要有DC对象
CPen pen(PS_SOLID, 10, RGB(255,0,0));//线条的颜色由DC中的画笔颜色确定,构造CPen对象并指定其颜色
dc.SelectObject(&pen);//将画笔选入设备描述表
//加载资源用LoadBitmap等,CDC对象用SelectObject(&xxx)
CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//GetStockObject调用NULL_BRUSH创建透明画刷
//CBrush的静态成员变量FromHandle将画刷句柄转化为指向画刷对象的指针
//FromHandle的参数是HBRUSH类型,强制类型转化
dc.SelectObject(pBrush);//将画刷选入设备描述表
switch (m_nDrawType)
{
case 1:
dc.SetPixel(point, RGB(255,0,0));//在指定的点设置一个像素
break;
case 2:
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
break;
case 3:
dc.Rectangle(CRect(m_ptOrigin,point));//利用CRect对象绘制矩形
break;
case 4:
dc.Ellipse(CRect(m_ptOrigin, point));//绘制椭圆
default:
break;
}
CView::OnLButtonUp(nFlags, point);
}
注意将CDC对象加入设备miaoshub;
注意静态成员函数的调用方式:要声明是哪个类的。
2、线宽线型(设置对话框)
1、将设置对话框创建一个新的对话框类
2、在主菜单添加设置菜单项并设置消息响应函数
3、控件关联无符号整型变量,View类创建同类型变量以保存其值
void CGraphicView::OnSetting()
{
// TODO: 在此添加命令处理程序代码
CSettingDlg dlg;//实例化设置对话框对象
dlg.m_nLineWidth = m_nLineWidth;//保持之前设置过的线宽
dlg.m_nLineStyle = m_nLineStyle;//保持之前设置过的线型
if (IDOK == dlg.DoModal())
{
m_nLineWidth = dlg.m_nLineWidth;//保存新设置的值
m_nLineStyle = dlg.m_nLineStyle;//保存设置的线型,WINGDI.h定义了一些符号常量,包括线型
//本例设置的对话框的线型排序正好按照WINGDI.h的顺序定义
}
}
3、颜色对话框
CColorDialog默认颜色对话框类了解(m_cc、CHOOSECOLOR、rgbResult、Flags (CC_RGBINIT))
void CGraphicView::OnColor()
{
// TODO: 在此添加命令处理程序代码
CColorDialog colordlg;//CColorDialog颜色对话框类
colordlg.m_cc.Flags |= CC_RGBINIT;//设置颜色对话框初始选择的颜色,需要设置该对话框的CC_RGBINIT标记
/*实际上,当在创建 CColorDialog 对象 dlg 时,它的数据成员 m_cc 中的 Flags 成员已经具有了一些初始的默认标记。
当我们将 CC_RGBINTT 标记直接赋给 Flags 成员时,就相当于将 Flags 成员初始默认的标记都去掉了。
这里不能给 Flags 标记直接赋值,应利用或操作(|)将CC_RGBINIT 标记与 Flags 先前的标记组合起来。*/
colordlg.m_cc.rgbResult = m_ctr;//保持之前设置过的颜色
if (IDOK == colordlg.DoModal())//创建颜色对话框
{
m_ctr = colordlg.m_cc.rgbResult;//CColorDialog类有一个结构体类型的变量m_cc
//CHOOSECOLOR结构体的rgbResult变量保存了用户选择的颜色
//将用户选择的颜色保存在m_ctr中
}
}
4、字体对话框
DeleteObject释放这个字体资源
CFont::CreateFontIndirect利用lpLogFont指向的LOGFONT结构体中的一些特征初始化CFont对象
Invalidate()引起窗口重绘
头文件
CFont m_font;//字体对象
CString m_strFontName;//保存所选字体的名称
源文件
void CGraphicView::OnFont()
{
// TODO: 在此添加命令处理程序代码
CFontDialog fontdlg;//字体对话框对象
if (IDOK == fontdlg.DoModal())
{
if (m_font.m_hObject)
/*利用CGdiobject 对象的数据成员 m_hObject 来判断 m_font 对象是否已经与某个字体资源相关联了,
该变量保存了与CGdiObject对象相关联的Windows GDI 的资源句柄。
如果已经有关联了,则调用 DeleteObject 释放这个字体资源*/
{
m_font.DeleteObject();
}
m_font.CreateFontIndirect(fontdlg.m_cf.lpLogFont);//CFont::CreateFontIndirect利用lpLogFont指向的LOGFONT结构体中的一些特征初始化CFont对象
m_strFontName = fontdlg.m_cf.lpLogFont->lfFaceName;//lfFaceName保存了字体名字
Invalidate();
/*窗口的客户区无效意味着需要重绘,例如,如果一个被其它窗口遮住的窗口变成了前台窗口,
那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。
MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。
视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。*/
/*系统会在多个不同的时机发送WM_PAINT消息:
当第一次创建一个窗口时,当改变窗口的大小时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗口时,等等,
这些动作都是由系统管理的,应用只是被动地接收该消息,在消息处理函数中进行绘制操作;
大多数的时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,
这一般是通过InvalidateRect和 InvalidateRgn函数来完成的。*/
}
}
5、示例对话框
void CSettingDlg::OnChangeLineWidth()
//对编辑框控件中的文本改变时,会向父窗口发送EN_CHANGE消息
{
Invalidate();//让窗口重绘以在OnPaint中完成示例线条的绘制
}
void CSettingDlg::OnClickedRadio1()
//单击Radio Button时,按钮会向对话框发送BN_CLICKED消息
{
Invalidate();
}
void CSettingDlg::OnClickedRadio2()
{
// TODO: 在此添加控件通知处理程序代码
Invalidate();
}
void CSettingDlg::OnClickedRadio3()
{
// TODO: 在此添加控件通知处理程序代码
Invalidate();
}
void CSettingDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CDialog::OnPaint()
UpdateData();//当控件与一个成员变量相关联时,如果想让控件上的值反应到成员变量上,必须调用UpdateData()
CPen pen(m_nLineStyle, m_nLineWidth, m_clr);
dc.SelectObject(&pen);
CRect rect;
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);
/*要想在组框中绘图,那么首先要得到组框的矩形区域范围。
这可以通过调用GetDlgltem 函数来得到指向组框窗口对象的指针,
然后利用 GetWindowRect 函数获得组框窗口矩形区域的大小,参数是指向CRect或RECT结构体的变量,接收屏幕坐标
需要提醒读者注意的是,这里不能直接调用 GetWindowRect 函数,否则得到的将是对话框的矩形区域大小。*/
ScreenToClient(&rect);//得到矩形区域的大小后,将其原点从屏幕坐标转化为客户坐标(即将应用程序坐标转化为设置对话框坐标)
dc.MoveTo(rect.left+20,rect.top+rect.Height()/2);//组框左上角的y值+矩形区域高度的一半,即将线条移动到示例窗口中间
dc.LineTo(rect.right - 20, rect.top + rect.Height() / 2);
}
6、改变对话框及其控件的背景和文本WM_CTLCOLOR
1、改变对话框及其控件的背景
//头文件声明
CBrush m_brush;//画刷
//构造函数初始化
m_clr = RGB(255,0,0);
m_brush.CreateSolidBrush(RGB(0,0,255));//利用CreateSolidBrush函数将m_brush初始化画刷颜色
//消息响应函数
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)//每一个控件(对话框和子控件)在绘制时都发送WM_CTLCOLOR消息
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: 在此更改 DC 的任何特性
// TODO: 如果默认的不是所需画笔,则返回另一个画笔
if (IDC_LINE_STYLE == pWnd->GetDlgCtrlID())//判断是否为线型组框
{
pDC->SetTextColor(RGB(255,0,0));//设置文本颜色为红色
pDC->SetBkMode(TRANSPARENT);//将控件上的文字背景设置为透明
return m_brush;//如果想要改变背景颜色,只需要自定义一个画刷,然后让OnColor函数返回这个画刷句柄即可
}
if (IDC_LINE_WIDTH == pWnd->GetDlgCtrlID())
{
pDC->SetTextColor(RGB(255, 0, 0));
// pDC->SetBkMode(TRANSPARENT);
pDC->SetBkColor(RGB(255, 0, 0));//设置窗口背景色
return m_brush;
}
return hbr;
}
2、改变控件的文本
//头文件声明
private:
CFont m_font;//改变控件上的文本字体
//构造函数初始化
m_font.CreatePointFont(200,"华文行楷");//初始化字体
//消息响应函数
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)//每一个控件(对话框和子控件)在绘制时都发送WM_CTLCOLOR消息
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: 在此更改 DC 的任何特性
// TODO: 如果默认的不是所需画笔,则返回另一个画笔
if (IDC_TEXT == pWnd->GetDlgCtrlID())
{
pDC->SelectObject(&m_font);//将字体选入设备描述表->改变字体
}
return hbr;
}
3、改变按钮的背景色及文本
7、位图的显示
BOOL CGraphicView::OnEraseBkgnd(CDC* pDC)//擦除背景时,系统发送WM_ERASEBKGND消息
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
BITMAP bmp;//定义BITMAP结构体变量
bitmap.GetBitmap(&bmp);//CBitmap::GetBitmap用位图信息填充BITMAP结构体
CDC dcCompatible;//创建兼容DC
dcCompatible.CreateCompatibleDC(pDC);//兼容DC(源DC)和当前DC兼容
dcCompatible.SelectObject(&bitmap);//将位图选入兼容DC,从而确定兼容DC显示表面的大小
CRect rect;
GetClientRect(&rect);//获得目的DC客户区大小
// pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0, SRCCOPY);//BitBlt()将源DC的位图1:1复制到目的DC
pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0,bmp.bmWidth,bmp.bmHeight, SRCCOPY);
//StretchBlt复制位图并实现拉伸或压缩;bmp.bmWidth,bmp.bmHeight存储源矩形的宽度和高度
return TRUE;//已经擦除过窗口背景了,返回非零值
// return CView::OnEraseBkgnd(pDC);//将窗口背景擦除
}
//构造函数初始化