-
2017-01-11 19:48:07
1.项目-->类向导-->添加类-->MFC类,其他操作如图所示。
2.在CwzdSplash.h的public:下声明CBitmap m_bitmap;
3.在CwzdSplash.cpp下。
从神处复制如下代码:http://blog.csdn.net/leixiaohua1020/article/details/12753857
- void CWzdSplash::Create(UINT nBitmapID)
- {
- m_bitmap.LoadBitmap(nBitmapID);
- BITMAP bitmap;
- m_bitmap.GetBitmap(&bitmap);
- //CreateEx(0,AfxRegisterWndClass(0),"",WS_POPUP|WS_VISIBLE|WS_BORDER,0,0,bitmap.bmWidth,bitmap.bmHeight,NULL,0);
- CreateEx(0,
- AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
- NULL, WS_POPUP | WS_VISIBLE, 0, 0, bitmap.bmWidth, bitmap.bmHeight, NULL , NULL);
- }
- void CWzdSplash::OnPaint()
- {
- // TODO: 在此处添加消息处理程序代码
- // 不为绘图消息调用 CWnd::OnPaint()
- CPaintDC dc(this); // device context forpainting
- BITMAP bitmap;
- m_bitmap.GetBitmap(&bitmap);
- CDC dcComp;
- dcComp.CreateCompatibleDC(&dc);
- dcComp.SelectObject(&m_bitmap);
- // draw bitmap
- dc.BitBlt(0,0,bitmap.bmWidth,bitmap.bmHeight,&dcComp,0,0,SRCCOPY);
- }
在Dlg.cpp文件下#include<CwzdSplash.h>,然后在OnInitDialog()中写如下代码。- void CWzdSplash::OnTimer(UINT_PTR nIDEvent)
- {
- // TODO: 在此添加消息处理程序代码和/或调用默认值
- //CWnd::OnTimer(nIDEvent);
- DestroyWindow(); //销毁初始画面窗口
- }
-
- CWzdSplash wndSplash; //创建启动窗口类的实例
- wndSplash.Create(IDB_SPLASH); //IDB_SPLASH为位图文件ID
- wndSplash.CenterWindow();
- wndSplash.UpdateWindow(); //send WM_PAINT
- Sleep(1500);
- wndSplash.DestroyWindow();//销毁初始画面窗口
-
更多相关内容 -
AS对话框
2020-07-06 17:45:28(一)警告对话框 AlertDialog: 一个可以有0到3个按钮, 一个单选框或复选框的列表的对话框. 警告对话框可以创建大多数的交互界面, 是推荐的类型. (二)进度对话框 ProgressDialog: 显示一个进度环或者一个进度条. ...一.相关概念
一个对话框一般是一个出现在当前Activity之上的一个小窗口. 处于下面的Activity失去焦点, 对话框接受所有的用户交互. 对话框一般用于提示信息和与当前应用程序直接相关的小功能.
Android API 支持下列类型的对话框对象:
(一)警告对话框 AlertDialog: 一个可以有0到3个按钮, 一个单选框或复选框的列表的对话框. 警告对话框可以创建大多数的交互界面, 是推荐的类型.
(二)进度对话框 ProgressDialog: 显示一个进度环或者一个进度条. 由于它是AlertDialog的扩展, 所以它也支持按钮.
(三)日期选择对话框 DatePickerDialog: 让用户选择一个日期.
(四)时间选择对话框 TimePickerDialog: 让用户选择一个时间二.AlertDialog的创建和使用
AlertDialog是Dialog的一个直接子类,使用AlertDialog,我们可以显示一个标题,最多3个按钮操作,以及一组选择框或者是自己定义的弹出框。
AlertDialog要使用Builder设计模式来创建对象 。(一)Dialog的创建和使用
在时间应用中的对话框基本不用Dialog而是用它的子类。
这里简单使用一下Dialog,代码如下:// 普通的对话框,普通的对话框功能很少,除非重写类
public void bt1(View v) {
// 创建对话框对象
Dialog dialog = new Dialog(this);
// 创建对话框内容
dialog.setTitle(“对话框的标题”);
// 显示对话框
dialog.show();}
运行后显示结果:
可以看到有一个标题的对话框,而且这里没有设置内容的属性,Dialog的使用方法和功能都是很少的,不建议使用。
(二)比较正常的AlertDialog的创建和使用
代码设计:// 警告对话框,需要用Builder方法创建
public void bt2(View v) {
// 这里的属性可以一直设置,因为每次设置后返回的是一个builder对象
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 设置提示框的标题
builder.setTitle(“提示标题”).
// 设置提示框的图标
setIcon(R.drawable.ic_launcher).
// 设置要显示的信息
setMessage(“文本的提示信息:你妈喊你回家吃饭了!”).
// 设置确定按钮
setPositiveButton(“确定”, new OnClickListener() {@Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "选择就确定哦", 0).show(); } }). // 设置取消按钮,null是什么都不做 setNegativeButton("取消", null). // 设置退出按钮,在中间的按钮 setNeutralButton("退出", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 退出程序 finish(); } }); // 生产对话框 AlertDialog alertDialog = builder.create(); // 显示对话框 alertDialog.show(); }
程序运行后显示的结果:
这里如果点击取消,对话框消失,什么都没有做。
如果点击退出,会直接退出程序。
如果点击确定,会弹出一个土司,提示你。
这里可以根据用户的选择做出相应的行为。(三)设计一个选择菜单样式的对话框
代码设计://数据源 String[] array = new String[] { "音乐", "体育", "舞蹈", "看书" }; // 显示一个菜单的对话框选项,点击选择菜单后,菜单会消失 // 匿名类去创建 public void bt3(View v) { final AlertDialog.Builder builder = new AlertDialog.Builder(this); // 设置标题 builder.setTitle("选择你最喜欢的课程"). // 设置可选择的内容,并添加点击事件 setItems(array, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // which代表的是选择的标签的序列号 Toast.makeText(MainActivity.this, "选择" + array[which], 0).show(); } }). // 产生对话框,并显示出来 create().show(); }
程序运行后显示的结果:
设置菜单是builder的setItem方法来完成的。
点击某一个菜单选项后对话框会自动消失,所以这里设置按钮也是没有什么用处的。
这里还能监听点击菜单的事件,获取用户的选择内容。(四)设置一个单选按钮菜单的对话框
其实这个对话框和上面的菜单对话框差不多,只是样式改变了,还有一点不同的是点击一个选项后对话框是不会消失的,所以必须要设置按钮,来使对话框消失。代码设计:
// 单选按钮的对话框,选择一个按钮值后,
// 对话框也是不会自动消失,需要自己关闭
// 要设置按钮,点击按钮后对话框才会消失
public void bt4(View v) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 设置标题
builder.setTitle(“选择你最喜欢的课程”).
// 设置可选择的内容,并添加点击事件
// 第一个参数是可以选择的单选框的内容
// 第二个参数是默认选中的项
// 第三个参数是点击选中的监听器
setSingleChoiceItems(array, 0, new OnClickListener() {@Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "选择" + array[which], 0).show(); } }). // 因为对话框不会自动消失,所以要添加按钮的必要 // 设置确定按钮 setPositiveButton("确定", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "选择确定", 0).show(); } }). // 取消,什么都不做,对话框消失而已 setNegativeButton("取消", null). // 产生对话框,并显示出来 create().show(); }
运行后的结果:
设置单选菜单是builder的setSingleChoiceItem方法来完成的。
这里要可以设置两个监听事件,一个是单选按钮某一个选项被选中时触发的,另一个是点击按钮时触发的事件。
程序中每次选中一个单选按钮都会弹出一个吐司,提示你选择的内容,点击确定后也会显示一个吐司。
1
2
3(五)多选项的对话框设计
多选框的显示和单选的显示也是有点相似的,都是需要设置按钮来使对话框消失。
代码如下:// 多选按钮的对话框
// 对话框也是不会自动消失,需要自己关闭
// 要设置按钮,点击按钮后对话框才会消失
public void bt5(View v) {// 多选按钮的默认状态 boolean[] checkedItems = { true, false, false, true }; // 多选按钮选中后保存的数据 final List<String> list = new ArrayList<String>(); // 默认的数据 list.add("音乐"); list.add("看书"); // 创建对话框的builder对象 final AlertDialog.Builder builder = new AlertDialog.Builder(this); // 设置标题 builder.setTitle("选择你最喜欢的课程"). // 设置可选择的内容,并添加点击事件 // 第一个参数是可以选择的选框的内容 // 第二个参数是一个布尔数组,如果是true代表的是默认选中 // 第三个参数是点击选中的监听器 setMultiChoiceItems(array, checkedItems, new OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { // 这里无论某个选项是被选中后被取消都会触发该方法 // which代表的是选中的是多少游标值的选项 // isChecked是代表该选项是或否选中了 // 判断如果集合中有某个数据,就把他移除,否则就添加数据 // 注意这里数选择之后的状态 if (isChecked) { // 添加数据 list.add(array[which]); } else { // 移除数据 list.remove(array[which]); } } }). // 因为对话框不会自动消失,所以要添加按钮的必要 // 设置确定按钮 setPositiveButton("确定", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "你选中了" + list, 0) .show(); } }). // 取消,什么都不做,对话框消失而已 setNegativeButton("取消", null). // 产生对话框,并显示出来 create().show(); }
程序运行后的结果:
设置多选菜单是builder的setMultiChoiceItem方法来完成的。
这里要可以设置两个监听事件,一个是选项框的某一个选项的状态改变时触发的,另一个是点击按钮时触发的事件。
程序中每次改变选项的内容,保存数据的集合都会改变,点击确定后也会显示一个吐司,显示集合里面的内容。选择全部内容后,确定,显示的结果:
(六)使用适配器显示对话框
使用适配器显示对话框和显示菜单对话框类似,都是选中对应选项后,对话框自动消失,但是使用适配器可以显示很负责的界面。
// 使用适配器显示对话框–>builder的setAdapter方法
// 这里选择一个选项后,对话框就会关闭,所以是不需要设置按钮的
// 其实这里可以设置很复杂的适配器对象,但是实际应用中不会在对话框显示太复杂的布局
public void bt6(View v) {// 创建ArrayAdapter适配器 // android.R.layout.simple_list_item_1是Android系统里面的简单布局 // 第三个参数是数据源 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, array); // 创建对话框对象 new AlertDialog.Builder(this). // 设置标题 setTitle("使用适配器显示数据"). // 添加适配器,并为适配器添加点击事件 setAdapter(adapter, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // which是选择的条目的游标值 Toast.makeText(MainActivity.this, "选择" + array[which], 0).show(); } }). // 产生并显示 create().show(); }
运行程序后显示的结果:
设置多选菜单是builder的setAdapter方法来完成的。
这里要设置一个监听事件,适配器的监听事件。
这里点击某一个选项后,对话框消失,并土司选中的内容。(七)设计一个有输入框的对话框
// 设置一个有输入文本的对话框---->builder的setView方法
// 输入数据后,对数据进行处理
// 这里要设置按钮,才能对数据的数据进行处理
public void bt7(View v) {// 创建一个EditText对象 final EditText et = new EditText(this); // 创建对话框对象 new AlertDialog.Builder(this). // 设置标题 setTitle("有输入框的对话框"). // 添加输入的文本框 setView(et). // 添加确定按钮 setPositiveButton("确定", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 获取输入的字符 String in = et.getText().toString(); Toast.makeText(MainActivity.this, "输入;" + in, 0).show(); } }).setNegativeButton("取消", null). // 产生并显示 create().show(); }
运行后的结果:
设置输入框是builder的setView方法来完成的。
这里要设置一个按钮监听事件。
这里点击确定按钮后,获取输入框的内容并土司出来。(八)使用对话框内的控件控制对话框
这里使用对话框内的一个布局文件的按钮,来关闭对话框// 使用对话框内的控件来关闭对话框 public void bt8(View v) { // 创建一个EditText对象 final Button btn = new Button(this); // 给按钮设置文字 btn.setText("关闭对话框"); // 创建对话框对象 final AlertDialog dialog = new AlertDialog.Builder(this). // 设置标题 setTitle("通过按钮关闭对话框"). // 添加输入的文本框 setView(btn). // 产生 create(); // 设置对话框不可以关闭,一般情况下对话框是失去焦点后自动消失的 // 但是加 了.setCancelable(false),对话框就不会消失,除非手动退出 dialog.setCancelable(false); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 点击后关闭对话框,两种方法都可以 dialog.cancel(); //dialog.dismiss(); } }); // 显示 dialog.show(); //给对话框设置一个监听时间,对话框退出前会执行 dialog.setOnDismissListener(new OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { // 只要关闭都会调用 Toast.makeText(MainActivity.this, "关闭", Toast.LENGTH_SHORT) .show(); } }); }
运行结果:
这里设置了dialog.setCancelable(false);的属性,代表了对话框不能随着焦点的失去而消失对话框,要收到关闭对话框,要么设置确定和取消按钮,要么在对话框布局中设置能够关闭对话框的控件;
(九)AlertDialog的详细属性
对话框的全部属性表现,如图所示:这里分上、中、下三部分。
区域一是标题相关
区域二是主体相关
区域三是按钮相关其中任何部分都可以不设置,
但是有一点要注意的是主体内容只能有一种,比如:设置了SetMessage不能再设置SetAdapter或SetView。因为设置了也不会显示的。1.区域1那里就是定义弹出框的头部信息
包括标题名或者是一个图标。
(1)setTitle : 为对话框设置标题
(2)setIcon : 为对话框设置图标2.区域2那里是AlertDialog对话框的content部分
在这里我们可以设置一些message信息,或者是定义一组选择框,还可以定义我们自己的布局弹出框。
(1)setMessage: 为对话框设置内容,如果设置了其他内容,那么Message不要设置,否者只显示Message。
(2)setView: 给对话框设置自定义View
setItems: 设置对话框要显示的一个list,一般用于显示几个命令时
//items是传入一个字符串数组,或者是R文件中的数组
(3)setSingleChoiceItems: 用来设置对话框显示一系列的单选按钮
(4)setMultiChoiceItems: 用来设置对话框显示一系列的复选框3.区域3那里使我们的Action Buttons部分
这里我们可以定义我们的操作按钮。
在AlertDialog中,定义按钮都是通过 setXXXButton 方法来完成,其中一共有3种不同的Action Buttons供我们选择:
(1)setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener)
这是一个相当于OK、确定操作的按钮.
(2)setNegativeButton (CharSequence text, DialogInterface.OnClickListener listener)
这是一个相当于取消操作的按钮。(3)setNeutralButton (CharSequence text, DialogInterface.OnClickListener listener)
这个是相当于一个忽略操作的按钮。触发相关按钮都的行为都是我们程序员自己定的,比如你可以设置点击确定后关闭页面,也可以设置点击取消后关闭页面。但是我们设计的时候还是要按照大部分人的阅读习惯来设定。
4.监听事件的说明
在AlertDialog的各种设置中,都充斥着DialogInterface.OnClickListener,比如按钮,内容区的items,单选按钮等,只有多选按钮是用了另外一个监听
DialogInterface.OnMultiChoiceClickListener。(1)单选按钮的监听
public void onClick(DialogInterface dialog, int which);
//which代表items,单选按钮中的序号(2)多选按钮的监听
public void onClick(DialogInterface dialog, int which, boolean isChecked);
//which代表items,单选按钮中的序号
//isChecked代表当前点击的序号是否被勾选(3)Adapter的监听
AlertDialog还支持ListAdapter的子类,我们可以传入各种ListAdapter的子类,来显示复杂的列表,当然直接使用 setView 也可以办到
public Builder setAdapter(final ListAdapter adapter, final
OnClickListener listener)三.ProgressDialog进度对话框
ProgressDialog是AlertDialog类的子类,可以为一个未定义进度的任务显示一个旋转轮形状的进度动画,或者为一个指定进度的任务显示一个进度条。它的用处非常广泛,在每次用户操作一个延迟的任务时,我们都必须以进度对话框的形式告诉用户,否者用户并不知道操作已经开始,可能会多次操作同一个任务,并有可能导致异常发生。
(一)构造方法和常用方法
1.构造方法需要传入一个上下文
public ProgressDialog(Context context)2.给定一个样式,带进度条和不带进度条的样式
public void setProgressStyle(int style) //可选值
//STYLE_SPINNER 默认 圆形,没有进度条的样式
//STYLE_HORIZONTAL 给定进度条的样式3.设置进度最大值,需要给定STYLE_HORIZONTAL
public void setMax(int max)4.是否能用返回键关闭 true可以关闭,false不能关闭
public void setCancelable(boolean flag)5.设置一个消息
public void setMessage(CharSequence message)6.判断Dialog是否显示
public boolean isShowing()7.关闭对话框
public void dismiss()8.ProgressDialog从AlertDialog中继承的常用方法setIcon和setTitile
9.虽然ProgressDialog可以设置Button,但是我们一般都不会去使用它
(二)进度对话框的设计的使用
1.圆形进度条对话框
// 显示圆形的进度框
public void bt1(View v) {
// 创建进度条的对话框
ProgressDialog dialog = new ProgressDialog(this);
// 设置进度条的样式,选择圆形或条状
// 这里这圆形的
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
// 设置标题
dialog.setTitle(“文件下载”);
// 设置文本信息
dialog.setMessage(“正在下载。。。”);
// 设置是否能用后退键出对话框
// 选择false就代表不能
// 如果设置为false,程序可能退出不了
dialog.setCancelable(true);
// 显示对话框
dialog.show();
}程序运行后的结果:
这里点击进度对话框的其他地方,对话框就消失了。因为默认情况,对话框失去焦点就会消失。
2.条形对话框
// 显示设置水平的进度框
public void bt2(View v) {
// 创建进度条的对话框
final ProgressDialog dialog = new ProgressDialog(this);
// 设置进度条的样式,选择圆形或条状
// 这里这圆形的
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
// 设置标题
dialog.setTitle(“文件下载”);
// 设置文本信息
dialog.setMessage(“正在下载。。。”);
// 设置是否能用后退键出对话框
// 选择false就代表不能
// 如果设置为false,程序可能退出不了
dialog.setCancelable(false);
// 显示对话框
dialog.show();// 这里新建一个线程来,跟新进度和关闭页面 new Thread(new Runnable() { @Override public void run() { // 获取进度值的当前的值 int index = 0; // 跟新进度 while (index < dialog.getMax()) { index++; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // 设置进度的值 dialog.setProgress(index); } // 完成任务后,退出对话框 dialog.cancel(); } }).start(); }
运行结果:
这个进度对话框是没有办法取消的,因为设置了:dialog.setCancelable(false);
只能等到文件下载完成后对话框自动消失。这个设置在实际应用中也是会有用到的,比如复制文件,不能随便打断。本示例中,进度的跟新需要用子线程来更新,进度条的更新可以直接在子线程中实现,而其他布局的跟新是不能在子线程中直接更新的。要使用runOnUIThread();方法来更新主线程的界面,四.时间和日期选择器对话框
(一)时间和日期对话框的方法
DatePickerDialog与TimePickerDialog都继承于AlertDialog,基本方法和我们前面讲的DatePicker和TimePicker组件一样,只是需要调用Dialog的show方法来显示。在构造方法里需要传入回调监听1.用户改变时间后的监听
public interface OnTimeChangedListener { void onTimeChanged(TimePicker view, int hourOfDay, int minute);
}2.用户改变日期后的监听
public interface OnDateChangedListener { void onDateChanged(DatePicker view, int year, int monthOfYear,
int dayOfMonth);
}(二)时间对话框的使用
// 显示时间的对话框
public void btn1(View v) {
// 第一个参数是上下文
// 第二个参数是监听时间选择后的事件
// 后面两个数是默认是时间
// 后一个是代表是否显示时间的格式是24小时制的
TimePickerDialog dialog = new TimePickerDialog(this,
new OnTimeSetListener() {@Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { Toast.makeText(MainActivity.this, hourOfDay + "时" + minute + "分", 0).show(); } }, 21, 8, true);
//显示标题
dialog.setTitle(“选择你要设定的时间”);
// 显示时间的对话框
dialog.show();}
运行后显示的结果:
这是时间对话框的选择按钮,其中也可以设置很多属性。
这里点击完成后会弹出一个选择的时间的土司。
如图所示:(三)日期对话框的使用
// 显示日期的对话框
public void btn2(View v) {
// 第一个参数是上下文
// 第二个参数是监听时间选择后的事件
// 后面三个数是默认是日期数
DatePickerDialog dialog = new DatePickerDialog(this,
new OnDateSetListener() {
// 日期选择器上的月份是从0开始的@Override public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { Toast.makeText( MainActivity.this, year + "年" + (monthOfYear + 1) + "月" + dayOfMonth + "日", 0).show(); } }, 2016, 11, 13); // 显示时间的对话框 dialog.show(); }
点击运行后,显示结果:
上面是日期对话框的默认画面,也是挺好看的,实际中也可以根据实际需要对它的页面样式进行修改。
点击完成后显示的结果:————————————————
版权声明:本文为CSDN博主「峥嵘life」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wenzhi20102321/article/details/52818351 -
基础语法篇_10——设置对话框、颜色对话框、字体对话框、示例对话框、改变对话框和控件的背景及文本颜色、...
2020-03-09 19:36:13文章目录一、简单绘图二、设置对话框2.1 设置线宽2.2 设置线型 新建一个单文档类型的MFC工程,取名:Graphic。此程序将实现简单的绘图功能。 一、简单绘图 实现简单的绘图功能,包括点、直线和椭圆的绘制。为了实现...🔳🔳 绘制线条 、画刷绘图、绘制连续线条、绘制扇形效果的线条
🔳🔳 插入符【文本插入符|图形插入符】、窗口重绘、路径、字符输入【设置字体|字幕变色】
🔳🔳 菜单命令响应函数、菜单命令的路由、基本菜单操作、动态菜单操作、电话本实例
🔳🔳 对话框的创建与显示、动态创建按钮、控件的访问【控件调整|静态文本控件|编辑框控件】、对话框伸缩功能、输入焦点的传递、默认按钮的说明
🔳🔳修改应用程序窗口的外观【窗口光标|图标|背景】、模拟动画图标、工具栏编程、状态栏编程、进度栏编程、在状态栏上显示鼠标当前位置、启动画面
🔳🔳设置对话框、颜色对话框、字体对话框、示例对话框、改变对话框和控件的背景及文本颜色、位图显示
文章目录
先学习简单绘图:MFC–简单绘图,了解基本知识。
新建一个单文档类型的MFC工程,取名:Graphic。此程序将实现简单的绘图功能。
一、简单绘图
实现简单的绘图功能,包括点、直线和椭圆的绘制。为了实现这些功能:
⭕⭕1)首先为此程序添加一个子菜单,菜单名称为“绘图”;
⭕⭕2)为其添加四个子菜单项,分别用来控制不同图形的绘制。当用户选择其中的一个菜单项后,程序将按照当前的选择进行相应图形的绘制。添加的四个菜单项的ID及名称如下表:
⭕⭕3)然后分别为这四个菜单项添加命令响应,本程序让视类(CGraphicView)对这些菜单命令进行响应:
最终生成的代码:
CGraphicView.h:public: afx_msg void OnDot(); afx_msg void OnLine(); afx_msg void OnRectangle(); afx_msg void OnEllipse();
CGraphicView.cpp:
BEGIN_MESSAGE_MAP(CGraphicView, CView) // 标准打印命令 ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CGraphicView::OnFilePrintPreview) ON_WM_CONTEXTMENU() ON_WM_RBUTTONUP() ON_COMMAND(IDM_DOT, &CGraphicView::OnDot) ON_COMMAND(IDM_LINE, &CGraphicView::OnLine) ON_COMMAND(IDM_RECTANGLE, &CGraphicView::OnRectangle) ON_COMMAND(IDM_ELLIPSE, &CGraphicView::OnEllipse) END_MESSAGE_MAP() ...... void CGraphicView::OnDot() { // TODO: 在此添加命令处理程序代码 } void CGraphicView::OnLine() { // TODO: 在此添加命令处理程序代码 } void CGraphicView::OnRectangle() { // TODO: 在此添加命令处理程序代码 } void CGraphicView::OnEllipse() { // TODO: 在此添加命令处理程序代码 }
⭕⭕4)在程序运行以后,当用户单击某个菜单项时,应该把用户的选择保存起来,以便随后的绘图操作使用。因此,在CGraphicView类中添加一个私有变量用来保存用户的选择:
private: UINT m_nDrawType;
接着,在视类的构造函数中将此变量初始化为0:
CGraphicView::CGraphicView() noexcept { // TODO: 在此处添加构造代码 m_nDrawType = 0; }
⭕⭕5)当用户选择绘图菜单下的不同子菜单项时,将变量m_nDrawType设置为不同的值:
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; }
⭕⭕6)对于直线、矩形和椭圆,在绘图时都可以由2个点来确定其图形。当鼠标左键按下时得到一个点,当鼠标左键松开时又得到另外一个点。也就是说,在鼠标左键按下时将当前点保存为绘图原点,当鼠标左键松开时,就可以绘图了。
6.1)因此就需要为视类CGraphicView分别捕获鼠标左键按下和鼠标左键松开这两个消息。
void CGraphicView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CView::OnLButtonDown(nFlags, point); } void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CView::OnLButtonUp(nFlags, point); }
6.2)另外,当鼠标左键按下时,需要将鼠标当前按下点保存起来,因此为CGraphicView类再增加一个CPoint类型的私有成员变量:m_ptOrigin。
并在CGraphicView类构造函数中,将该变量的值设置为0,即将原点设置为(0,0)。CGraphicView::CGraphicView() noexcept { // TODO: 在此处添加构造代码 m_nDrawType = 0; m_ptOrigin = (0, 0); }
6.3)在鼠标左键按下消息响应函数中,保存当前点:
void CGraphicView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 m_ptOrigin = point; CView::OnLButtonDown(nFlags, point); }
6.4)鼠标左键松开消息响应函数中实现绘图功能。通过前面的知识知道,为了进行绘图操作,首先需要有DC对象,所以首先定义了一个CClientDC类型的变量: dc。在具体绘图时应根据用户的选择来进行,该选择已经保存在变量:m_nDrawType中了。可以用switch/case语句,分别完成相应图形的绘制:
-
🔳🔳 如果设置一个点,需要用到函数:SetPixel,这也是CDC类的一个成员方法,该函数是在指定的点设置一个像素。该函数两种声明形式,其中一种声明如下:
COLORREF SetPixel(POINT point,COLORREF crColor);
◼ point
指定的点
◼ crColor
指定的颜色。在程序中设定的颜色在系统颜色表中可能不存在,但系统会选择一种和这个颜色最接近的颜色。 -
🔳🔳 当用户选择直线时,这时就需要绘制直线,首先调用MoveTo函数移动到原点,然后调用LineTo函数绘制到终点。
-
🔳🔳 绘制矩形时可以使用Rectangle函数,该函数有一种声明:
BOOL Rectangle(LPCRECT IpRect);
该函数有一个指向CRect对象的参数,而CRect可以利用两个点来构造。
📢📢📢 Rectangle函数需要的是指向CRect对象的指针,而传递的此参数却是CRect对象,但程序编译时却能成功通过,运行时也不会报错的原因:
C系列的语言都是强类型语言,如果类型不匹配的话,需寒进行强制类型转换。CRect类提供一个成员函数:重载LPCRECT操作符,其作用是将CRect转换为LPCRECT类型。因此,当在程序中给Rectangle函数的参数赋值时,如果它发现该参数是一个CRect对象,它就会隐式地调用LPCRECT操作符,将CRect类型的对象转换为LPCRECT类型。 -
🔳🔳 当用户选择椭圆菜单项时,调用Ellipse函数绘制一个椭圆。
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CClientDC dc(this); 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)); break; case 4: dc.Ellipse(CRect(m_ptOrigin, point)); break; } CView::OnLButtonUp(nFlags, point); }
运行Graphic程序,由于DC中有一个默认的白色画刷,在绘制图形时会使用这个默认画刷填充其内部,因此在绘制时,如果存在重叠部分,那么先前绘制的图形会被后来绘制的图形所覆盖:
⭕⭕7)绘制其他颜色的线条。
绘制的直线,以及矩形和椭圆的边框都是黑色的。一般来说,在程序运行过程中,用户都希望能够使用他们自己指定的颜色来绘制各种图形。通过前面章节的介绍,知道线条的颜色是由DC中画笔的颜色确定的,为了绘制其他颜色的线条就需要:- 构造一个CPen对象,
- 为它指定一种颜色,
- 将此画笔选入设备描述表中。
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CClientDC dc(this); CPen pen(PS_SOLID, 20, RGB(150, 140, 32)); dc.SelectObject(&pen); switch (m_nDrawType) { case 1: dc.SetPixel(point,RGB(200,0, 0)); break; case 2: dc.MoveTo(m_ptOrigin); dc.LineTo(point); break; case 3: dc.Rectangle(CRect(m_ptOrigin, point)); break; case 4: dc.Ellipse(CRect(m_ptOrigin, point)); break; } CView::OnLButtonUp(nFlags, point); }
⭕⭕8)把DC中的画刷设置为透明。
不想使用DC默认的白色画刷来填充矩形或椭圆的内部,而是希望能够看到这些图形内部的内容,可以把DC中的画刷设置为透明的。- 利用参数NULL BRUSH调用GetStockObject函数可以创建透明画刷,
- 然后调用CBrush类的静态成员函数FromHandle将画刷句柄转换为指向画刷对象的指针,但是该函数的参数需要的是HBRUSH类型,GetStockObject函数返回的是HGDIOBJ类型,因此需要进行强制转换,将其转换为画刷的句柄,即HBRUSH类型对象。
- 将创建的新画刷选入设备描述表中。
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CClientDC dc(this); //改变画笔颜色 CPen pen(PS_SOLID, 5, RGB(150, 140, 32)); dc.SelectObject(&pen); //设置画刷透明 CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); dc.SelectObject(pBrush); switch (m_nDrawType) { case 1: dc.SetPixel(point,RGB(200,0, 0)); break; case 2: dc.MoveTo(m_ptOrigin); dc.LineTo(point); break; case 3: dc.Rectangle(CRect(m_ptOrigin, point)); break; case 4: dc.Ellipse(CRect(m_ptOrigin, point)); break; } CView::OnLButtonUp(nFlags, point); }
再次运行Graphic程序,选择相应菜单项,然后绘制各种图形,这时可以看到所有绘制的线条都可以看到了,因为现在使用的是透明画刷。
二、设置对话框
许多软件都为用户提供了设置对话框,或者称为选项对话框,允许用户通过设置一些选项来改变软件的某些行为和特性。
实现:
给Graphic程序添加一个设置对话框,允许用户指定画笔的类型、线宽,并且让随后的绘图操作就使用用户指定的新设置值来进行绘制。为了实现这一功能,首先需要为Graphic程序添加一个对话框资源,并按照下表所列内容修改其属性。
创建对话框:2.1 设置线宽
⭕⭕1)首先实现线宽的设置。为新添加的设置对话框资源添加一个静态文本框,并将其Caption属性设置为: 线宽;
⭕⭕2)再添加个编辑框,让用户输入设定的线宽,将其ID设置为: IDC_LINE_WIDTH。
⭕⭕3)创建一个相应的对话框类。类名设置为CSettingDlg,基类:CDialog。📢📢📢 此时生成的类所关联的ID并不是自己想关联的对话框的ID。所以需要自己手动指定关联的对话框ID号。
需要更改的关联位置有两处:
⭕⭕4)为对话框中的编辑框控件增加一个成员变量:m_nLineWidth,类型:UINT。因为对于线,不希望用户设置的值小于0,因此将它的类型选择为无符号整型(UINT)。
改正上面的视频内容:
m_nLineWidth设置为公开public类型——由于后面发现需要在别的类中访问此成员变量,所以需要设置为公开类型。public: UINT m_nLineWidth;
⭕⭕5)显示设置对话框。
5.1)为Graphic程序在绘图子菜单下再增加一个菜单项,名称为:设置,并将其ID设置为:IDM_SETTING:
5.2)用户单击该菜单项后,程序应立即显示刚才新建的设置对话框。因此为此菜单项添加一个命令响应,并选择视类(CGraphicView)对此消息做出响应:
5.3)将CSettingDlg的头文件包含到视类源文件CGraphicView.cpp中:#include "CSettingDlg.h"
5.4)首先构造设置对话框对象(dlg),然后调用该对象的DoModal函数显示该对话框:
void CGraphicView::OnSetting() { // TODO: 在此添加命令处理程序代码 CSettingDlg dlg; dlg.DoModal(); }
运行程序:
⭕⭕6)当用户在线宽编辑框中输入线宽值并确定此操作后,程序应把这个线宽值保存起来,然后随后的绘图都使用这个线宽值来设置线的宽度。
6.1)为CGraphicView类添加一个私有的成员变量:m_nLineWidth;类型:UINT,用来保存用户输入的线宽:
6.2)在CGraphicView类的构造函数中将其初始化为0。CGraphicView::CGraphicView() noexcept { // TODO: 在此处添加构造代码 m_nDrawType = 0; m_ptOrigin = (0, 0); m_nLineWidth = 0; }
6.3)判断用户关闭设置对话框时的选择。
在用户输入线宽后,应该是在用户单击OK按钮后才保存这个线宽值;如果用户选择的是Cancle按钮,并不需要保存这个线宽值。因此在CGraphicView类的OnSetting函数中需要判断一下用户关闭设置对话框时的选择,如果选择的是OK按钮,则保存用户输入的线宽值:void CGraphicView::OnSetting() { // TODO: 在此添加命令处理程序代码 CSettingDlg dlg; if (IDOK == dlg.DoModal()) { m_nLineWidth = dlg.m_nLineWidth; } }
DoModal函数:
。。。。。。。。。。。。。。。。。。。⭕⭕7)在构造画笔对象时,其宽度就可以利用m_nLineWidth这个变量来代替了:
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CClientDC dc(this); //改变画笔颜色 //CPen pen(PS_SOLID, 5, RGB(150, 140, 32)); CPen pen(PS_SOLID, m_nLineWidth, RGB(150, 140, 32)); dc.SelectObject(&pen); //设置画刷透明 CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); dc.SelectObject(pBrush); switch (m_nDrawType) { case 1: dc.SetPixel(point,RGB(200,0, 0)); break; case 2: dc.MoveTo(m_ptOrigin); dc.LineTo(point); break; case 3: dc.Rectangle(CRect(m_ptOrigin, point)); break; case 4: dc.Ellipse(CRect(m_ptOrigin, point)); break; } CView::OnLButtonUp(nFlags, point); }
在程序运行时,当用户设置线宽后,在下一次绘图时,就会以用户输入的线宽创建画笔,那么随后的绘图就是按照用户设置的线宽来绘制的。
运行Graphic程序,单击绘图下的设置菜单项,在弹出的设置对话框中指定新线宽,并单击OK按钮关闭设置对话框。然后再绘图,可以发现程序使用的是用户指定的新线宽来绘制图形的。
但是,当再次打开设置对话框时,线宽编辑框的值又变回0了。一般来说,当再次回到这个设置对话框时,应该看到上次设置的值,但这里的情况并不是这样的。
📋📋原因:由于设置对话框对象dlg是一个局部对象。当再次单击单击绘图下的设置菜单项,即再次调用OnSetting函数时,又将重新构造dlg这个设置对话框对象。因此该对象的所有成员变量都将被初始化,而CSettingDlg对象的构造函数中m_nLineWidth初始化为0,所以每次打开设置对话框时,看到的编辑框内都为0:CSettingDlg::CSettingDlg(CWnd* pParent /*=nullptr*/) : CDialog(IDD_DLG_SETTING, pParent) , m_nLineWidth(0) { }
🟢🟢解决:为了解决这个问题,当CSettingDlg对话框(dlg)对象产生之后,应该将CGraphicView类中保存的用户先前设置的线宽再传回给这个设置对话框:
void CGraphicView::OnSetting() { // TODO: 在此添加命令处理程序代码 CSettingDlg dlg; //将CGraphicView类中保存的用户先前设置的线宽再传回给这个设置对话框 dlg.m_nLineWidth = m_nLineWidth; if (IDOK == dlg.DoModal()) { m_nLineWidth = dlg.m_nLineWidth; } }
2.2 设置线型
实现:
为Graphic程序添加允许用户设置线型的功能。提供一些单选按钮让用户从多种线型中选择一种。⭕⭕1)首先再为Graphic程序已有的设置对话框资源添加一个组框,并设置Caption:线型;ID:IDC_LINE_STYLE。
组框的作用通常是起标示作用,所以它的ID默认情况下是IDC_STATIC。但如果在程序中需要对组框进行操作的话,那么其ID就不能是默认的IDC_STATIC了,需要修改这个ID。
因为后面的程序会对这个组框进行一些操作,所以这里将它的ID修改为: IDC_LINE_STYLE。
⭕⭕2)接着在此组框内放置三个单选按钮,保持它们默认的ID值不变,将它们的名称分别设置为:实线、虚线、点线。
然后将这三个单选按钮设置成为一组。方法:在第一个单选按钮(实线)上单击鼠标右键,打开其属性对话框,选中Group选项。这时,这三个单选按钮就成为一组的了。
⭕⭕3)利用类向导为这组单选按钮关联一个成员变量:这样在程序运行时:
- 如果选中第1个单选按钮,该变量的值就是0;
- 如果选中第2个单选按钮,该变量的值就是1;
- 如果选中第3个单选按钮,该变量的值就是2;
- 如果都没有选中,那么该变量的值是-1。
生成代码:
CSettingDlg.hpublic: UINT m_nLineWidth; // 记录线型的值 int m_nLineStyle;
CSettingDlg.cpp
CSettingDlg::CSettingDlg(CWnd* pParent /*=nullptr*/) : CDialog(IDD_DLG_SETTING, pParent) , m_nLineWidth(0) , m_nLineStyle(0) { } void CSettingDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_LINE_WIDTH, m_nLineWidth); DDX_Radio(pDX, IDC_RADIO1, m_nLineStyle); }
⭕⭕4)保存用户选择的线型。
同上,当用户单击设置对话框上的OK按钮关闭该对话框后,应该将用户选择的线型保存下来,因此需要:
4.1)为CGraphicView类再添加一个int类型的私有成员变量:m_nLineStyle:
4.2)并在该类的构造函数中将其初始化为0。CGraphicView::CGraphicView() noexcept { // TODO: 在此处添加构造代码 m_nDrawType = 0; m_ptOrigin = (0, 0); m_nLineWidth = 0; m_nLineStyle = 0; }
4.3)然后在CGraphicView类的OnSetting函数中,当用户单击设置对话框的OK按钮关闭该对话框后,将用户选择的线型保存到CGraphicView类的m_nLineStyle变量中。
void CGraphicView::OnSetting() { // TODO: 在此添加命令处理程序代码 CSettingDlg dlg; dlg.m_nLineWidth = m_nLineWidth; if (IDOK == dlg.DoModal()) { m_nLineWidth = dlg.m_nLineWidth; //户选择的线型保存到CGraphicView类的m_nLineStyle变量中 m_nLineStyle = dlg.m_nLineStyle; } }
4.4)与前面线宽的设置一样,为了把上一次选择的线型保存下来,同样需要在CGraphicViev类中把已保存的线型值再设置回设置对话框的线型变量:
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; } }
⭕⭕5)根据用户指定的线型创建画笔。
获得用户指定的线型后,程序应根据此线型创建画笔。在wingdi.h文件中定义了一些符号常量:可以在CGraphicView类OnLButtonUp函数中构造画笔的那行代码中的符号常量: PS_SOLID上单击鼠标右键打开wingdi.h文件,并定位于该常量符号的定义处。
从中可以看到PS_SOLID (实线)的值本身就是0,PS_DASH (虚线)就是1,PS_DOT(点线)就是2,这正好与CGraphicView类的成员变量m_nLineStyle的取值一一对应。这是因为在设置对话框中排列的线型顺序正好是按照实线、虚线和点线的顺序来做的。因此,程序在构造画笔对象时,可以直接使用m_nLineStyle变量作为线型参数的值:void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CClientDC dc(this); //改变画笔颜色 //CPen pen(PS_SOLID, 5, RGB(150, 140, 32)); //直接使用m_nLineStyle变量作为线型参数的值 CPen pen(m_nLineStyle, m_nLineWidth, RGB(150, 140, 32)); dc.SelectObject(&pen); //设置画刷透明 CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); dc.SelectObject(pBrush); switch (m_nDrawType) { case 1: dc.SetPixel(point,RGB(200,0, 0)); break; case 2: dc.MoveTo(m_ptOrigin); dc.LineTo(point); break; case 3: dc.Rectangle(CRect(m_ptOrigin, point)); break; case 4: dc.Ellipse(CRect(m_ptOrigin, point)); break; } CView::OnLButtonUp(nFlags, point); }
运行Graphic程序,首先打开设置对话框,可以看到初始的选择是实线,这是因为在CGraphicView类的构造函数中将线型变量(m_nLineStyle)初始设置为0,即实线。然后程序在构造设置对话框对象之后,将CGraphicView类的线型变量赋给了这个对话框对象的线型变量,因此该对话框初始显示时,选中的线型是实线。
📢📢📢 画笔宽度小于等于1时,虚线和点线线型才有效。否则,线型会自动改为实线。
- 若想设置虚线的线宽可以参考: 绘制粗虚线
三、颜色对话框
颜色对话框类似于Windows提供的画图程序中选择编辑颜色莱单项后出现的对话框:
利用颜色对话框,可以让用户选择一种颜色,程序随后按照此颜色创建绘图所需的画笔。颜色对话框看起来比较复杂。实际上,MFC提供了一个类:CColorDialog,可以很方便地创建一个颜色对话框。该类的派生层次结构:
由此,可以知道颜色对话框也是一个对话框。
CColorDialog类的构造函数:CColorDialog(COLORREF clrInit = 0, DWORD dwFlags = 0,CWnd* pParentWnd = NULL);
◼ clrInit
指定默认的颜色选择。默认是黑色。
◼dwFlags
指定一组标记,用于定制颜色对话框的功能和它的外观。
◼ pParentWnd
指向颜色对话框父窗口或拥有者窗口的指针。为了在Graphic程序中增加颜色对话框的显示:
⭕⭕1)首先为该程序增加一个菜单项,当用户选择此菜单项时,程序将显示颜色对话框。
将这个新菜单项放置在已有的绘图子菜单下,并设置ID:IDM_COLOR、Caption:颜色。
⭕⭕2)为其增加一个命令响应,并选择CGraphicView类对此菜单项命令做应。
⭕⭕3)在响应函数OnColor中显示颜色。void CGraphicView::OnColor() { // TODO: 在此添加命令处理程序代码 CColorDialog dlg; dlg.DoModal(); }
运行Graphic程序,点击颜色菜单项,即可以看到出现了一个颜色对话框:
可以看到在该对话框左边颜色块的黑色块上有一个黑色的边框,说明默认选择的是黑色。
⭕⭕4)将用户选择的颜色保存下来。
4.1)CColorDialog类有一个CHOOSECOLOR结构体类型的成员变量:m_cc。CHOOSECOLOR结构体的定义:typedef struct { //指定结构的长度(字节) DWORD lStructSize; //拥有对话框的窗口的句柄。该成员可以是任意有效的窗口句柄,或在对话框没有所有者时,可为NULL HWND hwndOwner; //如果Flag成员设置了CC_ENABLETEMPLATEHANDLE标识符时,该成员是一个包含了对话框模板的内存对象的句柄。如果 CC_ENABLETEMPLATE 标识符被设置时,该成员是一个包含了对话框的模块句柄。如果上述两个标识符都未被设置,则该成员被忽略。 HWND hInstance; //如果CC_RGBINIT标识符被设置时,该成员指定了对话框打开时默认的选择颜色。如果指定的颜色值不在有效的范围内,系统会自动选择最近的颜色值。如果该成员为0或CC_RGBINIT未被设置,初始颜色是黑色。如果用户单击OK按钮,该成员指定了用户选择的颜色。 COLORREF rgbResult; //指向一个包含16个值的数组,该数组包含了对话框中自定义颜色的红、绿、蓝(RGB)值。如果用户修改这些颜色,系统将用新的颜色值更新这个数组。如果要在多个ChooseColor函数中保存这个新的数组,应该为该数组分配静态内存空间。 COLORREF* lpCustColors; //一个可以让你初始化颜色对话框的位集。当对话框返回时,它用来这些标识符来标识用户的输入。该成员可以为一组标识符的任意组合。 DWORD Flags; //指定应用程序自定义的数据,该数据会被系统发送给钩子程序。当系统的发送WM_INITDIALOG消息给钩子程序时,消息的lParam参数是一个指向CHOOSECOLOR结构的指针。钩子程序可以利用该指针获得该成员的值。 LPARAM lCustData; //指向CCHookProc钩子程序的指针,该钩子可以处理发送给对话框的消息。该成员只在CC_ENABLEHOOK标识被设定的情况下才可用,否则该成员会被忽略。 LPCCHOOKPROC lpfnHook; //指向一个NULL结尾的字符串,该字符串是对话框模板资源的名字。 LPCWSTR lpTemplateName; }CHOOSECOLOR, *LPCHOOSECOLOR;
当用户单击颜色对话框上的OK按钮后,这个结构体中的rgbResult变量就保存了用户选择的颜色。因此,在程序中通过这个变量就可以获得用户选择的颜色。
4.2)在Graphic程序中,为了保存用户选择的颜色,为CGraphicView类再增加一个COLORREF类型的私有成员变量:m_clr。右键点击类视图类中的CGraphicView,选项添加–>添加变量
4.3)并在CGraphicView类的构造函数中将其初始化为红色:
CGraphicView::CGraphicView() noexcept { // TODO: 在此处添加构造代码 m_nDrawType = 0; m_ptOrigin = (0, 0); m_nLineWidth = 0; m_nLineStyle = 0; m_clr = RGB(255,0, 0); }
4.4)在颜色子菜单的响应函数OnColor函数中进行判断:如果用户单击的OK按钮,就将用户选择的颜色保存下来。
void CGraphicView::OnColor() { // TODO: 在此添加命令处理程序代码 CColorDialog dlg; if (IDOK == dlg.DoModal()) { m_clr = dlg.m_cc.rgbResult; } }
⭕⭕5)当用户选择颜色后,随后进行的绘图操作都应用此颜色来绘制,也就说应该按此颜色创建绘图用的画笔。
所以修改CGraphicVview类OnLButtonUp函数中已有的创建画笔的代码,将用户当前选择的颜色(即m_clr变量)传递给CPen构造函数的第三个参数。此外,还需要修改该函数中绘制点图形的代码,用用户当前选择的颜色来设置像素点的颜色。void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CClientDC dc(this); //改变画笔颜色 //CPen pen(PS_SOLID, 5, RGB(150, 140, 32)); CPen pen(m_nLineStyle, m_nLineWidth, m_clr); dc.SelectObject(&pen); //设置画刷透明 CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); dc.SelectObject(pBrush); switch (m_nDrawType) { case 1: dc.SetPixel(point,m_clr); break; case 2: dc.MoveTo(m_ptOrigin); dc.LineTo(point); break; case 3: dc.Rectangle(CRect(m_ptOrigin, point)); break; case 4: dc.Ellipse(CRect(m_ptOrigin, point)); break; } CView::OnLButtonUp(nFlags, point); }
运行Graphic程序,打开颜色对话框,选择某种颜色,然后进行绘图操作,可以发现这时所绘制的图形边框的颜色就是刚才选择的颜色。但是当再次打开颜色对话框时,它默认选择的仍是黑色,而不是刚才选择的颜色。
⭕⭕6)颜色值设置回颜色对话框。
6.1)自然就会想到应该像上面的处理一样,将用户选择的颜色,即CGraphicView类的m_clr变量保存的颜色值设置回颜色对话框对象,因此修改CGraphicView类的OnColor函数:void CGraphicView::OnColor() { // TODO: 在此添加命令处理程序代码 CColorDialog dlg; dlg.m_cc.rgbResult = m_clr; if (IDOK == dlg.DoModal()) { m_clr = dlg.m_cc.rgbResult; } }
再次运行Graphic程序,先选择一种颜色,然后进行图形的绘制,可是当再次打开颜色对话框时,将会发现结果仍不对,默认选中的颜色仍是黑色。
6.2)实际上如果想要设置颜色对话框初始选择的颜色,则需要设置该对话框的CC_RGBINIT标记:
- 这个标记可以在创建颜色对话框时通过其构造函数的第二个参数来设置;
- 也可以在该对话框创建之后,设置其m_cc成员变量的Flags成员。
采用后一种方法,修改CGraphicView类的OnColor函数:
void CGraphicView::OnColor() { // TODO: 在此添加命令处理程序代码 CColorDialog dlg; dlg.m_cc.Flags = CC_RGBINIT; dlg.m_cc.rgbResult = m_clr; if (IDOK == dlg.DoModal()) { m_clr = dlg.m_cc.rgbResult; } }
再次运行Graphic程序,选择颜色菜单项,出现一个非法操作提示对话框:
6.3)实际上,当在创建CColorDialog对象dlg时,它的数据成员m_cc中的Flags成员已经具有了一些初始的默认标记。将CC_RGBINIT标记直接赋给Flags成员时,就相当于将Flags成员初始默认的标记都去掉了。所以不能给Flags标记直接赋值,应利用或操作(|)将CC_RGBINIT标记与Flags先前的标记组合起来。void CGraphicView::OnColor() { // TODO: 在此添加命令处理程序代码 CColorDialog dlg; dlg.m_cc.Flags = CC_RGBINIT| dlg.m_cc.Flags; dlg.m_cc.rgbResult = m_clr; if (IDOK == dlg.DoModal()) { m_clr = dlg.m_cc.rgbResult; } }
再次运行Graphic程序,打开颜色对话框,可以看到初始选择的就是红色。接着,选择其他某种颜色并关闭该对话框。然后再打开颜色对话框,这时就可以看到现在选中的是先前选择的颜色了。
6.4)另外, Flags成员的取值还有一个常用标记: CC-FULLOPEN,该标记的作用就是让颜色对话框完全展开。
void CGraphicView::OnColor() { // TODO: 在此添加命令处理程序代码 CColorDialog dlg; dlg.m_cc.Flags = CC_RGBINIT| dlg.m_cc.Flags|CC_FULLOPEN; dlg.m_cc.rgbResult = m_clr; if (IDOK == dlg.DoModal()) { m_clr = dlg.m_cc.rgbResult; } }
再次运行Graphic程序,打开颜色对话框,这时可以看到这个颜色对话框处于完全展开状态:
四、字体对话框
下面为Graphic程序添加字体对话框应用,该对话框类似于在对话框资源的属性对话框中所看到的字体对话框。
与上面的颜色对话框一样,字体对话框的创建也很简单,因为MFC也提供了一个相应的类: CFontDialog,该类的派生层次结构:
由此可知该类派生于CDialog,所以其也是一个对话框类。CFontDialog类的构造函数如下:
CFontDialog( LPLOGFONT lplfInitial = NULL, DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, CDC* pdcPrinter = NULL, CWnd* pParentWnd = NULL ); CFontDialog( const CHARFORMAT& charformat, DWORD dwFlags = CF_SCREENFONTS, CDC* pdcPrinter = NULL, CWnd* pParentWnd = NULL );
◼ lplfInitial
指向LOGFONT结构体的指针,允许用户设置一些字体的特征。
◼ dwFlags
s主要设置一个或多个与选择的字体相关的标记。
◼ pdcPrinter
指向打印设备上下文的指针。
◼ pParentWnd
指向字体对话框父窗口的指针。由CFontDialog类的构造函数的声明可知,它的参数都有默认值,因此在构造字体对话框时可以不用指定任何参数。字体对话框的创建与前面的颜色对话框的一样,首先构造一个字体对话框对象,然后调用该对象的DoModal函数显示这个对话。
⭕⭕1)为Graphic程序增加一个菜单项用来显示字体对话框,将其ID设置为: IDM_FONT;Caption:字体。
⭕⭕2)接着为其增加一个命令响应,并选择CGraphic类对此菜单项命令做出响应。
⭕⭕3)然后在此响应函数中添加创建并显示字体对话框的代码。void CGraphicView::OnFont() { // TODO: 在此添加命令处理程序代码 CFontDialog dlg; dlg.DoModal(); }
运行程序,点击字体菜单项:
⭕⭕4)程序保存用户通过字体对话框选择的字体信息。
当用户通过字体对话框选择某种字体后,程序应该把当前选择保存下来,然后在CGraphicView类中利用此字体将用户选择的字体名称显示出来。CFontDialog类有一个CHOOSEFONT结构体类型的数据成员:m_cf。CHOOSEFONT结构体的定义如下:
typedef struct { //指定这个结构的大小,以字节为单位 DWORD lStructSize; //调用窗口句柄,指向所有者对话框窗口的句柄。这个成员可以是任意有效窗口句柄,或如果对话框没有所有者它可以为NULL。 HWND hwndOwner; //显示的设备环境句柄,一般为NULL; HDC hDC; //选中的字体返回值,这里的字体是逻辑字体 LPLOGFONTW lpLogFont; //字体的大小 INT iPointSize; //字体的位标记,用来初始化对话框。当对话框返回时,这些标记指出用户的输入。 DWORD Flags; //字体颜色 COLORREF rgbColors; //自定义数据,这数据是能被lpfnHook成员识别的系统传到的钩子程序 LPARAM lCustData; //做钩子程序用的回调函数 LPCFHOOKPROC lpfnHook; //指向一个以空字符结束的字符串,字符串是对话框模板资源的名字,资源保存在能被hInstance成员识别的模块中 LPCWSTR lpTemplateName; //实例句柄 HINSTANCE hInstance; //字体风格 LPWSTR lpszStyle; //字体类型 WORD nFontType; //字体允许的最小尺寸 INT nSizeMin; //字体允许的最大尺寸 INT nSizeMax; } CHOOSEFONT *LPCHOOSEFONT;
其中成员IpLogFont是指向逻辑字体(LOGFONT类型)的指针。LOGFONT结构的定义:
typedef struct tagLOGFONT { LONG lfHeight; LONG lfWidth; LONG lfEscapement; LONG lfOrientation; LONG lfWeight; BYTE lfItalic; BYTE lfUnderline; BYTE lfStrikeOut; BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality; BYTE lfPitchAndFamily; WCHAR lfFaceName[LF_FACESIZE]; } LOGFONT;
各成员的含义:LOGFONT——百度百科
其中成员lfFaceName中存放的就是字体的名称。也就是说,可以通过此成员得到字体的名称。
至于字体对象的创建:
- 首先可以利用CFont类构造一个字体对象,
- 然后利用CFont类的CreateFontIndirect成员函数根据指定特征的逻辑字体(LOGFONT类型)来初始化这个字体对象。
CreateFontIndirect函数的功能就是利用参数IpLogFont指向的LOGFONT结构体中的一些特征来初始化CFont对象。该函数的声明:
BOOL CreateFontIndirect( CONST LOGFONT* lpLogFont);
所以,为了保存用户选择的字体:
4.1)首先通过类向导为Graphic工程的CGraphicView类增加两个私有的成员变量:
▪▪▪ CFont :m_font,用于存放字体对象;
▪▪▪ CString:m_strFontName,用来保存所选字体的名称。
4.2)然后在CGraphicView类的构造函数中初始化m_strFontName为空字符串。类向导创建的成员变量已经自动被初始化。
4.3)然后在CGraphicView类的OnFont函数中进行判断,如果用户单击的是字体对话框的OK按钮,就用所选字体信息初始化m_font对象,并保存所选字体的名称。void CGraphicView::OnFont() { // TODO: 在此添加命令处理程序代码 CFontDialog dlg; if (IDOK == dlg.DoModal()) { //保存字体对话框选择的字体样式 m_font.CreateFontIndirectW(dlg.m_cf.lpLogFont); //保存设置的字体名称 m_strFontName = dlg.m_cf.lpLogFont->lfFaceName; } }
4.4)在视类窗口中把字体名称用选择的字体显示出来。
- 在上述OnFont函数中,在保存完字体的名称之后需要调用Invalidat函数让窗口无效,这样当下一次发生WM_PAINT消息时,窗口就会进行重绘。
void CGraphicView::OnFont() { // TODO: 在此添加命令处理程序代码 CFontDialog dlg; if (IDOK == dlg.DoModal()) { //保存字体对话框选择的字体样式 m_font.CreateFontIndirectW(dlg.m_cf.lpLogFont); //保存设置的字体名称 m_strFontName = dlg.m_cf.lpLogFont->lfFaceName; //让窗口无效 Invalidate(); } }
- 在CGraphicView类的OnDraw函数中把 “字体名称” 用选择的字体显示出来。首先把用户选择的新字体选入设备上下文,然后在窗口的(0, 0)处显示所选字体的名称,最后恢复初始的字体,将初始的字体选入设备上下文。
void CGraphicView::OnDraw(CDC* pDC) { CGraphicDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: 在此处为本机数据添加绘制代码 CFont* pOldFont = pDC->SelectObject(&m_font); pDC->TextOutW(0, 0, m_strFontName); pDC->SelectObject(pOldFont); }
运行Graphic程序,选择字体菜单项,这时将打开字体对话框,可以选择任一种字体、字形,还可以指定字体的大小。然后单击字体对话框的OK按钮,这时在视类窗口中就可以看到用选定的字体、字形和大小输出了所选字体的名称:
可是当再次选择字体菜单项后,程序将出现非法操作。
📋📋 原因:这是因为当第一次选择字体后,OnFont函数中就把m_font对象与这种字体资源相关联了。当再次选择一种字体后,OnFont函数又会试图把m_font对象与新字体资源相关联,这时当然就会出错。
🟢🟢 解决:在点击OK菜单项后,在字体菜单项的命令响应函数中实现:在保存用户选择的字体资源前应该进行一个判断,如果mfont对象已经与一个字体资源相关联了,首先就需要切断这种关联,释放该字体资源,然后才能与新资源相关联。如何释放CFont资源:
要想释放先前的资源,可以利用CGdiObject类(CPen、 CFont、CBitmap、CBrush都派生于该类)的成员函数DeleteObject来实现。该函数通过释放所有为Windows GDI对象所分配的资源,从而删除与CGdiObject对象相关联的Windows GDI对象,同时与CGdiObjec对象相关的存储空间并不会受此调用影响。
CGdiObjec对象 和 Windows GDI对象区别:
CGdiObjec对象是一个类的对象;而Windows GDI是一种资源对象。就好像窗口类的对象和窗口的关系一样。例如视类对象和视类窗口,它们之间的联系在于视类对象有一个数据成员:m_hWnd保存了窗口资源的句柄值。
CGdiObject类的对象和Windows GDI资源对象是两个不同的概念,它们之间也是一个数据成员来维系的。当删除Windows GDI资源对象后,对于CGdiObiect类所定义的对象来说,并不受影响,只是它们之间的联系被切断了。如果想判断m_font对象是否已经与某个字体资源相关联了,最简单的方法就是利用CGdiObject对象的数据成员m_hObject来判断,该变量保存了与CGdiObject对象相关联的Windows GDI资源的句柄。如果已经有关联了,则调用DeleteObject释放这个字体资源,然后再和新的资源相关联。
void CGraphicView::OnFont() { // TODO: 在此添加命令处理程序代码 CFontDialog dlg; if (IDOK == dlg.DoModal()) { //判断m_font对象是否已经与某个字体资源相关联了 //m_hObject变量保存了与CGdiObject对象相关联的Windows GDI资源的句柄, //若不为空,即为真,则说明已经关联过 if (m_font.m_hObject) { //如果已经有关联了,则调用DeleteObject释放这个字体资源 m_font.DeleteObject(); } //保存字体对话框选择的字体样式 m_font.CreateFontIndirectW(dlg.m_cf.lpLogFont); //保存设置的字体名称 m_strFontName = dlg.m_cf.lpLogFont->lfFaceName; //让窗口无效 Invalidate(); } }
运行程序,一切正常:
五、示例对话框
再为Graphic程序添加一个类似的功能,也就是在已有的设置对话框中作一个示例区,当用户改变线宽或选择不同的线型时,在示例区也能看到这种改变。
⭕⭕1)首先在设置对话框中增加一个组框,并将它的Caption设置为:示例。组框的默认ID:IDC_STATIC。如果在程序中需要对组框进行操作的话,需要修改它的ID。因为后面的程序会对这个示例组框进行一些操作,所以ID设置为: IDC_SAMPLE。
⭕⭕2)为了反映用户对线宽和线型所做的改变,CSettingDlg类需要捕获编辑框控件和单选按钮的通知消息,以反映出用户所做的改变。- 对编辑框控件来说,当用户在其上面对文本进行改变时,它会向其父窗口,即对话框发送一个EN_CHANGE通知消息。
- 当用户单击单选按钮时,该按钮会向对话框发送BN_CLICKED消息。
2.1)首先利用类向导为CSettingDlg类添加编辑框控件(IDC_LINE_WIDTH)的EN_CHANGE消息的响应函数OnChangeLineWidth。
2.2)然后分别对三个单选按钮(IDC_RADIO1、IDC_RADIO2和IDC_RADIO3),都选择BN_CLICKED消息,用3种方式添加它们的消息响应函数:OnRadio1、OnRadio2和OnRadio3。- 方法1:通过类向导添加命令响应函数
- 方法2:直接鼠标左键点击资源视图中Setting对话框中的虚线按钮:
- 方法3:
2.3)完成示例线条的绘制。
如果把在示例组框中绘制线条的代码在这四个消息响应函数中都写一遍,代码重复性则太高,不太合适。可以考虑这么做:
2.3.1)在这四个函数中调用Invalidate函数让窗口无效,当下一次发生WM_PAINT消息时重绘窗口,void CSettingDlg::OnChangeLineWidth() { // TODO: 如果该控件是 RICHEDIT 控件,它将不 // 发送此通知,除非重写 CDialog::OnInitDialog() // 函数并调用 CRichEditCtrl().SetEventMask(), // 同时将 ENM_CHANGE 标志“或”运算到掩码中。 // TODO: 在此添加控件通知处理程序代码 Invalidate(); } void CSettingDlg::OnClickedRadio1() { // TODO: 在此添加控件通知处理程序代码 Invalidate(); } void CSettingDlg::OnBnClickedRadio2() { // TODO: 在此添加控件通知处理程序代码 Invalidate(); } void CSettingDlg::OnBnClickedRadio3() { // TODO: 在此添加控件通知处理程序代码 Invalidate(); }
2.3.2)然后在该消息响应函数: OnPaint中完成示例线条的绘制。为CSettingDlg类添加WM_PAINT消息的响应函数,并在此函数中完成示例线条的绘制。
void CSettingDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CDialog::OnPaint() //首先根据指定的线宽和线型创建画笔,先将画笔的颜色设置为红色 CPen pen(m_nLineStyle, m_nLineWidth, RGB(255, 0, 0)); //然后将该画笔对象选入设备描述表中 dc.SelectObject(&pen); //获得IDC_SAMPLE组框的矩形 CRect rect; //通过调月GetDlgltem函数来得到指向组框窗口对象的指针 //然后利用GetWindowRect函数获得组相窗口矩形区域的大小 GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect); //用选择的画笔线型和线宽画一条示例线, //示例线的坐标参考组框矩形的坐标位置,起点横坐标:矩形左上角右移20;起点纵坐标:矩形中间位置的纵坐标 //即画一条距组框左侧20pixel到距组框右侧20像素处的线。 dc.MoveTo(rect.left + 20, rect.top + rect.Height() / 2); dc.LineTo(rect.right - 20, rect.top + rect.Height() / 2); }
运行Graphic程序,打开设置对话框,改变线宽的值,但是在示例组框中并没有发现绘制的线条。但是把设置对话框移动到屏幕左则,会出现示例内容:
📋📋 原因:之所以发生这种现象的原因主要是因为GetWindowRect函数的调用,该函数的声明:void GetwindowRect(LPRECT IpRect) const;
该函数的参数是指向CRect或RECT结构体的变量,接收屏幕坐标,也就是说,通过该函数接收到的是屏幕坐标。而在绘图时是以对话框左上角为原点的客户区坐标,而通过GetWindowRect函数得到的屏幕坐标是以屏幕左上角为原点的,这样得到的rect对象的各个坐标值是相对屏幕左上角的,值都比较大。但是绘制线条时又是以对话框客户区的左上角为原点进行的,因此就绘制到对话框的外面,就看不到线条了。
🟢🟢 解决:通过上面的分析,可以知道在得到组框的矩形区域大小后,需要将其由屏幕坐标转为客户坐标。这可以利用ScreenToClient函数来实现。该函数的声明:
void ScreenToclient(LPRECT IpRect) const;
ScreenToClient函数的声明中要求该函数的参数是LPRECT类型,但下面添加的代码中传递的却是CRect对象,程序却能成功编译,这与之前介绍的情况一样,因为CRect类重载了LPRECT操作符。一般情况下,为了明白起见,通常还是为此参数添加一个取地址符,表示传递的是指针。即:
void CSettingDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CDialog::OnPaint() CPen pen(m_nLineStyle,m_nLineWidth, RGB(244,0,0)); dc.SelectObject(&pen); //获得IDC_SAMPLE组框的矩形 CRect rect; GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect); ScreenToClient(&rect); //用选择的画笔线型和线宽画一条示例线, //示例线的坐标参考组框矩形的坐标位置,起点横坐标:矩形左上角右移20;起点纵坐标:矩形中间位置的纵坐标 //即画一条距组框左侧20pixel到距组框右侧20像素处的线。 dc.MoveTo(rect.left + 20, rect.top + rect.Height() / 2); dc.LineTo(rect.right - 20, rect.top + rect.Height() / 2); }
再次运行程序,发现示例框中出现了一条示例线条。
改变线宽的值,但是发现示例组框中线条的宽度并没有发生变化。当一个控件与一个成员变量关联时,
2.3.3)如果想让控件上的值反映到成员变量上,必须调用UpdateData函数。void CSettingDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CDialog::OnPaint() UpdateData(); CPen pen(m_nLineStyle,m_nLineWidth, RGB(244,0,0)); dc.SelectObject(&pen); //获得IDC_SAMPLE组框的矩形 CRect rect; GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect); ScreenToClient(&rect); //用选择的画笔线型和线宽画一条示例线, //示例线的坐标参考组框矩形的坐标位置,起点横坐标:矩形左上角右移20;起点纵坐标:矩形中间位置的纵坐标 //即画一条距组框左侧20pixel到距组框右侧20像素处的线。 dc.MoveTo(rect.left + 20, rect.top + rect.Height() / 2); dc.LineTo(rect.right - 20, rect.top + rect.Height() / 2); }
再次运行Graphic程序,打开设置对话框,改变线宽,或在线宽为1时,选择不同线型,这时在设置对话框的示例组中随时可以看到用户所做的改变。
⭕⭕3)选择的颜色也要反映到示例线条上。
如果希望用户选择了颜色之后,也要反映到示例线条上,就要:
3.1)为设置对话框再添加一个COLORREF类型的成员变量: m_clr,因为将在CGraphicView类中访问这个变量,因此将此变量设置为公有的。3.2)并在对话框的构造函数中将此变量初始化红色:
CSettingDlg::CSettingDlg(CWnd* pParent /*=nullptr*/) : CDialog(IDD_DLG_SETTING, pParent) , m_nLineWidth(0) , m_nLineStyle(0) { m_clr = RGB(255, 0, 0); }
3.3)因为对现在的Graphic程序来说,当用户利用颜色对话框选择某种颜色后,选择的结果就会保存到CGraphicView类的m_clr变量,所以应该在CGraphicView类中在设置对话框显示时将该变量保存的颜色值传递给设置对话框的m_clr变量,即在CGraphicView类的OnSetting函数中,在调用CSettingDlg对象的DoModal函数之前将变量保存的颜色值传递给设置对话框的m_clr变量:
void CGraphicView::OnSetting() { // TODO: 在此添加命令处理程序代码 CSettingDlg dlg; dlg.m_nLineWidth = m_nLineWidth; dlg.m_nLineStyle = m_nLineStyle; //变量保存的颜色值传递给设置对话框的m_clr变量 dlg.m_clr = m_clr; if (IDOK == dlg.DoModal()) { m_nLineWidth = dlg.m_nLineWidth; m_nLineStyle = dlg.m_nLineStyle; } }
3.4)然后修改CSettingDlg类的OnPaint函数中创建画笔的代码,将m_clr的值传递给该创建函数的第三个参数:
void CSettingDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CDialog::OnPaint() UpdateData(); CPen pen(m_nLineStyle,m_nLineWidth, m_clr); dc.SelectObject(&pen); //获得IDC_SAMPLE组框的矩形 CRect rect; GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect); ScreenToClient(&rect); //用选择的画笔线型和线宽画一条示例线, //示例线的坐标参考组框矩形的坐标位置,起点横坐标:矩形左上角右移20;起点纵坐标:矩形中间位置的纵坐标 //即画一条距组框左侧20pixel到距组框右侧20像素处的线。 dc.MoveTo(rect.left + 20, rect.top + rect.Height() / 2); dc.LineTo(rect.right - 20, rect.top + rect.Height() / 2); }
再次运行Graphic程序,打开颜色对话框选择某种颜色,然后打开设置对话框,这时可以看到示例组框中线条的颜色就是刚才所选的颜色。
六、改变对话框和控件的背景及文本颜色
通常,看到的对话框及其上的控件的背景都是浅灰色的,有时为了使程序的界面更加美观,需要更改它们的背景,以及控件上的文本的颜色。
首先介绍一个消息:WM_CTLCOLOR,它的响应函数是CWnd类的OnCtIColor。该函数的声明:
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
■ pDC
指向当前要绘制的控件的显示上下文的指针。
◼ pWnd
指向当前要绘制的控件的指针。
◼ nCtlColor
指定当前要绘制的控件的类型,它的取值如下:
该函数将返回将被用来绘制控件背景的画刷的句柄。当一个子控件将要被绘制时,它都会向它的父窗口发送一个WM_CTLCOLOR(这个父窗口通常都是对话框)消息来准备一个设备上下文(即上述pDC参数),以便使用正确的颜色来绘制该控件。如果想要改变该控件上的文本颜色,可以在OnCtlColor函数中以指定的颜色为参数调用SetTextColor函数来实现。对对话框来说,它上面的每一个控件在绘制时都要向它发送WM_CTLCOLOR消息,它需要为每一个控件准备一个DC,该DC将通过pDC参数传递给OnCtlColor函数。也就是说,对话框对象的OnCtlColor这个消息响应函数会被多次调用。
6.1 改变整个对话框及其上子控件的背景色
下面为Graphic程序的设置对话框(CSettingDlg对象)捕获WM_CTLCOLOR消息,即添加该消息的响应处理:
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 return hbr; }
可以看到,在OnCtlColor这个消息响应函数中,首先调用对话框基类: CDialog的OnCtIColor函数,返回一个画刷句柄(hbr),然后该函数直接返回这个画刷句柄。之后,系统就会使用这个画刷句柄来绘制对话框及其子控件的背景。如果想要改变对话框的背景色,只需要自定义一个画刷,然后让OnCtIColor函数返回这个画刷句柄即可。
⭕⭕1)首先为CSettingDlg类定义一个CBrush类型的私有成员变量: m_brush:
private: CBrush m_brush;
⭕⭕2)在其构造函数中利用CreateSolidBrush函数将该画刷初始化为一个粉色的画刷:
CSettingDlg::CSettingDlg(CWnd* pParent /*=nullptr*/) : CDialog(IDD_DLG_SETTING, pParent) , m_nLineWidth(0) , m_nLineStyle(0) { m_clr = RGB(255, 0, 0); //初始化为一个粉色的画刷 m_brush.CreateSolidBrush(RGB(255,193,193)); }
⭕⭕3)然后在OnCtIColor响应函数返回时返回上述自定义的画刷:m_brush:
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 //return hbr; return m_brush; }
运行Graphic程序,打开设置对话框:
可以看到对话框和控件的背景都变成了粉色:这是因为在该对话框绘制时,会调用OnCtlColor函数绘制整个对话框的背景,即用m_brush画刷来绘制对话框背景。当绘制其子控件时,也是调用这个OnCtlColor函数,也是用m_brush这个画刷来绘制背景的。因此我们看到子控件和对话框的背景都是粉色的。而对于OK和Cancel两个按钮的背景不改变的原因,稍后介绍。6.2 仅改变某个子控件的背景及文本颜色
如果想要精确控制对话框上某个控件(例如本例中的线型组框)的背景的绘制,就需要判断当前绘制的是哪一个控件。
⭕⭕1)通过上面的介绍,通过OnCtlColor函数的第二个参数:pWnd能够知道当前绘制的控件窗口对象,这时可以通过调用Cwnd类的GetDlgCtrlID成员函数得到该控件的ID,然后判断该ID是否就是需要控制其背景绘制的控件ID,若是就处理。GetDlgCtrllD函数的声明:
int GetDlgCtrlID() const;
该函数不仅能返回对话框子控件的ID,还能返回子窗口的ID。但是因为顶层窗口不具有ID值,所以如果调用该函数的CWnd对象是一个顶层窗口,该函数返回的值就是一个无效值。
⭕⭕2)在CSettingDlg类的OnCtIColor函数中就可以利用传递进来的pWnd来调用GetDlgCtrlD函数,然后判断一下如果其返回值等于线型组框的ID (IDC_LINE_STYLE),那么就可以知道当前绘制是的线型组框,那就需要改变该控件的背景色,即返回自定义的画刷,对其他控件仍使用先前的画刷。
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 //ID=IDC_LINE_STYLE的控件的背景设置为粉色 if (pWnd->GetDlgCtrlID()==IDC_LINE_STYLE) { return m_brush; } //其他的背景色保存默认。 return hbr; }
运行Graphic程序,打开设置对话框,可以发现线型组框的背景色变成了粉色。
OnCtlColor函数要求返回HBRUSH类型的画刷句柄,但上述代码中返回了一个CBrush类型的画刷对象。这是因为CBrush类重载了HBRUSH操作符。
⭕⭕3)为了改变线型组框控件上的文本颜色,应在OnCtlColor消息响应函数对当前绘制的控件进行判断,如果判断出当前绘制的控件就是线型组框控件,在返回自定义的画刷之前,就调用SetTextColor函数将该控件上的文本设置为希望的颜色。本例将线型组框控件上的文本设置为绿色:
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 //ID=IDC_LINE_STYLE的控件的背景设置为粉色 if (pWnd->GetDlgCtrlID()==IDC_LINE_STYLE) { //将线型组框控件上的文本设置为绿色 pDC->SetTextColor(RGB(0, 100, 0)); return m_brush; } //其他的背景色保存默认。 return hbr; }
设置的颜色无效,暂时不知道原因。
⭕⭕4)实现编辑框控件背景的改变。实现原理同上,这时在OnCtlColor函数中如果判断当前绘制的是编辑框控件,就设置文本颜色,并返回自定义的画刷。
因此可以参照上面修改线型组框控件的代码来实现编辑框控件背景色和文本颜色的改变:
4.1)改变编辑框的背景颜色HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 /*ID=IDC_LINE_STYLE的控件的背景设置为粉色 if (pWnd->GetDlgCtrlID()==IDC_LINE_STYLE) { //将线型组框控件上的文本设置为绿色 pDC->SetTextColor(RGB(0,100,0)); //将线型组框上的文字的背景设置为透明的 pDC->SetBkMode(TRANSPARENT); return m_brush; }*/ if (pWnd->GetDlgCtrlID()== IDC_LINE_WIDTH) { pDC->SetTextColor(RGB(0, 100, 0)); return m_brush; } //其他的背景色保存默认。 return hbr; }
4.2)改变编辑框字体的颜色为红色:HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 /*ID=IDC_LINE_STYLE的控件的背景设置为粉色 if (pWnd->GetDlgCtrlID()==IDC_LINE_STYLE) { //将线型组框控件上的文本设置为绿色 pDC->SetTextColor(RGB(0,100,0)); //将线型组框上的文字的背景设置为透明的 pDC->SetBkMode(TRANSPARENT); return m_brush; }*/ if (pWnd->GetDlgCtrlID()== IDC_LINE_WIDTH) { pDC->SetTextColor(RGB(255, 0, 0)); return m_brush; } //其他的背景色保存默认。 return hbr; }
4.3)有些控件上的文本本身也有背景色,设置背景色为透明模式,将编辑框上的文字的背景设置为透明的:HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 /*ID=IDC_LINE_STYLE的控件的背景设置为粉色 if (pWnd->GetDlgCtrlID()==IDC_LINE_STYLE) { //将线型组框控件上的文本设置为绿色 pDC->SetTextColor(RGB(0,100,0)); //将线型组框上的文字的背景设置为透明的 pDC->SetBkMode(TRANSPARENT); return m_brush; }*/ if (pWnd->GetDlgCtrlID()== IDC_LINE_WIDTH) { pDC->SetTextColor(RGB(255, 0, 0)); pDC->SetBkMode(TRANSPARENT); return m_brush; } //其他的背景色保存默认。 return hbr; }
4.4)如果要改变单行编辑框控件的背景颜色,可以调用SetBkColor函数设置其背景色。设置其背景色为黑色:HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 /*ID=IDC_LINE_STYLE的控件的背景设置为粉色 if (pWnd->GetDlgCtrlID()==IDC_LINE_STYLE) { //将线型组框控件上的文本设置为绿色 pDC->SetTextColor(RGB(0,100,0)); //将线型组框上的文字的背景设置为透明的 pDC->SetBkMode(TRANSPARENT); return m_brush; }*/ if (pWnd->GetDlgCtrlID()== IDC_LINE_WIDTH) { pDC->SetTextColor(RGB(255, 0, 0)); //pDC->SetBkMode(TRANSPARENT); pDC->SetBkColor(RGB(0, 0, 0)); return m_brush; } //其他的背景色保存默认。 return hbr; }
📢📢📢
- SetTextColor()函数很明显是设置文本颜色的;
- SetBkColor()函数不是用来设置控件背景颜色的,而是用来设置文本背景颜色的,就是包含文本的矩形;
- SetBkMode()是用来设定文字背景模式的,参数只有两个选择OPAQUE、TRANSPARENT表示是否透明。
6.3 改变控件的文本字体
接下来,改变控件上文本的字体。也就是说,在绘制控件时为其准备一种字体,让它按照此种字体显示。
⭕⭕1)为了显示控件字体的改变效果,再为Graphic程序的设置对话框资源增加一个静态文本控件,设置其ID为: IDC_TEXT, Caption:WaitFoF,你好啊!:
⭕⭕2)然后在程序中修改该控件的文本字体。先为CSettingDlg类增加一个CFont类型的私有成员变量: m_font:
⭕⭕3)其构造函数中初始化该变量,创建一个大小为70,名称为“汉仪综艺体简”的字体。:CSettingDlg::CSettingDlg(CWnd* pParent /*=nullptr*/) : CDialog(IDD_DLG_SETTING, pParent) , m_nLineWidth(0) , m_nLineStyle(0) { m_clr = RGB(255, 0, 0); //初始化为一个粉色的画刷 m_brush.CreateSolidBrush(RGB(255, 193, 193)); //创建一个大小为70,名称为“汉仪综艺体简”的字体 m_font.CreatePointFont(70, _T("汉仪综艺体简")); }
⭕⭕4)然后在CSettingDlg类的OnCtlColor函数中判断当前绘制的如果是静态文本框控件,那么就将新建的字体m_font选入设备描述表中,这样DC中的字体就被改变了,它就会使用新创建的字体来显示文本:
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 //ID=IDC_LINE_STYLE的控件的背景设置为粉色 if (pWnd->GetDlgCtrlID()==IDC_LINE_STYLE) { //将线型组框控件上的文本设置为绿色 pDC->SetTextColor(RGB(0,100, 0)); //将线型组框上的文字的背景设置为透明的 pDC->SetBkMode(TRANSPARENT); return m_brush; } if (pWnd->GetDlgCtrlID()== IDC_LINE_WIDTH) { pDC->SetTextColor(RGB(255,0,0)); //pDC->SetBkMode(TRANSPARENT); pDC->SetBkColor(RGB(0, 0, 0)); return m_brush; } if (pWnd->GetDlgCtrlID() == IDC_TEXT) { pDC->SelectObject(&m_font); } //其他的背景色保存默认。 return hbr; }
6.4 改变按钮控件的背景色及文本颜色
接下来,根据上面的知识。按照同样的方法改变Graphic程序中设置对话框上的OK按钮的背景色及其文字的颜色。
⭕⭕1)在CSettingDlg类的OnCtlColor函数中,如果判断出当前绘制的控件的ID等于OK按钮的ID:IDOK,那么就返回自定义的画刷:
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 //ID=IDC_LINE_STYLE的控件的背景设置为粉色 if (pWnd->GetDlgCtrlID()==IDC_LINE_STYLE) { //将线型组框控件上的文本设置为绿色 pDC->SetTextColor(RGB(255, 0, 0)); //pDC->SetBkColor(RGB(255, 0, 0)); //将线型组框上的文字的背景设置为透明的 //pDC->SetBkMode(TRANSPARENT); return m_brush; } if (pWnd->GetDlgCtrlID()== IDC_LINE_WIDTH) { pDC->SetTextColor(RGB(255,0,0)); //pDC->SetBkMode(TRANSPARENT); //背景黑色 pDC->SetBkColor(RGB(0, 0, 0)); return m_brush; } if (pWnd->GetDlgCtrlID() == IDC_TEXT) { pDC->SelectObject(&m_font); } if (pWnd->GetDlgCtrlID() == IDOK) { //红色字体 pDC->SetTextColor(RGB(255, 0, 0)); return m_brush; } //其他的背景色保存默认。 return hbr; }
运行Graphic程序,发现新加的代码并没有起作用,OK按钮的背景色和文字颜色并没有被改变。
对于按钮来说,使用上述方法来改变其背景和文本颜色是无效的,只能寻找其他的解决方法。
⭕⭕2)实际上,如果想要改变按钮控件的背景色和文本颜色,需要使用CButton类的一个成员函数:DrawItem,该函数的声明:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
从其声明形式,可以知道这个函数是一个虚函数。当一个自绘制按钮(具有BS_OWNERDRAW风格的按钮)在绘制时,框架将会调用这个虚函数。因此,如果想要实现一个自绘制按钮控件的绘制,应该重载这个虚函数。该函数的参数是DRAWITEMSTRUCT结构类型,其定义如下所示:
typedef struct tagDRAWITEMSTRUCT { UINT CtlType; UINT CtlID; UINT itemID; UINT itemAction; UINT itemState; HWND hwndItem; HDC hDC; RECT rcItem; ULONG_PTR itemData; } DRAWITEMSTRUCT;
该结构体中有一个hDC成员,指向将要绘制的按钮的DC。为了绘制这个按钮,可以向该DC中选入自定义的颜色、画刷等对象。但此重载函数结束前,一定要恢复hDC中原有对象。
因此,如果想要改变OK按钮的文本颜色:
- 需要编写一个自己的按钮类,让这个类派生于CButton类;
- 重写Drawltem函数,在此函数中实现按钮背景色和文本颜色的设置;
- 然后,将OK按钮对象与这个类相关联。
这样,在绘制OK按钮时,框架就会调用这个自定义的按钮类的Drawltem函数来绘制。
🔳🔲◼▪◼🔲🔳 改变按钮文字颜色
2.1)Graphic程序创建一个派生于CButton的类,新建MFC类。将新增的类命名为:CTestBtn、基类: CButton。
CTestBtn.h:
#pragma once // CTestBtn class CTestBtn : public CButton { DECLARE_DYNAMIC(CTestBtn) public: CTestBtn(); virtual ~CTestBtn(); protected: DECLARE_MESSAGE_MAP() };
CTestBtn.cpp:
// CTestBtn.cpp: 实现文件 // #include "pch.h" #include "Graphic.h" #include "CTestBtn.h" // CTestBtn IMPLEMENT_DYNAMIC(CTestBtn, CButton) CTestBtn::CTestBtn() { } CTestBtn::~CTestBtn() { } BEGIN_MESSAGE_MAP(CTestBtn, CButton) END_MESSAGE_MAP() // CTestBtn 消息处理程序
2.2)为此类添加DrawItem虚函数的重写:
void CTestBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // TODO: 添加您的代码以绘制指定项 UINT uStyle = DFCS_BUTTONPUSH; //这段代码只对按钮有效。 ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON); //如果选择绘图,将样式添加到DrawFrameControl。 if (lpDrawItemStruct->itemState & ODS_SELECTED) uStyle |= DFCS_PUSHED; // 绘制按钮框架 ::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, uStyle); //获取按钮的文本。 CString strText; GetWindowText(strText); //按钮文字使用文字的颜色为绿色。 COLORREF crOldColor = ::SetTextColor(lpDrawItemStruct->hDC, RGB(0, 255, 0)); ::DrawText(lpDrawItemStruct->hDC,strText,strText.GetLength(), &lpDrawItemStruct->rcItem,DT_SINGLELINE | DT_VCENTER | DT_CENTER); ::SetTextColor(lpDrawItemStruct->hDC, crOldColor); }
2.3)为设置对话框上的OK按钮关联一个成员变量。将变量名称设置为:m_btnTest,类型选择为:CTestBtn:
在SettingDlg.h文件中包含CTestBtn类的定义文件:#include "CTestBtn.h"
此外自绘制控件应该具有BS_OWNERDRAW风格,这可以通过设置控件属性对话框中的Owner Draw选项为True:
运行Graphic程序,打开设置对话框,这时可以看到OK按钮的文字变成绿色,即CTestBtn类的设置起作用:
此时可以删除CSettingDlg类中OnCtlColor中关于OK按钮的代码。
🔳🔲◼▪◼🔲🔳 改变按钮背景颜色
为了改变OK按钮的背景色,使用CButtonST类,此类是在网上找到的一个按钮类,这个类的功能比较丰富,可以用于实际开发中。CButtonST类的源码中含有如下文件:
为了演示CButtonST类的使用,在Graphic程序中的设置对话框资源上再添加一个按钮控件,将其ID设置:IDC_BTN_ST,Caption设置为:ButtonST:
下面演示使用这个类:
2.1)将类的头文件和源文件复制到自己的Graphic工程所在目录下,并将它们添加到自己的Graphic工程中。方法如下:
2.2)在SettingDlg.h文件中包含CButtonST类的头文件:#include "BtnST.h"
2.3)为CButtonST按钮添加一个关联的成员变量:
与上面设置OK按钮背景色和文本颜色的实现方法不同的是,使用CButtonST这个类时,并不需要通过按钮属性对话框设置其Owner Draw选项为True,这个类的内部会自动设置BS_OWNERDRAW分格。2.4)接下来初始化m_btnST变量,可以放到CSettingDlg类的OnInitDialog函数中进行,所以在CSettingDlg中重写OnInitDialog函数。
初始化工作包括两个部分:设置背景色和前景色,后者也就是按钮活动时文字的颜色。BOOL CSettingDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 //造成了m_btnST的重复子类化————屏蔽 //m_btnST.SubclassDlgItem(IDC_BTN_ST,this); //普通状态时的背景色 #9400D3 m_btnST.SetColor(CButtonST::BTNST_COLOR_BK_OUT, RGB(148,0,211)); //普通状态时的前景色 m_btnST.SetColor(CButtonST::BTNST_COLOR_FG_OUT, RGB(255,0,0)); //鼠标放在按钮内时的背景色 m_btnST.SetColor(CButtonST::BTNST_COLOR_BK_IN, RGB(205,155,155)); //鼠标放在按钮内时的前景色 m_btnST.SetColor(CButtonST::BTNST_COLOR_FG_IN, RGB(72, 118, 255)); //按钮被按下后的背景色 m_btnST.SetColor(CButtonST::BTNST_COLOR_BK_FOCUS, RGB(255,127, 0)); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE }
使用的工具类,可点击下载:CButtonST
七、位图显示
有多种方式可以实现在窗口中显示位图:
-
第一步是创建位图,这可以先利用CBitmap构造一个位图对象,然后利用LoadBitmap函数加载一幅位图资源。
-
第二步是创建兼容DC。其中CreateCompatibleDC函数将创建一个内存设备上下文,与参数pDC所指定的DC相兼容。内存设备上下文实际上是一个内存块,表示一个显示的表面。如果想把图像复制到实际的DC中,可以先用其兼容的内存设备上下文在内存中准备这些图像,然后再将这些数据复制到实际DC中。
-
第三步是将位图选入兼容DC中。当兼容的内存设备上下文被创建时,它的显示表面是标准的一个单色像素宽和一个单色像素高。在应用程序中可以使用内存设备上下文进行绘图操作之前,必须将一个具有正确高度和宽度的位图选入设备上下文,这时内存设备上下文的显示表面大小就由当前选入的位图决定了。
-
第四步是将兼容DC中的位图贴到当前DC中。有多个函数可以以几种不同的方式完成这一操作。调用BitBlt函数将兼容DC中的位图复制到当前DC中。BitBlt函数的功能是把源设备上下文中的位图复制到目标设备上下文中。该函数的声明形式如下所示:
BOOL BitBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc,int ySrc, DWORD dwRop);
◼ x和y
指定目标矩形区域左上角的x和y坐标。
◼ nWidth和nHeight
指定源和目标矩形区域的逻辑宽度和高度。因为该函数在复制时是按照1:1的比例进行的,所以源矩形区域和目标矩形区域的宽度和高度是相同的。
◼ pSrcDC
指向源设备上下文对象。
◼ xSrc 和 ySrc
指定源矩形区域左上角的x和y坐标。
◼ dwRop
指定复制的模式,也就是指定源矩形区域的颜色数据,如何与目标矩形区域的颜色数据组合以得到最后的颜色。
如果函数调用成功,则BitBIt函数将返回非0值;否则返回0。
按照上述四个步骤,来完成在窗口显示位图的功能。
⭕⭕1)首先需要准备一幅位图。
然后在Graphic工程中,导入位图资源:
调整位图:⭕⭕2)完成位图的显示。
首先需要了解一下窗口的绘制过程,包含两个步骤:- 首先擦除窗口背景,
- 然后在对窗口重新进行绘制。
🔳🔲◼▪◼🔲🔳擦除窗口背景时完成位图的显示。
当擦除窗口背景时,程序会发送一个WM_ERASEBKGND消息,因此可以在此响应函数中完成位图的显示。另外,根据前面的知识,可以知道应该在视类窗口中进行位图的绘制。
因此为Graphic工程的CGraphicView类添加WM_ERASEBKGND消息的响应处理函数:OnEraseBkgnd。
BOOL CGraphicView::OnEraseBkgnd(CDC* pDC) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //第一步: //首先构建位图对象: bitmap CBitmap bitmap; //加载图像 bitmap.LoadBitmap(IDB_BITMAP1); //第二步:创建兼容的DC //创建与当前DC (pDC)兼容的DC: dcCompatible CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); //第三步:将位图选入兼容DC中 //调用SelectObject函数将位图选入兼容DC中,从而确定兼容DC显示表面的大小 dcCompatible.SelectObject(&bitmap); //第四步:将兼容DC (dcCompatible)中的位图复制到目的DC (pDC)中 //因为要指定复制目的矩形区域的宽度和高度,首先需要得到目的DC客户区大小,所以就构造一个CRect对象 CRect rect; //然后调用GetClientRect函数得到客户区大小 GetClientRect(&rect); //源DC就是先前创建的兼容DC, //复制模式选择: SRCCOPY:就是将源位图复制到目的矩形区域中 pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0, SRCCOPY); //最后,调用视类的基类(即CView)的OnEraseBkgnd函数。 return CView::OnEraseBkgnd(pDC); }
运行Graphic程序,但是看到程序窗口在显示时,位图只是闪了一下就消失了。主要是因为在上述OnEraseBkgnd代码中,在调用BitBIt函数复制位图之后,又调用了视类的基类(即CView)的OnEraseBkgnd函数,该函数的调用把窗口的背景擦除了,所以位图只是闪了一个就消失了。
⭕⭕3)对OnEraseBkgnd函数来说,如果其擦除了窗口背景,将返回非0值。因此CGraphicView类的OnEraseBkgnd函数的最后,不应该再调用其基类的OnEraseBkgnd函数,而是应该直接返回TRUE值:
BOOL CGraphicView::OnEraseBkgnd(CDC* pDC) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //第一步: //首先构建位图对象: bitmap CBitmap bitmap; //加载图像 bitmap.LoadBitmap(IDB_BITMAP1); //第二步:创建兼容的DC //创建与当前DC (pDC)兼容的DC: dcCompatible CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); //第三步:将位图选入兼容DC中 //调用SelectObject函数将位图选入兼容DC中,从而确定兼容DC显示表面的大小 dcCompatible.SelectObject(&bitmap); //第四步:将兼容DC (dcCompatible)中的位图复制到目的DC (pDC)中 //因为要指定复制目的矩形区域的宽度和高度,首先需要得到目的DC客户区大小,所以就构造一个CRect对象 CRect rect; //然后调用GetClientRect函数得到客户区大小 GetClientRect(&rect); //源DC就是先前创建的兼容DC, //复制模式选择: SRCCOPY:就是将源位图复制到目的矩形区域中 pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0, SRCCOPY); return TRUE; }
⭕⭕4)位图压缩和拉伸。
4.1)若要在窗口中完整地显示一幅位图,如果位图比窗口大,就要压缩位图;如果小,就可以拉伸位图。然而BitBlt函数是没有办法实现压缩和拉伸的,因为它是按照1:1的比例进行复制的。再介绍另一个显示位图的函数:StretchBlt
,其声明:BOOL StretchBlt (int x, int y, int nwidth, int nHeight, CDC* pSrcDc, int xSrc, int ysrc, int nSrcwidth, int nSrcHeight, DWORD dwRop );
该函数与前面介绍的BitBlt函数的功能基本相同,都是从源矩形区域中复制一个位图到目标矩形,但是与BitBlt函数不同的是:StretchBlt函数可以实现位图的拉伸或压缩,以适合目的矩形区域的尺寸。
与BitBlt函数相比,StretchBlt函数只是多了两个参数: nSrcWidth和nSrcHeight,分别用来指示源矩形区域的宽度和高度。
4.2)前面已经说过,兼容DC原始只有1个像素大小。它的大小由选入的位图的大小所决定,也就是说,如果想要得到源矩形的宽度和高度,就要想办法得到选入的位图的宽度和高度。选入的位图的宽度和高度可以通过调用CBitmap类的GetBitmap函数来得到,该函数的原型声明如下:
int GetBitmap(BITMAP* pBitMap);
该函数有一个参数,是一个指向BITMAP结构体的指针,该函数将用位图的信息填充,BITMAP结构体的定义如下:
typedef struct tagBITMAP { LONG bmType; LONG bmWidth; LONG bmHeight; LONG bmWidthBytes; WORD bmPlanes; WORD bmBitsPixel; LPVOID bmBits; } BITMAP;
其中bmWidth指示位图的宽度,bmHeight指示位图的高度。
4.3)因此在CGraphicView类的OnEraseBkgnd函数中,在位图对象bitmap成功加载位图资源之后,通过调用CBitmap类的GetBitmap函数来得到选入的位图的宽度和高度,接下来就可以调用StretchBlt函数复制位图了。
BOOL CGraphicView::OnEraseBkgnd(CDC* pDC) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //第一步: //首先构建位图对象: bitmap CBitmap bitmap; //加载图像 bitmap.LoadBitmap(IDB_BITMAP1); BITMAP bmp; bitmap.GetBitmap(&bmp); //第二步:创建兼容的DC //创建与当前DC (pDC)兼容的DC: dcCompatible CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); //第三步:将位图选入兼容DC中 //调用SelectObject函数将位图选入兼容DC中,从而确定兼容DC显示表面的大小 dcCompatible.SelectObject(&bitmap); //第四步:将兼容DC (dcCompatible)中的位图复制到目的DC (pDC)中 //因为要指定复制目的矩形区域的宽度和高度,首先需要得到目的DC客户区大小,所以就构造一个CRect对象 CRect rect; //然后调用GetClientRect函数得到客户区大小 GetClientRect(&rect); //源DC就是先前创建的兼容DC, //复制模式选择: SRCCOPY:就是将源位图复制到目的矩形区域中 //pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0, SRCCOPY); pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY); return TRUE; }
运行Graphic程序,这时就可以看到整幅位图都显示出来了:
🔳🔲◼▪◼🔲🔳窗口重绘时完成位图的显示。
本例是在窗口显示更新的第一步,即擦除窗口背景这一步实现位图的显示。也可以在窗口显示更新的第二步,即重绘窗口时实现这一功能。窗口重绘时会调用OnDraw函数,因此可以把显示位图的代码放到这个函数中。
void CGraphicView::OnDraw(CDC* pDC) { CGraphicDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: 在此处为本机数据添加绘制代码 CFont* pOldFont = pDC->SelectObject(&m_font); pDC->TextOutW(0, 0, m_strFontName); pDC->SelectObject(pOldFont); //第一步: //首先构建位图对象: bitmap CBitmap bitmap; //加载图像 bitmap.LoadBitmap(IDB_BITMAP1); BITMAP bmp; bitmap.GetBitmap(&bmp); //第二步:创建兼容的DC //创建与当前DC (pDC)兼容的DC: dcCompatible CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); //第三步:将位图选入兼容DC中 //调用SelectObject函数将位图选入兼容DC中,从而确定兼容DC显示表面的大小 dcCompatible.SelectObject(&bitmap); //第四步:将兼容DC (dcCompatible)中的位图复制到目的DC (pDC)中 //因为要指定复制目的矩形区域的宽度和高度,首先需要得到目的DC客户区大小,所以就构造一个CRect对象 CRect rect; //然后调用GetClientRect函数得到客户区大小 GetClientRect(&rect); //源DC就是先前创建的兼容DC, //复制模式选择: SRCCOPY:就是将源位图复制到目的矩形区域中 //pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0, SRCCOPY); pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY); }
总结比较:程序显示的结果是一样的,但是产生的效果是不一样的。
- 窗口重绘时完成位图的显示
当窗口尺寸发生变化时,程序窗口会有闪烁现象,这是因为当窗口尺寸发生变化时,会引起窗口重绘操作,它会首先擦除背景,然后在OnDraw函数中再重新贴上位图。 - 窗口擦除时完成位图的显示
窗口闪烁比较小。因为没有擦除背景,而是直接贴上位图。
-
-
在VS2010的环境下,给mfc下的对话框添加启动画面
2015-10-09 10:35:22摸索了半天,终于完成简单的开机启动画面了,不过大部分都是套用别人的模板的,即下载Spash.cpp 以及Splash.h文件并添加至工程中,该文件在附件中有讲。 首先下载一张.BMP图形,该图形最好和你的对话框的尺寸大小...摸索了半天,终于完成简单的开机启动画面了,不过大部分都是套用别人的模板的,即下载Spash.cpp 以及Splash.h文件并添加至工程中,该文件在附件中有讲。
首先下载一张.BMP图形,该图形最好和你的对话框的尺寸大小一致,并将该图形导入至资源视图中,设置其ID为IDB_SPLASH
在你的对话框的***.cpp下的InitInstance()函数中添加以下语句:
CShellManager *pShellManager = new CShellManager;
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
CSplashWnd::EnableSplashScreen(cmdInfo.m_bShowSplash);
当然要在该文件的前面添加 #include"Splash.h"同样在***Dlg.cpp文件的前面添加#include"Splash.h"
在该文件下添加OnCreate虚函数
int ***Dlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
CSplashWnd::ShowSplashScreen(this);//显示启动画面
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
return 0;
}
BOOL***Dlg::Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd)
{
// TODO: 在此添加专用代码和/或调用基类
return CDialog::Create(IDD, pParentWnd);
}
运行一下,就显示成功了,显示的时间可以在Spash.cpp的OnCreate函数中修改
文件竟然上传不了 那我直接复制在这上面吧!
Spash.cpp
#include "stdafx.h" // e. g. stdafx.h
#include "resource.h" // e.g. resource.h
#include "Splash.h" // e.g. splash.h
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
/
// Splash Screen class
BOOL CSplashWnd::c_bShowSplashWnd;
CSplashWnd* CSplashWnd::c_pSplashWnd;
CSplashWnd::CSplashWnd()
{
}
CSplashWnd::~CSplashWnd()
{
// Clear the static window pointer.
ASSERT(c_pSplashWnd == this);
c_pSplashWnd = NULL;
}
BEGIN_MESSAGE_MAP(CSplashWnd, CWnd)
//{{AFX_MSG_MAP(CSplashWnd)
ON_WM_CREATE()
ON_WM_PAINT()
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CSplashWnd::EnableSplashScreen(BOOL bEnable /*= TRUE*/)
{
c_bShowSplashWnd = bEnable;
}
void CSplashWnd::ShowSplashScreen(CWnd* pParentWnd /*= NULL*/)
{
if (!c_bShowSplashWnd || c_pSplashWnd != NULL)
return;
// Allocate a new splash screen, and create the window.
c_pSplashWnd = new CSplashWnd;
if (!c_pSplashWnd->Create(pParentWnd))
delete c_pSplashWnd;
else
c_pSplashWnd->UpdateWindow();
}
BOOL CSplashWnd::PreTranslateAppMessage(MSG* pMsg)
{
if (c_pSplashWnd == NULL)
return FALSE;
// If we get a keyboard or mouse message, hide the splash screen.
if (pMsg->message == WM_KEYDOWN ||
pMsg->message == WM_SYSKEYDOWN ||
pMsg->message == WM_LBUTTONDOWN ||
pMsg->message == WM_RBUTTONDOWN ||
pMsg->message == WM_MBUTTONDOWN ||
pMsg->message == WM_NCLBUTTONDOWN ||
pMsg->message == WM_NCRBUTTONDOWN ||
pMsg->message == WM_NCMBUTTONDOWN)
{
c_pSplashWnd->HideSplashScreen();
return TRUE; // message handled here
}
return FALSE; // message not handled
}
BOOL CSplashWnd::Create(CWnd* pParentWnd /*= NULL*/)
{
if (!m_bitmap.LoadBitmap(IDB_SPLASH))
return FALSE;
BITMAP bm;
m_bitmap.GetBitmap(&bm);
return CreateEx(0,
AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
NULL, WS_POPUP | WS_VISIBLE, 0, 0, bm.bmWidth, bm.bmHeight, pParentWnd->GetSafeHwnd(), NULL);
}
void CSplashWnd::HideSplashScreen()
{
// Destroy the window, and update the mainframe.
DestroyWindow();
AfxGetMainWnd()->UpdateWindow();
}
void CSplashWnd::PostNcDestroy()
{
// Free the C++ class.
delete this;
}
int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
CenterWindow();
SetTimer(1, 3000, NULL);//显示时间设置为3s
return 0;
}
void CSplashWnd::OnPaint()
{
CPaintDC dc(this);
CDC dcImage;
if (!dcImage.CreateCompatibleDC(&dc))
return;
BITMAP bm;
m_bitmap.GetBitmap(&bm);
// Paint the image.
CBitmap* pOldBitmap = dcImage.SelectObject(&m_bitmap);
dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcImage, 0, 0, SRCCOPY);
dcImage.SelectObject(pOldBitmap);
}
void CSplashWnd::OnTimer(UINT nIDEvent)
{
// Destroy the splash screen window.
HideSplashScreen();
}
splash.h文件为:
#ifndef _SPLASH_SCRN_
#define _SPLASH_SCRN_
// Splash.h : header file
//
/
// Splash Screen class
class CSplashWnd : public CWnd
{
// Construction
protected:
CSplashWnd();
// Attributes:
public:
CBitmap m_bitmap;
// Operations
public:
static void EnableSplashScreen(BOOL bEnable = TRUE);
static void ShowSplashScreen(CWnd* pParentWnd = NULL);
static BOOL PreTranslateAppMessage(MSG* pMsg);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSplashWnd)
//}}AFX_VIRTUAL
// Implementation
public:
~CSplashWnd();
virtual void PostNcDestroy();
protected:
BOOL Create(CWnd* pParentWnd = NULL);
void HideSplashScreen();
static BOOL c_bShowSplashWnd;
static CSplashWnd* c_pSplashWnd;
// Generated message map functions
protected:
//{{AFX_MSG(CSplashWnd)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint();
afx_msg void OnTimer(UINT nIDEvent);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#endif
-
如何把西门子精智面板画面直接转换成博图WINCC RT ADV画面
2020-12-19 13:51:39那么有没有什么方法只写HMI程序,然后把HMI程序复制到上位机运行即可呢?西门子博图V15.0就有这个功能。方法如下所示:(1)打开博图V15.0软件,进入工程开发界面。在“HMI_1[TP1500 Comfort]”处右击,选择“复制”,... -
第4章 Qt 5基本对话框
2021-09-09 21:44:06首先介绍标准文件对话框(QFileDialog)、标准颜色对话框(QColorDialog)、标准字体对话框(QFontDialog)、标准输入对话框(QInputDialog)及标准消息对话框(QMessageBox),运行效果如图4.1所示。 按如图4.1... -
ABAP 消息弹出框,对话框
2021-06-11 11:52:30弹出对话框 代码: DATA: LV_ANSWER TYPE STRING. "用于存储用户选择 CALL FUNCTION 'POPUP_TO_DECIDE_WITH_MESSAGE' EXPORTING DIAGNOSETEXT1 = '推送成功' "诊断文本的第一行 TEXTLINE1 = '2' "弹出窗口的... -
抖音对话框视频怎么做?如何在视频画面上添加对话气泡框?视频加对话气泡的方法
2021-05-16 19:41:48然后再打开软件的安装目录,将刚刚复制的程序全部粘贴到安装目录下,选择“复制和替换”选项哦。然后就可以正常使用软件了,双击打开软件,这是软件打开一个引导页面,我们还可以来录制电脑桌面视频。当然,我们... -
Android对话框的使用总结
2016-10-14 19:15:17本文对各类对话框的设计和使用都有比较详细的介绍。 一个对话框一般是一个出现在当前Activity之上的一个小窗口. 处于下面的Activity失去焦点, 对话框接受所有的用户交互. 对话框一般用于提示信息和与当前应用程序... -
自定义一个简单的加载对话框
2019-04-07 09:29:06提醒:样式可以按下面的创建对话框代码写,其他的忽略即可(例如静态引用 Context),会造成内存泄漏,自己在 Activity 里随便封装个方法就行了。 在公司项目遇到了这么一个小需求,在加载网页的时候显示一个进度条... -
怎么把电脑命令行窗口里的内容复制到剪贴板
2021-07-28 10:20:48想要把电脑命令行窗口里的内容复制到剪贴板,该如何操作呢,那么怎么把电脑命令行窗口里的内容复制到剪贴板的呢?下面是学习啦小编收集整理的怎么把电脑命令行窗口里的内容复制到剪贴板,希望对大家有帮助~~把电脑... -
python模拟按键与鼠标操作(二)将复制内容粘贴至指定文本框中
2020-06-24 22:15:56一、简介 有时我们需要调用pywin32库操作页面时,经常需要传入一些变量在页面的文本框中,比如查询数据的日期,查询数据的类型等等,这时我们可以利用datatime生成我们想要的时间,定义为一个变量,然后传入页面的... -
基础语法篇_7——MFC对话框:逃跑按钮、属性表单、向导创建
2020-03-02 21:17:48文章目录一、基于对话框的程序二、“逃跑”的按钮三、属性表单和向导的创建3.1 创建属性页 一、基于对话框的程序 新建一个基于对话框的项目,项目名称:DlgTest。 生成的项目结构为: 解决方案结构: 类视图下有三... -
QT5开发及实例学习之九基本对话框(二)
2021-09-10 14:29:35本章将介绍QToolBox类的使用、进度条的用法、QPalette类的用法、QTimer类的用法、mousePressEvent/mouseMoveEvent类的用法、可扩展对话框的基本实现方法、不规则窗体的实现及程序启动画面(QSplashScreen)的使用... -
Alt + PrintScreen复制当前活动窗口到剪切板
2018-12-08 21:45:35众所周知,PrintScreen键可以复制整个屏幕的内容到剪切板,但是如果我们只需要复制屏幕上某个窗口的内容呢? 一种方式是使用Windows自带的截图工具(win + R在“运行中输入snippingtool”)或者微信的截图工具... -
基础语法篇_6——对话框的创建与显示、动态创建按钮、控件的访问【控件调整|静态文本控件|编辑框控件】、...
2020-02-27 20:04:29对话框就是一个窗口,它不仅可以接收消息,而且可以被移动和关闭,甚至可以在客户区进行绘图。对话框相当于一个容器,在它上面能够放置各种各样的标准控件和扩展控件,使程序支持用户输入的手段更加丰富。 1.1 常用... -
凯立德导航开机画面修改
2012-09-27 09:53:43将压缩包解压到一个单独文件夹中。...启动BMP2KLD.exe,点击“分解KLD StartUpRes” ,在弹出的对话框中选择刚刚复制进去的StartUpRes.dll。程序会自动分解出启动画面,稍后会提示StartUpRes分解完成。 -
mfc对话框ok没效果_利用PS制作逼真双重曝光效果案例演示,合成紫色城市建筑风格海报图片...
2020-12-22 02:28:2610、下面我们再做一些细化的工作,现在整个画面还是有一些沉闷。新建一个图层,点击“渐变工具”,打开“渐变编辑器”,如下图所示,偷个懒,直接选择“预设”中的“色谱”渐变,向下拉掉不需要的色块,我们做一个... -
向画面中快速填充前景色的快捷键是()。
2021-04-24 16:53:52【Alt】 限制在自由变换模式下 【Shift】 扭曲在自由变换模式下 【Ctrl】 取消变形在自由变换模式下 【Esc】 自由变换复制的象素数据 【Ctrl】+【Shift】+【T】 再次变换复制的象素数据并建立一个副本【Ctrl】+... -
为基于对话框的应用程序设置启动画面
2010-05-24 17:14:00下载地址:http://download.csdn.net/source/23887462、把Splash.cpp 和 Splash.h复制到你的工程目录下,然后选择“Project——Add To Project——Files”,在打开的对话框中选择此两个文件。3、为工程添加一位图... -
openCV 对话框 显示图像
2013-01-15 15:07:40// CVDlgReadDlg.h : 头文件 // #pragma once #include #include #include #include using namespace std; using namespace cv;...// CCVDlgReadDlg 对话框 class CCVDlgReadDlg : public CDialog -
Vue深拷贝将Form表单行传入dialog
2022-01-29 17:39:42huidui 通常我们在修改后台数据时,也就修改前端页面显示的一行数据时,会将某一行的数据传入dialog以便用户修改, 再通过dialog里面的提交按钮来...来对数据进行深拷贝,复制一份数据,不会对展示界面的值由影响 -
手机端程序员刷题-repl.it-mobile:Repl.it的移动应用程序
2021-07-01 12:34:34手机端程序员刷题复制手机 在线编码平台和社区的移动客户端。 这是一项正在进行的工作,如果您有兴趣! 如果你想尝试一下,我有时会主持一个“隧道”。 如果您想访问它,请下载并在您的手机上打开。 如果您使用的是 ... -
计算机基础实验二.pdf
2020-12-16 21:13:42计算机基础实验二 计算机信息技术基础实验二 练习 1Win 98/2000 的桌面...4 在桌面上新建一个名为 上机实验作业的文件夹 5将系统 日期/时间属性对话框的画面复制到 写字板应用程序的文档中并将此文件保存 在 上机实验作 -
来自韩国的影音全能播放器 KMPlayer 4.2.2.45 x64 中文多语免费版.zip
2021-05-19 09:36:521.捕获音频:选择此项后,会弹出“音频捕获”对话框,在这里可以指定输出路径及文件名,然后单击“开始”按钮,即可将当前正在播放的视频的音频信息保存在一个MP3文件当中,简单说就是将电影的声音提取出来。... -
有些网页的内容为什么不能复制
2018-05-14 10:22:19估计在很多时候你会遇到很多的优美的比较实用的文章,你很想将其复制下来然后为己所用,但是可恨的是不知网页到底加了些什么东西搞得你复制不小来,如果去一个字一个字的打出来估计会浪费很多的时间,那么如何才能... -
学习:用SQL实现分布式数据复制(转)
2019-09-27 01:23:46复制的概念 复制作为一个重要并且强大的技术,为分布式数据的存储和处理提供了有力支持。微软公司的SQL Server可以生成数据的拷贝,并能把这些数据的拷贝分发到不同的地方,自动进行数据的同步,保持所有的数据拷贝... -
QQ2005 beta2 私服咨询版(显IP).rar
2019-08-29 21:17:48作为2005年的第一个重要版本,QQ2005beta2私服咨询版 将与... 另外,当鼠标悬停在左上方的头像和号码处或者消息对话框的边缘处(可定制),将显示对方的 IP 信息及地理位置,此时单击鼠标右键可将 IP 信息复制到剪贴板。 -
CST微波工作室学习笔记4 快速演练(2)
2022-01-18 15:14:21实例:选中方块1,选中Translate(平移)操作,并在Translation vector(平移矢量)中输入(5,0,0),再选中Copy(复制)选项,并在 Repetition factor(重复次数)栏中输入2,得到如下图形: 注:变换后如只有一个... -
《计算机基础》在线作业,重庆西南大学17年秋季1056《计算机基础》在线作业(参考复习资料)...
2021-06-23 01:04:12“复制““剪切““删除““截屏“27、在 Windows中,在桌面上打开的多个窗口的排列方式“只能层叠排列““只能并排显示““只能堆叠显示““既可以设置并排排列,也可以设置层叠排列“28、在 Windows对话框中,不...