目录
一、什么是对话框
二、模式对话框
1.添加打开菜单
2.添加Dialog对话框资源
3.获取线条宽度
1.对话框获取线条宽度
2.滑块框获取
4.模拟动画图标
5.选项框颜色
6.示例效果
我在上篇文章中已经搞定了绘制一些简单图形的功能使用MFC做一个低配版画图软件,但是绘制图形的线条样式都是固定的,那么有没有方法能够保证可以随时改变这样线条样式呢?本篇,我将使用对话框来实现动态的样式改变。
最终效果
与用户进行交互的控件,如文件对话框,字体对话框,颜色对话捐给等,一般用于搞事,提醒等。
大体上说就是一种和用户交互的单独窗口,在这里我为大家介绍其中的一个对话框——模式对话框
模式对话框在其显示时,整个程序会暂停,直到关闭该对话框,我们上面使用的就是模式对话框。
首先我们添加一个打开模式对话框的菜单
编辑菜单和ID
给菜单项添加COMMAND消息事件
我们先搞定调节线条宽度的功能,在这里为大家提供两种方法,一种是通过文本对话框获取;另一种是滑块框获取。
采用文本方式获取线条宽度Static Text 和 Edit Control
第一步:添加控件并更改控件描述,给对话框资源添加ID为 ID_DLG_SETUP
第二步:绑定对话框资源
给对话框资源添加一个类
第三步:补充消息处理函数并测试
void CMFCApplication6View::OnTypeSetup()
{
//弹出对话框选项
Setup conf;
//调用成员函数的调用 模式对话框并返回对话框结果
conf.DoModal();
}
测试
第四步:获取文本框输入内容
给文本编辑框绑定变量
第五步:补充消息处理函数,将文本框中获取数据放入设置画笔函数中
m_nLineWidth为view类成员变量,存储画笔宽度
void CMFCApplication6View::OnTypeSetup()
{
//弹出对话框选项
Setup conf;
//调用成员函数的调用 模式对话框并返回对话框结果
//conf.DoModal();
//每次进入对话框时,文本框显示的是上一次设置的画笔宽度
conf.m_nLineWidth = m_nLineWidth;
//用户按下“确定”按钮 DoModal()返回 IDOK
if (IDOK == conf.DoModal()) {
m_nLineWidth = conf.m_nLineWidth;
}
}
测试
第一步:添加Slider Control控件
自动打勾:指定滑块在其值范围内对于每一个增量都有一个刻度线
打勾标记:指定滑块显示刻度线
工具提示:滑块滑到某个位置时显示其值
第二步:设置滑块范围
通过类向导添加OnInitDialog虚函数,也可以直接右击滑块控件添加(更方便)
在虚函数中
BOOL Setup::OnInitDialog()
{
CDialogEx::OnInitDialog();
//获得滑块控件
//GetDlpItem传入ID,获得相应的控件或窗口
CSliderCtrl* slider = (CSliderCtrl*)GetDlgItem(IDC_SLIDER1);
//修改滑块属性
slider->SetRange(1, 30);
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
其中GetDlgItem返回CWnd* 类型,要将其强转成CSliderCtrl*类型
测试
第四步:获取滑块的值
剩下的操作与上面的文本对话框一样,也是给滑块和view类分别添加成员变量,用来存储画笔宽度
测试
完成调节线条宽度后接着就是更改画笔样式,画笔样式使用按钮来让用户选择。
第一步:添加:Group Box
在对话框中添加一个Group Box,并修改其标题为“线型”。Group Box起到一个框框的作用,让接下来添加的按钮看起来是一组的。
如下效果
第二步:添加Radio Button
接着添加三个单选按钮Radio Button,并按上面同样方法求改其描述
此时这三个按钮代表三组选项(也就是可以一次选多个),而我们只是想让用户一次只能选择一个,那么我们就要将三个按钮绑定为一组
第三步:按钮绑定为一组
选择第一个按钮,将其“组”属性改为TRUE,那么从这个按钮往下都会成为一组,直到有下一个“组”出现。这个时候这三个选项就会变成一组,每次也只能选择一项。
第四步:添加成员变量
这个时候我们就可以为这一组添加一个成员变量来记录用户选择的是哪一个按钮。
同样在view文件中也按此方式添加,用来接收对话框中的值
第五步:补充消息处理函数
void CMFCApplication6View::OnTypeSetup()
{
//弹出对话框选项
Setup conf;
//调用成员函数的调用 模式对话框并返回对话框结果
//conf.DoModal();
//每次进入对话框时,文本框显示的是上一次设置的画笔宽度
conf.m_nLineWidth = m_nLineWidth;
conf.m_nLineStyle = m_nLineStyle;
//用户按下“确定”按钮 DoModal()返回 IDOK
if (IDOK == conf.DoModal()) {
m_nLineWidth = conf.m_nLineWidth;
m_nLineStyle = conf.m_nLineStyle;
}
}
当用户选择了第一个按钮会返回0,选择第二个按钮会返回1,依次类推,而PS_SOLID定义的表示也正好是从0开始的,所以我们可以直接将m_nLineStyle填入所有的画笔设置函数中即可
最终效果
选项框颜色我决定不直接将显示在对话框内,而是通过一个按钮,当用户点击按钮时才去打开颜色对话框
添加一个按钮Button,修改其标题为“颜色”
下面就是给按钮添加相应事件,添加相应事件对快捷的方法就是双击按钮,当然也可以通过类向导来添加
void Setup::OnBnClickedButton1()
{
//打开颜色对话框
CColorDialog colDlg;
colDlg.DoModal();
}
测试
按照同样的方法,分别给控件和view 类添加成员变量,再将相应函数补充完整。
void Setup::OnBnClickedButton1()
{
//打开颜色对话框
CColorDialog colDlg;
colDlg.DoModal();
if (IDOK == colDlg.DoModal()) {
//返回选中的颜色
m_color = colDlg.m_cc.rgbResult;
}
}
void CMFCApplication6View::OnTypeSetup()
{
//弹出对话框选项
Setup conf;
//调用成员函数的调用 模式对话框并返回对话框结果
//conf.DoModal();
//每次进入对话框时,文本框显示的是上一次设置的画笔宽度
conf.m_nLineWidth = m_nLineWidth;
conf.m_nLineStyle = m_nLineStyle;
conf.m_color = m_color;
//用户按下“确定”按钮 DoModal()返回 IDOK
if (IDOK == conf.DoModal()) {
m_nLineWidth = conf.m_nLineWidth;
m_nLineStyle = conf.m_nLineStyle;
m_color = conf.m_color;
}
}
再将程序中设置画笔颜色改为m_color就可以了
与上面添加控件不同的是,我们使用按钮进行颜色选择时是弹出了一个新的对话框,这就导致我们不能像上面那样在view里的相应事件函数中使用conf.m_color = m_color;来保证每次打开都会显示上一次的选择结果(上面那种方式每次打开颜色对话框时都会停留在黑色位置)
我们需要在对应的颜色框响应函数中修改(内容也略微有所不同)
void Setup::OnBnClickedButton1()
{
//打开颜色对话框
CColorDialog colDlg;
colDlg.m_cc.Flags |= CC_RGBINIT;
colDlg.m_cc.rgbResult = m_color;
if (IDOK == colDlg.DoModal()) {
//返回选中的颜色
m_color = colDlg.m_cc.rgbResult;
}
}
这样就能保证每次打开颜色框时都是保留上次选择的颜色
每次用户更改线条样式时都会在“示例”区绘制出线条样式。
给对话框添加Group Box,并更改其标题为“示例”
当我们修改线型或者粗细或者颜色时,示例框中的线条也要跟着改变,这就需要我们给这些调整线条样式的控件添加响应函数
我们先给选择线条样式的三个按钮添加
void Setup::OnBnClickedRadio1()
{
}
void Setup::OnBnClickedRadio2()
{
}
void Setup::OnBnClickedRadio3()
{
}
每当这些控件发生改变时,就要在“示例”区重新绘制线条,所谓重新绘制,也就是说之气的那个“页面”(对话框页面)不需要了,可以去掉了。Invalidate() 默认参数为TRUE,也就是先发送背景刷新消息,再刷新前景
void Setup::OnBnClickedRadio1()
{
//指定是否清除在更新区域内的背景
//使窗口内容无效
//每当这些控件发生改变时就要去重新绘制线,所谓重新绘制,就是之前的不需要了,可以去掉了
//Invalidate( )可以使得当前的区域无效,就要去重新绘制
Invalidate();
}
void Setup::OnBnClickedRadio2()
{
Invalidate();
}
void Setup::OnBnClickedRadio3()
{
Invalidate();
}
给控件类(Setup)添加WM_PAINT消息
void Setup::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CDialogEx::OnPaint()
//在示例区域绘制一条线
CPen pen(m_nLineStyle, m_nLineWidth, m_color);
CPen* pOldPen = dc.SelectObject(&pen);
//获得“示例”区位置(通过“示例”的ID)“示例”区ID为IDC_SAMPLE
CRect rect;
//得到对话框某一组件或控件的实例,并通过GetWindowRect将组件坐标信息存入到rect中
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);
//GetWindowRect得到的是屏幕坐标,而我们想要的是工作区坐标,因此需要转换
ScreenToClient(rect);
//不要太靠近左右边,与左右边相差20距离
dc.MoveTo(rect.left + 20, rect.top + rect.Height() / 2);
dc.LineTo(rect.right - 20, rect.top + rect.Height() / 2);
dc.SelectObject(pOldPen);
}
这个时候在“示例”区中已经显示了直线,但是当我们选择线型时直线却没有改变,原因在于虽然我们在下面代码中已经 将控件ID和成员变量绑定,但是在OnPaint()函数中这个还没有生效
void Setup::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
//数据交换,传入控件ID和变量,将控件值绑定到变量中
DDX_Slider(pDX, IDC_SLIDER1, m_nLineWidth);
DDX_Radio(pDX, IDC_RADIO1, m_nLineStyle);
}
我们需要在OnPaint()中增添UpdateData(); 实现同步控件和绑定的成员变量
void Setup::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CDialogEx::OnPaint()
//同步控件和绑定的成员变量
UpdateData();
//在示例区域绘制一条线
CPen pen(m_nLineStyle, m_nLineWidth, m_color);
CPen* pOldPen = dc.SelectObject(&pen);
//获得“示例”区位置(通过“示例”的ID)
CRect rect;
//得到对话框某一组件或控件的实例,并通过GetWindowRect将组件坐标信息存入到rect中
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);
//GetWindowRect得到的是屏幕坐标,而我们想要的是工作区坐标,因此需要转换
ScreenToClient(rect);
//不要太靠近左右边,与左右边相差20距离
dc.MoveTo(rect.left + 20, rect.top + rect.Height() / 2);
dc.LineTo(rect.right - 20, rect.top + rect.Height() / 2);
dc.SelectObject(pOldPen);
}
接下来就是同样的方法,当颜色或者线条粗细发生改变时也要去重新绘制线条
在颜色对话框中,当有colDlg.DoModal() = IDOK就要重新绘制,添加Invalidate( )
void Setup::OnBnClickedButton1()
{
//打开颜色对话框
CColorDialog colDlg;
colDlg.m_cc.Flags |= CC_RGBINIT;
colDlg.m_cc.rgbResult = m_color;
if (IDOK == colDlg.DoModal()) {
//返回选中的颜色
m_color = colDlg.m_cc.rgbResult;
Invalidate();
}
}
滑块有水平滑动和垂直滑动两个事件,而我们的调节线条粗细使用的是水平滑动。如果我们要捕获这个动作,则需要添加WM_HSCROLL消息处理函数
void Setup::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
//响应滑块控件的水平移动消息
Invalidate();
CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
}
最后这个对话框功能就大功告成了