精华内容
下载资源
问答
  • AS对话框

    2020-07-06 17:45:28
    一个对话框一般是一个出现当前Activity之上的一个小窗口. 处于下面的Activity失去焦点, 对话框接受所有的用户交互. 对话框一般用于提示信息和与当前应用程序直接相关的小功能. Android API 支持下列类型的对话框...

    一.相关概念

    一个对话框一般是一个出现在当前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

    展开全文
  • 文章目录一、简单绘图二、设置对话框2.1 设置线宽2.2 设置线型 新建一个单文档类型的MFC工程,取名:Graphic。此程序将实现简单的绘图功能。 一、简单绘图 实现简单的绘图功能,包括点、直线和椭圆的绘制。为了实现...

    🔳🔳 绘制线条 、画刷绘图、绘制连续线条、绘制扇形效果的线条


    🔳🔳 插入符【文本插入符|图形插入符】、窗口重绘、路径、字符输入【设置字体|字幕变色】


    🔳🔳 菜单命令响应函数、菜单命令的路由、基本菜单操作、动态菜单操作、电话本实例


    🔳🔳 对话框的创建与显示、动态创建按钮、控件的访问【控件调整|静态文本控件|编辑框控件】、对话框伸缩功能、输入焦点的传递、默认按钮的说明


    🔳🔳 MFC对话框:逃跑按钮、属性表单、向导创建


    🔳🔳 在对话框程序中让对话框捕获WM_KEYDOWN消息


    🔳🔳修改应用程序窗口的外观【窗口光标|图标|背景】、模拟动画图标、工具栏编程、状态栏编程、进度栏编程、在状态栏上显示鼠标当前位置、启动画面


    🔳🔳设置对话框、颜色对话框、字体对话框、示例对话框、改变对话框和控件的背景及文本颜色、位图显示

    先学习简单绘图: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中画笔的颜色确定的,为了绘制其他颜色的线条就需要:

    1. 构造一个CPen对象,
    2. 为它指定一种颜色,
    3. 将此画笔选入设备描述表中。
    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中的画刷设置为透明的。

    1. 利用参数NULL BRUSH调用GetStockObject函数可以创建透明画刷,
    2. 然后调用CBrush类的静态成员函数FromHandle将画刷句柄转换为指向画刷对象的指针,但是该函数的参数需要的是HBRUSH类型,GetStockObject函数返回的是HGDIOBJ类型,因此需要进行强制转换,将其转换为画刷的句柄,即HBRUSH类型对象。
    3. 将创建的新画刷选入设备描述表中。
    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.h

    public:
    	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这个画刷来绘制背景的。因此我们看到子控件和对话框的背景都是粉色的。而对于OKCancel两个按钮的背景不改变的原因,稍后介绍。

    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按钮的文本颜色:

    1. 需要编写一个自己的按钮类,让这个类派生于CButton类
    2. 重写Drawltem函数,在此函数中实现按钮背景色和文本颜色的设置;
    3. 然后,将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函数中再重新贴上位图。
    • 窗口擦除时完成位图的显示
      窗口闪烁比较小。因为没有擦除背景,而是直接贴上位图。
    展开全文
  • Android对话框的使用总结

    千次阅读 多人点赞 2016-10-14 19:15:17
    本文对各类对话框的设计和使用都有...一个对话框一般是一个出现当前Activity之上的一个小窗口. 处于下面的Activity失去焦点, 对话框接受所有的用户交互. 对话框一般用于提示信息和与当前应用程序直接相关的小功能.

    一.相关概念



    一个对话框一般是一个出现在当前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();
    
        }


    运行后显示结果:

    Dialog1

    可以看到有一个标题的对话框,而且这里没有设置内容的属性,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();
    
        }

    程序运行后显示的结果:

    AlertDialog2

    这里如果点击取消,对话框消失,什么都没有做。
    如果点击退出,会直接退出程序。
    如果点击确定,会弹出一个土司,提示你。
    这里可以根据用户的选择做出相应的行为。


    (三)设计一个选择菜单样式的对话框

    代码设计:

        //数据源
        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方法来完成的。
    这里要可以设置两个监听事件,一个是单选按钮某一个选项被选中时触发的,另一个是点击按钮时触发的事件。
    程序中每次选中一个单选按钮都会弹出一个吐司,提示你选择的内容,点击确定后也会显示一个吐司。


    (五)多选项的对话框设计

    多选框的显示和单选的显示也是有点相似的,都是需要设置按钮来使对话框消失。
    代码如下:

    // 多选按钮的对话框
        // 对话框也是不会自动消失,需要自己关闭
        // 要设置按钮,点击按钮后对话框才会消失
        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的详细属性

    对话框的全部属性表现,如图所示:
    Dialog属性图


    这里分上、中、下三部分。
    区域一是标题相关
    区域二是主体相关
    区域三是按钮相关


    其中任何部分都可以不设置,
    但是有一点要注意的是主体内容只能有一种,比如:设置了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();
    
        }

    点击运行后,显示结果:

    日期对话框

    上面是日期对话框的默认画面,也是挺好看的,实际中也可以根据实际需要对它的页面样式进行修改。

    点击完成后显示的结果:

    日期结果


    总结:
    以上就是Android对话框大部分控件的基本使用。
    对于有些对话框选择之后对话框会自动消失,就没有必要设置ActionButton了
    但是有些对话框是不会自动消失的,就需要设置ActionButton或在页面布局中有可以关掉对话框的控件。
    对于有些界面没有设计得很好看,这个需要自己花时间去慢慢琢磨,这里只是介绍它们的用法和一些属性。还有它们的简单效果。

    展开全文
  • 对话框和常用控件

    千次阅读 2011-04-20 09:22:00
    对话框是Windows应用程序中最重要的用户界面元素之一,是与用户交互的重要手段,程 序运行过程中,对话框可用于扑捉用户的输入信息或数据。对话框是一个特殊类型的窗口,任何对窗口进行的操作(如:移动、...

    对话框是Windows应用程序中最重要的用户界面元素之一,是与用户交互的重要手段,在程 序运行过程中,对话框可用于扑捉用户的输入信息或数据。对话框是一个特殊类型的窗口,任何对窗口进行的操作(如:移动、最大化、最小化等)都可在对话框中 实施,一般来说,在对话框中通过各种控件(如:按钮、编辑框、列表框、组合框等)来和用户进行交互。控件是在系统内部定义的用于和用户交互的基本单元。

    一、对话框的使用

        Visual C++提供的对话框编辑器能“可视”地进行设计、编辑,并可用

    ClassWizard为对话框从CDialog基类中派生一个类,MFC的CDialog类封装了用于对话框的显示、关闭等操作的许多功能函数,例如:DoModal函数用来显示模式对话框并返回用户操作的结果。

    1、模式对话框(为186附加举例页)

        所谓模式对话框是指,当对话被弹出时,用户必须在对话框中进行相应的操作,在退出对话框之前,对话框所在的应用程序不能继续执行。平常我们所见到的对话框大多是模式对话框。

    例:模式对话框(通过菜单命令弹出)

    1)  建一个单文档(SDI)应用程序

    2)  创建对话框模板

    InsertàResourceà选中Dialogà单击New

    拖过一个静态文本,鼠标对准它,按右键点properties改标题为“新建模式对话框”。

    3)鼠标右键对准对话框的任何位置单击,选择properties选项,设置ID为IDD_MYDIALOG

    4)给对话框创建类

         双击新建对话框的任何位置,单击OK,写类名为“CMyDlg”,保

    证”CDialog”作为该类的基类。

    5)创建菜单命令:

    a)打开资源编辑器的菜单项Menu

    b)  双击IDR_MAINFRAME

    c)  双击右边空白菜单,点开pop_up(让它是空白),在名字处写”弹出对话框(&A)”,ID处写ID_PUPDIALOG

    6)将菜单命令连接到主框架程序中,完成ID_PUPDIALOG的消息映射:

        ViewàClassWizardà保证Class name里是CMainFrame,在ObjectIDs

    里找到ID_PUPDIALOG点黑àMessages里(右边)点COMMAND建立主框架对象方法并加代码:

    void CMainFrame::OnPupdialog()

    { CMyDlg  MyDlg;

    MyDlg.DoModal(); //DoModal()是CDialog类成员函数,通过调用该

    //函数将显示对话框。

    }               

      7)在CMainFrame.cpp文件里加:

              #include “MyDlg.h”   // 之后运行。

     2、无模式对话框(为186附加页)

    非模式对话框,弹出后,可一直保留在屏幕上,用户可继续在应用中进行其它操作或启动其它应用程序,当需要使用对话框时,只需象激活一般窗口一样激活对话框即可。

    1)建一个SDI(单文档)应用程序

    2)创建对话框模板

    InsertàResourceà点黑DialogàNew

    拖过一个静态文本,鼠标对准它,按右键点properties改标题为“新建非模式对话框”。

    3)为对话框创建类

    点出对话框(IDD_DIALOG1缺省的ID号),双击对话框中的任意位置,出现一个表,你点OKà出现一个对话框,你写类名:CDlg保证基类为CDialog

    4)创建菜单

    打开工作区àMenuàIDR_MAINFRAMEà双击空白菜单写“非模式对话框”关闭à再点下面空菜单写名字“显示非模式对话框”ID处写ID_DLG.

    5)添加菜单命令消息

     WiewàClassWizardàMessage Mapsà保证Class name里是Wiew类(视图类),在Object IDS里找到ID_DLG(菜单的ID)点黑右边COMMAND双击它àOK

    6)a、你在Wiew.h里加:#include “Dlg.h”

       b、在public:里加:CDlg *dlg;//创建CDlg对象

       c、在View.cpp的OnDlg()函数里加:

          CMyView::OnDlg()

           { 

              dlg=new CDlg(this);

              dlg->Create(IDD_DIALOG1);//使对话框摸板从资源中创建一个非

    //模式对话框

              dlg->ShowWindow(SW_RESTORE);//显示对话框

            }

    二、使用对话框编辑器(对话框编程)(187页)

    *1、可建一个单文档应用程序名为“对话框编程”,在此程序中加入一个对话框:

       InsertàResourceà点黑Dialogànew 出现188页的图

    2、拖入一些控件,对这些控件进行排序、布局、大小调整、上下对齐、测试等。*最后在对话框IDD_DIALOG1上留一个按钮控件,其标识符为:IDC_BUTTON1

    3、识别控件工具栏(188页)

    4、在加对话框时,InsertàResourceà点开+Dialog见有7类对话框,分别了解其

    不同用途(见192页)。

    5、对话框的属性

    A、ViewàProperties B、按Alt+Enter C、用鼠标右键单击对话框模板àProperties

    都能弹出对话框的属性框,见书193页对属性General的解释。

    *6、为对话框添加类(194页)

        对准对话框的任意非控件区域双击鼠标,将弹出书194页图5.11所示

    的对话框,询问是否为对话框资源创建一个新类à单击OKà弹出书194页

    图5.12所示的对话框à你定义一个新类的名字如:CMyDlg(注意:类名必

    须以C打头),下面的基类Base class和ID标识符Dialog ID内容一般不改。

    * 7、添映射消息(195页)

      接上,点OK出现“MFC ClassWizard”对话框,如书195页图5.13所示à(保证类名处是CMyDlg)选定点黑IDC_BUTTON1à单击BN_CLICKED 消息àAdd Functionà出现书195页图5.14对话框àOKàEdit Code

    *8、添加用户代码(195页)

    接上,到MyDlg.cpp文件中,写:

    void CMyDlg::OnButton1()

    {

       MessageBox(“欢迎进入对话框的设计!”);

    }//这时运行还不见对话框,接下

    *9、在程序中使用对话框(196页)

        由于对话框的代码是以类为模块来设计的,使用时需要在程序中加入该类

    的头文件,并定义一个类对象,然后就可以使用该类的相关成员。

    项目工作区àFileViewà打开应用程序的.cpp文件,在前面加:

      #include “MyDlg.h”

       在InitInstance函数体中的return TRUE语句之前添加下列代码:

         CMyDlg dlg;

         dlg.DoModal();//DoModal()函数是负责对话框的显示和终止。

    运行!则直接显示出对话框,单击按钮则出现“欢迎进入对话框的设计”

    的字样。

    本例建的CMyDlg类及以后在各个项目中建立的类,在文档、视图、主框

    架类中都可同样使用。

    (1)添加对话框资源

    对话框资源类型:

    IDD_DIALOGBAR 对话条,往往和工具条放在一起。

    IDD_FORMVIEW  一个表状风格的对话框,用于无模式对话框或视图类

    IDD_OLE_PROPPAGE_LARGE一个大的OLE属性页

    IDD_OLE_PROPPAGE_SMALL一个小的OLE属性页

    IDD_PROPPAGE_LARGE一个大属性页,用于属性对话框

    IDD_PROPPAGE_MEDIUM一个中等大小的属性页,用于属性对话框

    IDD_PROPPAGE_SMALL一个小的属性页,用于属性对话框

    (2)改变对话框的属性

    ID框:修改或选择对话框的标识符名称

    Caption框:输入对话框的标题名称,中英文均可。

    Font按钮:单击此按钮可选择字体的种类(如宋体)及尺寸(如9号)

    Xpos/Ypos:对话框左上角在父窗口中的X,Y坐标都为0时表示居中

    Menu框:默认值为无,当对话框需要选单时输入或选择指定的选单资源

    Class name:默认值为无,它提供C/C++语言编程时所需要的对话框类名,对

               MFC类库的资源文件来说,该项不被激活

    三、控件的创建和使用方法(197页)

       控件是在系统内部定义的能够完成特定功能的控制程序单元。在应用程序中使用控件不仅简化了编程,还能完成常用的各种功能。为了更好地发挥控件的作用,用户还必须理解和掌握控件的属性、消息以及创建和使用方法。

    注:控件工具栏及各按钮含义:

    1、控件的选择2、静态文本3、组框4、复选框5、组合框6、水平滚动条

    7、旋转按钮8、滑动条9、列表视图10、标签11、复合编辑12、月历

    13、用户定制工具14、静态图片15、编辑框16、按钮17、单选框18、列表框

    19、垂直滚动条20、进展条21、热键22、树形视图23、动画24、日期选择

    25、IP地址26、组合框的扩展(从左边往下数,再从右边往下数)

     

    1、控件的创建和使用方法(197页)

        控件的创建方法有2种:1)是在对话框摸板中用编辑器指定控件,也就是说,将控件的父窗口指定为对话框,如:上面的BUTTON1按钮控件。2)是将控件 看做任意一窗口的子窗口,并通过调用相应的Create函数来创建。下面我们用第2种方法创建控件(此种方法涉及的内容复杂,也不能发挥对话框编辑器可视 化编程的优点,故不提倡此种方法,而用第一种方法)。

    (1)打开上个项目,在CMyDlg类的头文件MyDlg.h里添加一个按钮类CButton

         指针变量:(public:里)

             CButton *m_btnWnd;

    (2)按Ctrl+W或ViewàClassWizard打开MFClassWizard对话框,并切换到

    Message Maps页面,在Object IDs列表中选定点黑CMyDlg项,并在

    Message列表中找到WM_INITDIALOG消息点黑àAddFunctionàEditCode

    (3)添加代码:

    BOOL CMyDlg::OnInitDialog()

    { -----

     m_btnWnd=new CButton();//构造按钮控件

    //下面“”按钮上的字,创建子窗口|窗口最初是可见的|创建的是按键按钮

     m_btnWnd->Create(“你好”,WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,

                CRect(20,20,120,60),this,201);//创建

     CFont *font=this->GetFont();//获取对话框的字体

     m_btnWnd->SetFont(font);//设置控件字体

     return TRUE;//(程序原有的)

    }

    代码中,Create用来创建一个按钮控件,该函数的第一个参数用来指定该按钮的标题,第二个参数用来指定控件的风格,第三个参数用来指定它在父窗口中的位置和大小,第四个参数用来指定父窗口指针,最后一个参数是指定该控件的标识值。

    WS_CHILD表示按钮是作为对话框的一个子窗口来创建的。

    WS_VISIBLE是使控件可见。

    BS_PUSHBUTTON表示创建的是按键按钮。

     (4)编译并运行

    2、控件的数据交换和数据效验(数据成员)(198页)

        使用ClassWizard可以很容易地定义一个控件的成员变量及其数据范围。例如:上面的CMyDlg类的按钮控件IDC_BUTTON1添加并使用其成员变量m_MyBtn,步骤如下:

    (1)打开上例项目,ViewàClassWizardàMember Variablesà选定Class name

    中为CMyDlg,然后在Control IDs列表中à点黑IDC_BUTTON1àAddVariable

    (或双击鼠标左键),弹出Add Member Variable对话框,如书198页图5.17

    à写好数据成员名:m_MyBtn  下面Category和Variables type里的不动

    àOKà见表上已建成(如:书199页图5.18)

    下面是建成员变量的三个对话框图:

    (2)再向对话框加一个编辑控件,并加成员变量m_strEdit,类型为CString置

    数值为Value(注:Category框内可选择Value或Control两种类型。 Control所对应的变量类型就是MFC为该控件封装的控件类。Value所对应的是数值类型。不同控件所提供的关联的数值类型各不同,例如:对于编辑 框来说,Variables type中的数值类型有CString (字符串),int,UINT(32位无符号整数),long(32位带符号整数), DWORD(32位无符号整数,段地址和相关的偏移),float,double,BYTE(8位无符号整数),short,BOOL等)。OK后在下面 写20为最大值。(下面见书199—200页)

    打开本项目的MyDlg.h见到:

       CButton m_MyBtn;

       CString m_strEdit;

    在MyDlg.cpp里见到:

       m_strEdit=_T(“ “);

    在DoDataExchange函数体内见:

      DDX_Control(pDX,IDC_BUTTON1,m_MyBtn);

        DDX_Text(pDX,IDC_EDIT1,m_strEdit);//IDC_EDIT1是标识m_strEdit

    //是成员变量

    DDV_MaxChars(pDX,m_strEdit,20);//校验m_strEdit的最大字符个数不超过20。

     (3)将CMyDlg::OnButton1()修改成:

          void CMyDlg::OnButton1()

    {

       UpdateData();//默认参数值是真TRUE

       m_MyBtn.SetWindowText(m_strEdit);

       //GetDlgItem(IDC_BUTTON1)->SetWindowText(“欢迎”);//用此条代替

    //上条也行,

                                  //单击Button1按钮,此按钮名就是“欢迎”。

    }

    之后运行该程序,当在编辑框输入Hello后,单击Button1按钮,则该按

    钮的名称就变成了编辑框中的内容Hello了,见书200页。

    八、控件的通用属性 (201页)

            在控件的属性对话框中含有许多属性,如:书201页(按钮的属性对话

    框),General(一般属性)、Styles(控件的风格)、Extended Styles(控件的扩

    展风格)。Styles和Extended Styles是用来设定控件的外观的、辅助功能的。

    不同控件具有不同的风格和扩展风格。见201页表5.5.

    控件的General属性:

    ID:控件的标识符,每种控件都有默认的ID,例如按钮控件为IDC_BUTTON1

    Caption:控件的标题,大多数控件都有默认的标题,例如按钮控件为Button1

    Visible:指定控件初始化时是否可见

    Group:指定控件组中的第一个控件,如果该项被选中,则此控件后的所有控件均被看成一组,成组的目的是可以让用户键盘方向键在同一组控件中进行切换

    Help ID:若该项被选中,则为该控件建立一个上下文相关的帮助标识符

    Disabled:指定控件初始化时是否禁用

    TabStop:若该项被选中,则用户可以使用Tab键来选择控件

    九、控件的消息

         对于每个消息,系统都会用一个MSG结构来记录(见201页)。对一般控件来说,其通知消息是一条WM_COMMAND消息(见202页)。例:

    1、打开上面的项目“对话框”

    2、ViewàClassWizardà在CMyDlg里(左边的Object IDs里点黑CMyDlg)

       à在Message里找到OnCommand点黑àAdd FunctionàEdit Codeà写:

     BOOL CMyDlg::OnCommand(WPARAM wParam,LPARAM lParam)

       {

           WORD nCode=HIWORD(wParam);//控件的通知消息

              WORD nID=LOWORD(wParam);//控件的ID号

            if((nID==201)&&(nCode==BN_CLICKED))//用户单击按钮产生的消息

                 MessageBox("你按下了/"你好/"按钮!");

            ----

       }//WORD是16位无符号整数

        //EN_CHANGE是编辑框中的文本被改变时发出的消息

    BN_CLICKED是当用户单击按钮产生的消息:

    单击对话框中的“你好”按钮,弹出"你按下了/"你好/"按钮!"的消息对话框。由于 Create创建的控件无法用ClassWizard直接映射其消息,因此上述方法祢补了ClassWizard的不足,使用时要特别注意。见书202页 通知消息是所有Windows控件所共有的:

    NM_CLICK    在控件中单击鼠标左键按钮

    NM_DBLCLK  在控件中双击鼠标左键按钮

    NM_RDBLCLK在控件中双击鼠标右键按钮

    NM_RETURN当控件具有输入焦点时按下ENTER键

    NM_SETFOCUS控件得到输入焦点

    NM_KILLFOCUS控件失去输入焦点

    NM_OUTOFMEMORY没有足够的内存使控件

    十、常用控件

    静态控件 CStatic  用于向用户显示一些几乎固定不变的文字或图形描述

    按    钮 CButton 用于产生某些命令或改变某些选项设置

    编辑框   CEdit  可完成文字的输入、输出双向操作,使用户能查看并编辑文字

    列表框   CListBox显示一个列表,让用户从中选取一个或多个项

    组合框   CComboBox它把列表框和编辑框有机地组合在一起,用户不仅能选择

    列表中已有的项,还能编辑出新的项

    滚动条   CScrollBar通过滚动块在滚动条上的移动来改变某些数值

    进展条   CProgressCtrl 用于指示一个操作的进度

    旋转按钮 CSpinButtonCtrl 又称“上下控制”,是一对箭头按钮,用户单击它们

    可以增加或减小某个值

    滚动条   CSliderCtrl 是一个包含一个滑动块和可选的刻度线,用户可以用鼠标

    或方向键沿某个方向移动滑动块

    图象列表 CImageList是一系列相同大小的图象的集合

    标签控件 CTabCtrl类似于一个笔记本的分割器或一个文件柜上的标签,使用它

    可以将一个窗口或对话框的相同区域定义为多个页面

    1、静态控件

        静态控件是用于显示一个字符、框、矩形、图标、位图或增强的图元文件,它可以用做标签、框或用于分隔其它的控件。一个静态控件一般不接受用户输入,也不产生通知消息。

    在对话框编辑器的控件工具栏中,属于静态控件的有:静态文本(Static Text)、

    组框(Group Box)、图片控件(Picture)三种。其中,静态图片控件的属性对话框如:书204页图5.21所示,表5.7列出了其一般属性和风格的各个项的意 义。我们可以选择Type(图片类型)、Image(图象资源)两个组合框中的有关选项内容,并可将应用程序资源中的图标、位图等内容显示在该静态图片控 件中。另外,用户还可设置其风格来改变控件的外观以及图象在控件的位置等。

    静态图片控件的General和Style属性对话框:

    Type     图片类型,用户可以从中选择Frame(框)、Rectangle(矩形区域)、Icon(图标)、Bitmap(位图)、Enhanced Metafile(增强图元文件,它是各种绘图命令的集合)

    Image 当图片类型为Icon或Bitmap时,通过此框可选择指定的资源ID号

          设置Frame和Rectangle的颜色,它可以是black(黑色)、white(白色)、

          gray(灰色)、或者是具有3D外观的etched(腐蚀色)

    Sunken        选中时,在控件的周围有下沉的边框

    Notify         选中时,当用户单击或双击图片时会向其父窗口发出通知消息

    Right justify     选中时,用户重置图片大小,图片右下角是固定不变的

    Border          选中时,图片周围有边框

    Center image    选中时,图片显示在控件中央,其余区域由图片左上角的象素

    颜色来填充

    Real size image选中时,按图片的原始大小来显示,超过控件区域的部分被裁剪

       在它的属性中,用户可以选择Type(图片)、Image(图象资源)两个组合框中的有关选项内容,并可将应用程序资源中的图标、位图等内容显示在该静态图片控件中,另外,用户还可以设置其风格来改变控件的外观以及图象在控件的位置等。

    例1:图片控件(将一个.bmp图形显示在图片控件上)(附加举例)

    1)建一个单文档(SDI)应用程序

    2)创建对话框模板:  InsertàResourceà点黑Dialogànew

    3)将图片控件Picture拖到对话框上,并拉大些,将OK和CANCEL拖到下面。

    4)向项目中插入一个 .bmp图片:InsertàResourceà点黑BitmapàImportà在

    出现的表中要下拉出:所有文件(*.*)在某处找到一个.bmp图形àImport放到此项目中。

    5)将这个图片放到图片控件上

         右键对准图片控件单击à出现属性框àType处下拉置Bitmapàimage处下拉置IDB_BITMAP1,就将图片加到了图片控件上。

    6)为刚才建的对话框添加类

         双击新建对话框的任何位置àOKà类名写:CMyDlg

    7)创建一个菜单项,用来显示对话框

       ResourceViewàMenuàIDR_MAINFRAMEà双击空白菜单à点POPUP

    ID处写:ID_DLG 菜单名写:显示图片对话框à关闭

    8)将菜单命令映射到View中去 

         ViewàClassWizardà要加到View中àID-DLGàCOMMANDàEditCode

      (在View.h的头部加:#include “MyDlg.h” 在public:下加:CMyDlg *dlg;)并

    加代码:

            CMyView::OnDlg()

    {  dlg=new CMyDlg(this);

                  dlg->Create(IDD_DIALOG1);

                  dlg->ShowWindow(SW_RESTORE);

            }

    9)编译运行

    2:按钮

    常见的按钮有三种类型:(204页)

    (1)按键按钮

         按键按钮通常可以立即产生某个动作,执行某个命令,因此也常被称为命

    令按钮。

    (2)单选按钮

         其外形是在文本前有一个圆圈,当它被选中时,就标上一个黑点。

    (3)复选框

    其外形是在文本前有一个空心方框,当它被选中时,就加上一个“∨”标记

    1)按钮的消息

       常见的按钮映射消息有两个:

    (1)    BN_CLICKED(单击按钮)(2)BN_DOUBLE_CLICKED(双击按钮)

    见下图:

    2)按钮选中操作

      最常用的按钮操作是设置或获取一个按钮或多个按钮的选中状态。

    CButton类的以下2个成员函数原型如下:

      void SetCheck(int nCheck); //设置指定按钮的选中状态

      int GetCheck()const;      //获取指定按钮的选中状态

      其中:nCheck和GetCheck函数返回的值可以是:0表示不选中,1表示选中,

      2表示不确定(仅用于三态按钮)

      而对于多个单选按钮的选中状态的设置或获取,需要使用CWnd类的成员函数CheckRadioButton和GetCheckedRadioButton,它们的原型如下:

    void CheckRadioButton(int nIDFirstButton,int nIDLastButton,int nIDCheckButton);

    int GetCheckedRadioButton(int nIDFirstButton,int nIDLastButton);

    其中,nIDFirstButton和nIDLastButton分别指定这组单选按钮的第一个和最后一个按钮ID值,nIDCheckButton用于指定要设置选中状态的按钮ID值,函数GetCheckedRadioButton返回被选中的按钮ID值。

    例2:用静态图片、单选按钮、复选框和按键按钮控件设计界面,运行结果如:书205页图 5.23所示。刚开始,所有单选按钮都是灰显的,我们不能选择它们,这种情况称为“禁用”,而当选中“允许”复选框后,所有单选按钮可以使用,用户选定一 个单选框后,单击[应用]按钮,则弹出相应的消息对话框。

    (1)用MFC AppWizard(exe)创建一个名为:“按钮的使用”的基于对话框的应用

    程序。(第一步将类型选择为Dialog Based,然后按[Finish]按钮即可。)

    (2)打开属性对话框,将其标题改为“使用Windows常用控件”。

    (3)参看书205页图5.23的控件布局,用编辑器为对话框添加如书206页表5.8

    所示的一些控件。

    (4)说明:上面4个单选按钮的Tab次序应连续,而且从IDC_RADIO1到IDC_RADIO4依次增加。

    (5)ViewàClassWizard(或按Ctrl+W快捷键)à切换到Member Variables页面,在Class name中选择CMyDlg,在Control IDs里选中点黑IDC_CHECK1复选框ID号àAdd Variables按钮,为其添加一个BOOL类型的成员变量

         m_bEnabledàOK。

    (6)切换到ClassWizard的Message Maps页面,分别选中点黑复选框IDC_CHECK1和按钮IDC_BUTTON1,分别为其添加映射消息,并添如下代码:

          void CMyDlg::OnCheck1()

           {

             UpdateData();

             for(int i=0;i<4;i++)

             GetDlgItem(IDC_RADIO1+i)->EnableWindow(m_bEnabled);

    }//EnableWindow是使一个控件窗口禁用或允许使用,它取决于该函数

    //的参数,为TRUE时表示可以使用,否则禁用。

    void CMyDlg::OnButton1()

    {

      UpdateData();

      if(!m_bEnabled) return;

      int nID=GetCheckedRadioButton(IDC_RADIO1,IDC_RADIO4);

      if(nID==IDC_RADIO1)

       { MessageBox(“1”); }

        if(nID==IDC_RADIO2)

        { MessageBox(“2”); }

    if(nID==IDC_RADIO3)

        { MessageBox(“3”); }

    if(nID==IDC_RADIO4)

        { MessageBox(“4”); }

    (7)在此文件中找到CMyDlg::OnInitDialog函数体,添加下列代码:

         BOOL  CMyDlg::OnInitDialog()

          {

             CheckRadioButton(IDC_RADIO1,IDC_RADIO4,IDC_RADIO1);

                         //设置第一个单选按钮为选中

             OnCheck1();

             return TRUE;//此条是原有的

    (8)编译运行

    3、编辑框

       编辑框是一个让用户从键盘输入和编辑文本的矩形窗口,用户可以通过它,很方便地输入各种文本、数字或口令,也可使用它来编辑和修改简单的文本内容。

       当编辑框被激活且具有输入焦点时,就会出现一个闪动的插入符(又可称为文本光标),表明当前插入点的位置。

      1)编辑框的属性和风格

      

    Align text 各行文本对齐方式:Left,Center,Right,默认时为Left

    Multiline  选中时为多行编辑框,否则为单行编辑框

    Number   选中时控件只能输入数字

    Horizontal scroll 水平滚动,仅对多行编辑框有效

    Auto HScroll当用户在行尾键入一个字符时,文本自动向右滚动

    Vertical scroll 垂直滚动,仅对多行编辑框有效

    Auto VScroll 当用户在最后一行按ENTER键时,文本自动向上滚动一页,仅对

    多行编辑框有效

    Password      选中时,键入编辑框的字符都将显示为”*”,用于口令设置,仅对

    单行编辑框有效

    No hide selection通常情况下,当编辑框失去键盘焦点时,被选择的文本仍然反

    色显示,选中时,则不必具备此功能

    OEM convert   选中时,实现对特定字符集的字符转换

    Want return     选中时,用户按下ENTER键,编辑框中就会插入一个回车符

    Border         选中时,在控件的周围存在边框

    Uppercase      选中时,键入在编辑框的字符全部转换成大写形式

    Lowercase      选中时,键入在编辑框的字符全部转换成小写形式

    Read-Only      选中时,防止用户键入或编辑文本

    多行编辑框具有简单文本编辑器的常用功能,例如:它可以有滚动条,用户按Enter键另起一行以及文本的选定、复制、粘贴等常见操作。而单行编辑框功能较简单,它仅用于单行文本的显示和操作

      2)编辑框的基本操作

    A、设置口令

       口令设置在编辑框中不同于一般的文本编辑框,用户输入的每个字符都被一个特殊的字符代替显示。这个特殊的字符称为口令字符。默认的口令字符是”*”。

    应用程序可以用成员函数CEdit::SetPasswordChar 来定义自己的口令字符,其函数原形如下:

        void SetPasswordChar(TCHAR ch);

    其中,参数ch表示设定的口令字符;当ch=0时,编辑框内将显示实际字符。

    B、选择文本

    编程选择文本,调用成员函数CEdit::SetSel来实现,还有:

    CEdit::GetSel和CEdit::ReplaceSel,它们分别用来获取编辑框中选择的开始和结束的位置以及替换被选择的文本。

    C、设置编辑框的页面边距

    用CEdit::SetMargins函数来实现,其函数原型如下:

    void SetMargins(UINT nLeft,UINT nRight);

      其中,参数nLeft和nRight分别用来指定左、右边距的象素大小。

    D、剪帖板操作

    E、获取多行编辑框文本(见书208页---209页)。

    如何设置口令(密码):附加例题见后面(教案页)

    3)编辑框的通知消息

    当编辑框的文本修改或者被滚动时,会向其父窗口发送一些消息,这些消息

    是:(参看书209页表5.10并由下图所示:)

    EN_CHANGE当编辑框中的文本已被修改,在新的文本显示之后发送此消息

    EN_HSCROLL当编辑框的水平滚动条被使用,在更新显示之前发送此消息

    EN_KILLFOCUS编辑框失去键盘输入焦点时发送此消息

    EN_MAXTEXT文本数目到达了限定值时发送此消息

    EN_SETFOCUS编辑框得到键盘输入焦点时发送此消息

    EN_UPDATE编辑框中的文本已被修改,新的文本显示之前发送此消息

    EN_VSCROLL当编辑框的垂直滚动条被使用,在更新显示之前发送此消息

     例:使用静态文本、组框、编辑框以及按钮等控件设计界面,运行结果参书209

    页图5.25即下图所示。当用户在“成绩1”、“成绩2”、和“成绩3”编辑框中输

    入成绩后,单击[计算平均分]按钮,将显示出这三个成绩的平均分。

     程序设计步骤如下:

    (1)    打开前面的项目“按钮的使用”。

    (2)    向应用程序中添加一个对话框资源,insertàResourceà点黑Dialogànew

    à出现一个新的对话框à右键单击这个新对话框àpropertiesà打开其属

    性对话框àFontà将其字体设置为“新宋体,9”,标题改为“使用编辑框”,

    ID号改为IDD_EDIT,删除默认的Cancel按钮。

    (3)、(4)、(5)按书210页往下作

    书(6)m_strAve="0.00";

              UpdateData(FALSE);//将成员变量数据传给控件,并在控件中显示

    书(7)UpdateData();//将控件显示的数据传给成员变量

              double ave=(double)(m_nScore1+m_nScore2+m_nScore3)/3.0;

              m_strAve.Format("%6.2f",ave);//Format是CString类的一个经常使用的成

    //员函数,它通过格式操作使任意类型的数据转换成一个字符串

              UpdateData(FALSE);//将成员变量数据传给控件,并在控件中显示

    书(8)定位到void CMyDlg::OnButton1()

           { ---------//注意,这是你上个程序的按钮命令,在里找到:

             if(nID= =IDC_RADIO1)

             {  CEditDlg dlg;     //注意,将原来MessageBox("1");去掉

                dlg.DoModal();   //加上这2条

             }

    书(9)在上个程序的:按钮的使用Dlg.cpp即CMyDlg.cpp的头文件处加:

           #include "EditDlg.h"(就是(8)所在的文件)

        编译运行

    4、列表框

    列表框是一个列有许多项目让用户选择的控件。它与单选按钮组或复选框组一样,都可让用户在其 中选择一个或多个项。但不同的是,列表框中项的数目是可灵活变化的,程序运行时可往列表框中添加或删除某些项。并且,当列表框中项的数目较多而不能一次全 部显示时,还可以自动提供滚动条来让用户浏览其余的列表项。

    1)列表框的风格

    按性质来分,列表框有单选、多选、扩展多选以及非选四种类型,默认风格

    下的单选列表框让用户一次只能选择一个项,多列表框可让用户一次选择几个项,而扩展多项列表 框允许用户用鼠标拖动或其它特殊组合键进行选择,非选列表框不提供选择功能。还有其它一系列风格,用于定义列表框的外观及操作方式,这些风格可在下图所示 的列表框属性对话框中设置:列表框的Styl属性:

    Selection指定列表框的类型:单选(Single)、多选(Multiple)、扩展多选(Extended)、不选(None)

    Owner draw自画列表框,默认为No

    Has strings选中时,在自画列表框的项目中含有字符串文本

    Border选中时,使列表框含有边框

    Sort选中时,列表框的项目按字母顺序排列

    Notify选中时,当用户对列表框操作就会向父窗口发送通知消息

    Multi-column选中时,指定一个具有水平滚动的多列列表框

    Horizontal scroll选中时,在列表框中创建一个水平滚动条

    Vertical scroll选中时,在列表框中创建一个垂直滚动条

    No redraw选中时,列表框发生变化后不会自动重画

    Use tabstops选中时,允许使用停止位来调整列表项的水平位置

    Want key input选中此项,当用户按键且列表框有输入焦点时,就会向列表框的父窗口发送

    相应消息

    Disable no scroll选中时,即使列表框的列表项能全部显示,垂直滚动条也会显示,但此时

    是禁用的(灰显)

    No integral height选中时,在创建列表框的过程中,系统会把用户指定的尺寸完全作为列表

    框的尺寸,而不论是否有项目在列表框,也不能完全显示出来

    2)  列表框的基本操作

    当列表框创建之后,往往要添加、删除、改变或获取列表框中的列表项,这些操作都可以调用MFC的CListBox类成员函数加以实现。

    索引:表明项目在列表框中排列的位置,它是以0为基数的,即列表框中第一项的索引是0,第二项的索引是1,依次类推。

    (1)添加列表项

    列表框创建时是一个空的列表,需要用户添加或插入一些列表项,其函数原型为:

    int AddString(LPCTSTR lpszItem);         

    int InsertString(int nIndex,LPCTSTR lpszItem);

     其中:列表项的字符串文本由参数pszItem来指定,成功返回列表在列表框的索引,错

    误返回LB_ERR,空间不够返回LB_ERRSPACE。

    但:InsertString函数不会将列表项进行排序,不论列表框控件是否具有sort属性,只是将列表项插在指定索引的列表项之前,若nIndex等于-1,则列表项添加在列表框末尾。

    而:AddString函数在当列表框控件具有sort属性时会自动将添加的列表项进行排序。

    以上2个函数只能将字符串增加到列表框中,但有时用户还会需要根据列表项使用其他数据。这时,ListBox的SetItemData和SetItemDataPtr能有效解决这个问题,它们能使用户数据和某个列表项关联起来:

    int SetItemData(int nIndex,DWORD dwItemData);

    int SetItemDataPtr(int nIndex,void *pData);

    其中,SetItemData是将一个32位数与某列表项(由nIndex指定)关联起来,而

          SetItemDataPtr可以将用户的数组、结构体等大量的数据与列表项关联

          若产生错误,它们都返回LB_ERR

    而:GetItemData和GetItemDataPar分别用来获取相关联的用户数据。

     

    以下等待修改

    (2)删除列表项(3)查找列表项(4)列表框的单项选择

    (5)列表框的多项选择

    3)列表框的通知消息

        当列表框中发生了某个动作,如用户双击选择了列表框中某仪项时,列表框就会向父窗口发送一条通知消息。常用的通知消息如书214页表5.14所示。

    例:将一个SCORE结构(含有三门成绩的数据成员)数据和列表中每一个学生

    姓名列表关联起来。当用户单击[添加记录]按钮时,学生成绩记录中的“姓

    名“被添加在列表框中,且该学生的成绩与该列表项关联。当用户单击[删

    除记录]按钮时,列表框中当前选择项被删除,相关联的数据所占的内存空

    间被释放。任何时候选中列表框中某个学生,相应的记录数据被显示出来,

    如:书214页图5.27所示。

    (1)    打开前面的基于对话框的项目“按钮的使用”。

    (2)    向项目中添加一个对话框资源IDD_LISTBOX,标题为“使用列表框”,并用ClassWizard为此对话框建类为:CListBoxDlg。

    接着按书214页往下作:(3)、(4)-------

    (可在此处将第9步、第11步作出…………. 因为第5步要用到SCORE结构体 )

    书(5)

    UpdateData(TRUE);

           if(m_strName.IsEmpty())//判断m_strName是否为空

           { MessageBox("姓名不能为空!");

             return;

           }

           m_strName.TrimLeft();//裁剪m_strName左边的空格

           m_strName.TrimRight();//裁剪m_strName右边的空格

           if((m_List.FindString(-1,m_strName))!=LB_ERR)

           {  MessageBox("列表框中已有相同姓名,不能添加!");

              return;

           }

           int nIndex=m_List.AddString(m_strName);//向列表框添加学生

                             //姓名将该学生成绩与新增的列表项关联起来

           SCORE data;

           data.score1=m_nScore1;

           data.score2=m_nScore2;

           data.score3=m_nScore3;

           m_List.SetItemDataPtr(nIndex,new SCORE(data));

    书(6)

           int nIndex=m_List.GetCurSel();//获得当前选项的索引

           if(nIndex!=LB_ERR)

           {  m_List.DeleteString(nIndex);//删除当前选择项

              m_strName.Empty();

              m_nScore1=m_nScore2=m_nScore3=0;

              UpdateData(FALSE);

           }

           else MessageBox("当前没有选择项或列表框操作失败!");

    书(7)

    int nIndex=m_List.GetCurSel();

           if(nIndex!=LB_ERR)

           {  m_List.GetText(nIndex,m_strName);

              SCORE *data=(SCORE *)m_List.GetItemDataPtr(nIndex);

              m_nScore1=data->score1;

           m_nScore2=data->score2;

           m_nScore3=data->score3;

              UpdateData(FALSE);

           }

    书(8)

    for(int nIndex=m_List.GetCount()-1;nIndex>=0;nIndex--)

           { //删除所有与列表相关联的SCORE结构数据,并释放内存

                  delete(SCORE *)m_List.GetItemDataPtr(nIndex);

           }

    CDialog::OnDestroy();//关闭对话框

    说明:对话框被清除时发送WM_DESTROY消息。用户在此消息的映射函数中添加一些对象删除代码,以便在对话框清除前有效地释放内存空间。

    书(9)

    struct SCORE

       {  int score1;

          int score2;

             int score3;

       };

    书(10)定位到void CMyDlg::OnButton1()

           { ---------//注意,这是你上个程序的按钮命令,在里找到:

             if(nID= =IDC_RADIO2)

             {  CListBoxDlg dlg;     //注意,将原来MessageBox("2");去掉

                dlg.DoModal();   //加上这2条

             }

    }

    书(11)在上个程序的:按钮的使用Dlg.cpp即CMyDlg.cpp的头文件处加:

           #include "ListBoxDlg.h"

    //(就是(10)所在的文件)

        编译运行

    注意:第4步完后,可以先将第9步和第11步作出,以能点出相应函数。

    5、组合框

          列表框中不能输入列表项之外的内容,而编辑框也没有列表框的选择操

    作。于是就把常用的项,列在列表框中以供选择,而同时提供编辑框,允许

    用户输入列表框中所没有的新项,这就是组合框控件。它结合列表框和编辑

    框的特点,取二者之长,从而完成较为复杂的输入功能。

    1)  组合框的风格及类型

    按照组合框的主要风格特征,可分为三类:简单组合框、下拉式组合框、

    下拉式列表框。简单组合框和下拉式组合框都包含列表框和编辑框,但是简单组合框中的列表框不需要下拉,是直接显示出来的,而当用户单击下拉式组合框中的下拉按钮时,下拉的列表框才被显示出来。下拉式列表框虽然具有下拉式的列表,却没有文字编辑功能。

    组合框的一些其它风格,见书217页图5.28所示的组合框的属性对话框

    中的设置。其各项含义见书218页表5.17所示。

    2)  组合框常见操作

       组合框的操作大致分为两类,一类是对组合框中的列表框进行操作,

    另一类是对组合框中的编辑框进行操作。这些操作都可以调用CComboBox

    成员函数来实现,见书218页表5.18所示。由于组合框的一些编辑操作与

    编辑框CEdit的成员函数相似,如:GetEditSet,SetEditSel等,因此这些

    成员函数没有在上述表中列出。

    3)  组合框的通知消息

       在组合框的通知消息中,有的是操作列表列表框发出的,有的是操作

    编辑框发出的,如:书219页表5.19所示。

    例:根据用户从组合框中选择的填充样式,在对话框中绘制一个矩形区域,      如:书220页图5.29所示。由于对话框是一个窗口,所以用户可以调用GDI绘图函数在对话框内的任何部分进行绘图,至于绘图时需要的一些技巧见书 220页的说明:(1)、(2)、(3)。本例操作步骤如下:

    (1)打开前面创建的基于对话框应用程序“按钮的使用”。

    (2)向应用程序中添加一个对话框资源IDD_COMBO,标题为“使用组合框”,如:书220页图5.30所示。并用ClassWizard为新加的对话框定义类,名为:CComboDlg。

    (3)、(4)、(5)按书220--221页整个操作步骤做完。

    书(6)

    int nIndex=m_Pattern.GetCurSel();//获得当前选项的索引

              if(nIndex!=CB_ERR)

              {  m_nDrawPattern=m_Pattern.GetItemData(nIndex);//获得与当

                                                    //前选项相关联的数据

                 Invalidate();//强制系统调用OnPaint函数重新绘制

              }

    书(7)

    CWnd *pWnd=GetDlgItem(IDC_DRAW);//获得控件IDC_DRAW的窗口指针

              pWnd->UpdateWindow();//避免系统自动重绘

              CDC *pDC=pWnd->GetDC();//获得所需要的绘图设备环境

              CBrush drawBrush;//定义一个画刷

              drawBrush.CreateHatchBrush(m_nDrawPattern,RGB(0,0,0));//创建画刷

              CBrush *pOldBrush=pDC->SelectObject(&drawBrush);//将画刷选入当前设

    //备环境中

              CRect rcClient;//定义一个CRect变量

              pWnd->GetClientRect(rcClient);//获得窗口客户区大小

              pDC->Rectangle(rcClient);//用当前画刷绘制一个矩形区域

              pDC->SelectObject(pOldBrush);//恢复设备环境原来的画刷设置

    书(8)

    CDialog::OnInitDialog();

              CString str[6]={"水平线","竖直线","向下斜线","向上斜线",

                  "十字线","交叉线"};

              int nIndex;

              for(int i=0;i<6;i++)

              {  nIndex=m_Pattern.AddString(str[i]);

                 m_Pattern.SetItemData(nIndex,i);

              }

              m_Pattern.SetCurSel(0);

              m_nDrawPattern=0;

    书(9)

    CComboDlg dlg;

       dlg.DoModal();

    书(10)#include “ComboDlg.h”

    -------------------------

    6、滚动条和进展条

        进展条通常用来说明操作的进度,并在操作完成时从左到右充填进展条,

    这个过程可以让用户看到还有多少任务要完成。滚动条也是一种控件,它能

    完成诸如定位之类的操作。

    1)  滚动条

    滚动条是一个独立的窗口,虽然它具有直接的输入焦点,但却不能自动地滚动窗口的内容,因此,它的使用受到一定的限制。根据滚动条的走向,可分为垂直滚动条和水平滚动条两种类型。

    (1)滚动条的基本操作:滚动条的基本操作一般包括设置和获取滚动条的范围及滚动块的相应位置。由于滚动条控件的默认滚动范围是0---0,因此如果不设置滚动条范围,那麽滚动条中的滚动块就滚动不起来。

    在MFC的CScrollBar类中,函数SetScrollRange是用来设置滚动条范围的,其原型如下:

       SetScrollRange(int nMinPos,int nMaxPos,BOOL bRedraw=TRUE);

    其中:nMinPos和nMaxPos表示滚动位置的最小值和最大值。bRedraw

    为重画标志,当为TRUE时,滚动条被重画。在CScrollBar类中,设置滚

    动块位置操作是由SetScrollPos函数来完成的,其原型如下:

      int SetScrollPos(int nPos,BOOL bRedraw=TRUE);

    其中:nPos表示滚动块的新位置,它必须是在滚动范围之内。获取滚动条

    的当前范围以及当前滚动位置的两个函数原型是:

         void GetScrollRange(LPINT lpMinPos,LPINT lpMaxPos);

         int GetScrollPos();

    需要说明的是:在CScorllBar类的成员函数中,还可以用SetScrollInfo

    和GetScrollInfo来代替上面提到的4个函数。与前面的函数相比,使用

    SetScrollInfo函数还能使滚动块的大小随内容的多少而改变,并且这两个函

    数都使用下面的SCROLLINFO结构:见书223页这个结构体的描述:

    (2)当用户对滚动条进行操作时,滚动条就会向父窗口发送WM_HSCROLL(水平滚动 条)或WM_VSCROLL(垂直滚动条)消息。这些消息是通过ClassWziard在其对话框(滚动条的父窗口)中进行映射的,并产生相应的消息映射 函数OnHScroll和OnVScroll,这两个函数原型如下:

    afx_msg void OnHScroll(UINT nSBCode,UINT nPos,CScrollBar *pScrollBar);

    afx_msg void OnVScroll(UINT nSBCode,UINT nPos,CScrollBar *pScrollBar);

    其中:nPos表示滚动块的当前位置,pScrollBar表示由滚动条控件的指针,

    nSBCode表示滚动条的通知消息。见书223页表5.21所示。

    2)  进展条

    进展条是一个如书224页图5.33所示的控件。除了能表示一个过程的进展情况外,使用进展条还可以表明温度、水平面或类似的测量值。进展条的设置范围、当前位置、设置增量等,都是通过:CProgressCtrl类的成员函数实现的。

      int SetPos(int nPos);

      int GetPos();

    这两个函数分别用来设置和获取进展条的当前位置,需要说明的是,这个当前位置是指在SetRange中的上限和下限范围之间的位置。

     voit SetRange(short nLower,short nUpper);

     void SetRange32(int nLower,int nUpper);

    void GetRange(int &nLower,int &nUpper);

    它们分别用于设置和获取进展条范围的上限和下限值,一旦设置后,还会重画

    此进展条来反映新的范围,成员函数SetRange32为进展条设置32位的范围。

    参数:nLower和nUpper分别表示范围的下限(默认值为0)和上限(默认值

    为100)。

       int SetStep(int nStep);

    该函数用于设置进展条的步长并返回原来的步长,默认步长为10。

       int SetpIt();

    该函数将当前位置向前移动一个步长并重画进展条,以反映新的位置,函数

    返回进展条上一次的位置。

    7、  旋转按钮控件和滑动条

    “旋转按钮控件”(也称为上下控件)是一对箭头按钮,用户单击它们来增加

    或减小某个值,比如一个滚动位置或显示在相应控件中的一个数字。

    一个“滑动条控件”(也称为跟踪器)是一个包含一个滑动块和可选的刻度线的窗口。这两个控件的说明见书225页。

    1)旋转按钮控件

    (1)旋转按钮控件常用的风格

    见书225页图5.36所示,其中各项含义见书225页表5.22所示

    (2)旋转按钮控件的基本操作

    MFC的CSpinButtonCtrl类提供了旋转按钮控件的各种操作函数,使用

    它们用户可以进行基数、范围、位置设置和获取等基本操作。成员函数:

    SetBase是用于设置基数的,这个基数值决定了伙伴窗口显示的数字是十进

    制的还是十六进制的。函数原型如下:

    int SetBase(int nBase);// 是用于设置基数的

    其中参数nBase表示控件的新的基数。与此函数相对应的成员函数GetBase

    是获取旋转按钮控件的基数。

    int SetPos(int nPos);//设置旋转按钮控件的当前位置

    int SetRange(int nLower,int nUpper);// 设置旋转按钮控件的当前范围。

    参数:nPos表示控件的新位置,它必须在控件的上限和下限指定的范围之内。

          nLower和nUpper表示控件的上限和下限,任何一个界限值都不能大于Ox7fff或小于-Ox7fff.

    函数GetPos()和GetRange()分别用于获取旋转按钮控件的当前位置和范围成员函数:SetAccel()和GetAccel()分别用于设置和获取旋转按钮控件的加速度。其中函数SetAccel的原型如下:

         BOOL SetAccel(int nAccel,UDACCEL *pAccel);

    参数:nAccel表示由pAccel指定的UDACCEL结构的数目,pAccel指向一

    个UDACCEL结构数组的指针,该数组包含加速信息。见书226页说明。

    (3)旋转按钮的通知消息是:UDN_DELTAPOS它是当控件的当前数值将要改

    变时向其父窗口发送的。

    3)滑动条

        滑动条控件是由滑动块和可选的刻度线组成的。当用户用鼠标或方向键移动滑动块时,该控件发送通知消息来表明这些改变。见书226—227页说明。

    (1)滑动条的风格

    见书227页图5.38所示,其中各项含义见书227页表5.23所示。

    (2)滑动条的基本操作

    MFC的CSliderCtrl类提供了滑动条控件的各种操作函数,这其中包括范围、位置设置和获取等。

    void SetPos(int nPos);//设置滑动条的位置

    void SetRange(int nMin,int nMax,BOOL bRedraw=FALSE);// 设置滑动条的范围

    void GetPos();//获取滑动条的位置

    void GetRange();//获取滑动条的范围

    BOOL SetTic(int nTic);//设置滑动条控件中的一个刻度线的位置

    void SetTicFreq(int nFreq);//设置显示在滑动条中的刻度线的疏密程度

    void ClearTics(BOOL bRedraw=FALSE);

    void SetSelection(int nMin,int nMax);

    参数:nPos表示新的滑动条位置

          bMin和nMax表示滑动条的最小和最大位置

          bRedraw表示重画标志,为TRUE时,滑动条被重画。

          nTic表示刻度线的位置

          nFreq表示刻度线的疏密程度,如果参数设置为2,则在滑动条的范围中,

               每两个增量显示一个刻度线,要使这个函数有效,必须在属性对话

    框中选中Auto ticks项。

          bRedraw表示重画标志,若该参数为TRUE,则在选择被清除后,重画滑

    动条

          nMin和nMax表示滑动条的开始和结束位置

    (3)    滑动条的通知消息

              滑动条的通知消息代码常见的有:

    TB_BOTTOM,TB_ENDTRACK,

    TB_LINE_DOWN,TB_LINEUP,TB_PAGEDOWN,TB_PAGEUP,

    TB_THUMBPOSITION,TB_THUMBTRACK和TB_TOP等。这些消息代码都来自于WM_HSCROLL或WM_VSCROLL消息,其具体含义同滚动条。

    例:用滚动条、滑动条和旋转按钮控件分别来调整RGB的三种颜色分量:R

    (红色分量)、G(绿色分量)和B(蓝色分量),并根据用户指定的颜色填充

    一个矩形区域,如书228页图5.39所示:

    操作步骤如下:

    (1)打开前面创建的基于对话框的应用程序“按钮的使用”。

    (2)、(3)、(4)、(注意:在这可先将第(9)、(10)步作出,因第5步要用m_bEditOK

    和Draw)按书228---231页做完

    书(5)

    if(!m_bEditOK) return;

           UpdateData();

           m_Scroll.SetScrollPos(m_RValue);

           m_Slider.SetPos(m_GValue);

           Draw();

    书(6)

    CWnd *pWnd=GetDlgItem(IDC_DRAW);

           pWnd->UpdateWindow();

           Draw();

    书(7)

           //设置滚动条和滑动条的范围和当前位置

           m_Scroll.SetScrollRange(0,255);

           m_Scroll.SetScrollPos(m_RValue);

           m_Slider.SetRange(0,255);

           m_Slider.SetPos(m_GValue);

           //设置旋转按钮的范围

           m_Spin.SetRange(0,255);

           UpdateData(FALSE);//将数据传给控件

           m_bEditOK=TRUE;

    书(8)

    int nID=pScrollBar->GetDlgCtrlID();

           if(nID==IDC_SLIDER1)//是滑动条产生水平滚动消息

           {  m_GValue=m_Slider.GetPos();  }//获得滑动条当前的位置

           if(nID==IDC_SCROLLBAR1)//是滚动条产生水平滚动消息

           {

                  switch(nSBCode)

                  { 

                case SB_LINELEFT:m_RValue--;//单击滚动条左边箭头

                               break;

                    case SB_LINERIGHT:m_RValue++;//单击滚动条右边箭头

                                        break;

                    case SB_PAGELEFT:m_RValue-=10;

                                        break;

                    case SB_PAGERIGHT:m_RValue+=10;

                                        break;

              case SB_THUMBTRACK:m_RValue=nPos;

                                        break;

                  }

                  if(m_RValue<0)  m_RValue=0;

                  if(m_RValue>255)m_RValue=255;

                  m_Scroll.SetScrollPos(m_RValue);

           }

           UpdateData(FALSE);

           Draw();

    书(9)第4步将Draw()函数加上了,在这里添加代码:

           CWnd *pWnd=GetDlgItem(IDC_DRAW);

           CDC *pDC=pWnd->GetDC();//获得窗口当前的设备环境指针

           CBrush drawBrush;//定义画刷变量

           drawBrush.CreateSolidBrush(RGB(m_RValue,m_GValue,m_BValue));

           //以上创建一个填充颜色画刷,RGB是一个颜色宏,用来将指定的红、

           //绿、蓝三种颜色分量转换成一个32位的RGB颜色值

           CBrush *pOldBrush=pDC->SelectObject(&drawBrush);

           CRect rcClient;

           pWnd->GetClientRect(rcClient);

           pDC->Rectangle(rcClient);

           pDC->SelectObject(pOldBrush);

    书(10)第4步已作了,别忘了在CScrollDlg.cpp的构造函数例将m_bEditOK

            设为:FALSE

    书(11)还是定位到原始程序的OnButton1函数处,改MessageBox("4")代码为:

    CScrollDlg dlg;

          dlg.DoModal();

    书(12)在按钮的使用Dlg.cpp的开头处加:#include “ScrollDlg.h”

    书(13)编译运行

    附:设置口令(附加举例)

    (1)建一个多文档应用程序(选MultipleDocuments后,直接按Finish)

    (2)插入一个对话框ResourseViewà点中Dialog单击右键àInsertDialogà出现对话框

    (3)将这个对话框的ID改为IDD_PASSWORD_DIALOG,名字处写:口令

    (4)将OK和CANCEL改为确定和取消(并拖到最下边)

    (5)拖一个静态文本StaticText写请输入口令,并拖一个编辑控件Edit Box

         改ID为:IDC_PASSWORD_EDIT,在Styles中选PassWord属性。

    (6)再加一个静态文本StaticText写:口令为0----9999之间的一个整数。

      在风格ExtendedStyles中置Client edge、Static edge、Modal frame。

    (7)可测试一下对话框布局

    (8)创建对话框类:

          双击对话框任意位置àOKà出见类表你写类名为:CPassWordDialog

    (9)加成员变量:

      ViewàClassWizardàMemberVariablesà(classname中是CPassWordDialog

    点中IDC_PASSWORD_EDITàAddVariableà名字写:m_password,TYPE

    类型写:int àOK后回来处,在下面的Minimumvalue处写:0

    Maximumvalue处写:9999。

    (10)在应用程序的InitInstance()函数中加代码:

         Bool CMyApp::InitInstance()

          {  -----

             CPassWordDialog  CDlg;

             if(CDlg.DoModal()==IDOK)  //用户按了确定按钮

                {

                  if(CDlg.m_password!=1949) //口令为整数1949

                  {

                    AfxMessageBox(“口令错误,确定后将退出程序”);

                    return FALSE;

                   }

                 }

              else  //如果按下取消按钮程序也结束

                 return FALSE;

              return TRUE;

            }

    (11)在文件“设置口令.cpp“头部加:

           #include “PassWordDialog.h”

    (12)编译、运行

     

    ---------------------

    8、图像列表和标签控件

        “图像列表”是一系列相同大小的图像的集合,每一个图像提供一个以0为基数的索引号。而一个“标签控件”类似于一个笔记本中的分割器,或一个文件上的标签。

      1)图象列表控件  

        图象列表控件常用来有效地管理多个位图和图标。在MFC中,图象列表控件是使用CImageList类来创建、显示或管理图象的。

    (1)创建一个图象列表首先要声明一个CImageList对象,然后调用Create

    函数。由于Create函数的重载很多,故这里只给出最常用的一个原型:

    BOOL Create(int cx,int cy,UINT nFlags,int nInitial,int nGrow);

    参数:cx和cy用于指定图象的像素大小;

    nFlags表示要创建的图象类型,一般取其ILC_COLOR和ILC_MASK(指定屏蔽图像)的组合,默认的ILC_COLOR为ILC_COLOR4(16色),ILC_COLOR8(256色)ILC_COLOR16(16位色)等。

    nInitial用于指定图像列表中最初的图像数目

    nGrow表示当图像列表的大小发生改变时,图像可以增加的数目。

    (2)图像列表常见的基本操作:

       int Add(CBitmap *pbmImage,CBitmap *pbmMask);

       int Add(CBitmap *pbmImage,COLORREF crMask);

       int Add(HICON hIcon);

    如上函数用来向一个图像列表添加一个图标或多个位图,成功时返回第一个新图像的索引号,否则返回-1。

         参数:pbmImage表示包含图像的位图指针

               pbmMask表示包含屏蔽的位图指针

               crMask表示屏蔽色

               hIcon表示图标句柄

     BOOL Remove(int nImage);//用于从图像列表中删除一个由nImage指定的图像

     BOOL Drew(CDC *pdc,int nImage,POINT pt,UINT nStyle);//用于在由pt指定的位//置处绘制一个图像

    参数:pdc表示绘制的设备环境指针

    nImage表示要绘制的图像的索引号

    nStyle用于指定绘制图像时采用的方式

    HICON ExtractIcon(int nImage);//用于将nImage指定的图像扩展为图标

    COLORREF SetBkColor(COLORREF cr);//设置图像列表的背景色,COLORREF是一种专门用于定义颜色的数据类型。

    2)标签控件

    通过使用标签控件,应用程序可以将一个窗口或对话框的相同区域定义为多个页面。每一页包含一 套信息或一组控件。当用户选择了相应的标签时,应用程序就会显示相应的信息或控件。一种特殊类型的标签控件把标签显示得象按钮一样。单击一个按钮将立即执 行一条命令而不是显示一个页,这也是标签控件与属性页的最根本区别。

    (1)标签控件的风格

    见书232页图5.40所示,书233页表5.26列出该属性对话框的各项含义。

    (2)标签控件的基本操作

     MFC的CTabCtrl类提供了标签控件的各种操作函数。

      CSize SetItemSize(CSize size);//设置某个项的宽度和高度

      void SetPadding(CSize size);//设置标签和标签周围的间隔

      int SetMinTabWidth(int cx);//设置标签项的最小宽度

     BOOL GetItemRect(int nItem,LPRECT lpRect)const;//获取标签的边界大小

     BOOL InsertItem(int nItem,LPCTSTR lpszItem);//

     BOOL InsertItem(int nItem,LPCTSTR lpszItem,int nImage);

     //如上函数表示在一个标签控件中插入某一标签项

     BOOL DeleteItem(int nItem);// 表示在一个标签控件中删除某一标签项

     BOOL DeleteAllItem();//表示在一个标签控件中删除所有标签项

    参数:nItem用于指定一个标签索引(0表示第一个标签)

          lpszItem用于指定标签文本

          nImage用于指定标签的图标在图像列表中的图像索引。

    说明:在标签控件中使用图像列表时,必须调用:

        CTabCtrl::SetImageList()函数来指定一个已创建的图像列表,函数

    原型如下:

     CImageList *SetImageList(CImageList *pImageList);

                 参数:pImageList用于表示一个图像列表指针

            int SetCurSel(int nItem);//设置当前选择的标签

            int GetCurSel()const;//获取当前选择的标签

          参数:nItem用于表示当前选择项的索引

            void DeselectAll(BOOL fExcludeFocus);//重新设置一个标签控件中的

    //项,清除任何被按下的项

            BOOL HighlightItem(int idItem,BOOL fHighlight=TRUE);//使某一个标签

    //项处于高亮状态

            参数:fExcludeFocus用于指定一个重排标志

                  idItem用于指定一个标签的索引

                  fHighlight指定要设置的高亮状态

     (3)标签控件的通知消息。

              常见的标签通知消息有:

    TCN_KEYDOWN  //表示用户按下某键

    TCN_SELCHANGE//表示标签选项已被改变

    和TCN_SELCHANGING//当前标签选项将要改变

    例: 使用无模式对话框来构造某个标签页面,一开始将所有的标签页面创建好,然后根据所选择的当前标签选项,决定哪个页面需要显示,哪个页面需要隐藏和禁用。

       操作步骤如下:

    (1)用MFC AppWizard(exe)创建一个基于对话框的应用程序“图像列表和标签控件”。按书234---237页做完。

    (2)、(3)、(4)、(5)、(6)、(7)、(8)、(9)注意:第9步的工作已经在第8

    步自动加完了,这里就不做什么了。

    书(10)切到ClassView右键对准CScrollDlg击出对话框后,加函数时要这样加:

         类型:void  函数名:SetDlgState(CWnd *pWnd,BOOL bShow)

         类型:void  函数名:DoTab(int nTab)

    void CMyDlg::SetDlgState(CWnd *pWnd,BOOL bShow)  

    {  pWnd->EnableWindow(bShow);

       if(bShow)

       {  pWnd->ShowWindow(SW_SHOW);

          pWnd->CenterWindow();//居中显示

       }

       else pWnd->ShowWindow(SW_HIDE);

    }

    void CMyDlg::DoTab(int nTab)

    {  if(nTab>2) nTab=2;//确定nTab值不能超过范围

       if(nTab<0) nTab=0;

       BOOL bTab[3];

       bTab[0]=bTab[1]=bTab[2]=FALSE;

       bTab[nTab]=TRUE;

       //切换对话框的显示和隐藏

       SetDlgState(m_pTab1Dlg,bTab[0]);

       SetDlgState(m_pTab2Dlg,bTab[1]);

       SetDlgState(m_pTab3Dlg,bTab[2]);

    }

    书(11)     int nSelect=m_Tab.GetCurSel();

                  if(nSelect>=0)

                      DoTab(nSelect);

    书(12)……

    书(13)

    m_pTab1Dlg=new CTab1Dlg();//为无模式对话框分配空间

        m_pTab2Dlg=new CTab2Dlg();

        m_pTab3Dlg=new CTab3Dlg();

           //创建无模式对话框,指定标签控件为无模式对话框的父窗口

           m_pTab1Dlg->Create(IDD_DIALOG1,&m_Tab);

        m_pTab2Dlg->Create(IDD_DIALOG2,&m_Tab);

        m_pTab3Dlg->Create(IDD_DIALOG3,&m_Tab);

           m_ImageList.Create(16,16,ILC_COLOR|ILC_MASK,3,0);//创建图象列表

           m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON1));//从图标加到图象列

    //表中

        m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON2));

        m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON3));

           m_Tab.SetImageList(&m_ImageList);//设置TAB控件所使用的图象列表

           m_Tab.InsertItem(0,"基本信息",0);

        m_Tab.InsertItem(1,"成绩",1);

        m_Tab.InsertItem(2,"备注",2);

           m_Tab.SetMinTabWidth(80);//设置标签项的最小宽度

           m_Tab.SetPadding(CSize(12,3));//设置标签项和图标周围的间隔

           m_Tab.SetCurSel(0);

           DoTab(0);

    书(14)if(m_pTab1Dlg) delete m_pTab1Dlg;

            if(m_pTab2Dlg) delete m_pTab2Dlg;

            if(m_pTab3Dlg) delete m_pTab3Dlg;

    书(15)CheckRadioButton(IDC_RADIO_MAN_1,IDC_RADIO_WOMAN_1,

                   IDC_RADIO_MAN_1);

    书(16)编译运行

    9、通用对话框和消息对话框

       A)通用对话框

         Windows提供了一组标准用户对话框,它们都具有相应的MFC库中的类

    来支持。所有通用对话框类都是从一个公共的基类CCommonDialog派生而来

    的。见书238页表5.28列出了这些通用对话框。

        这些对话框都有一个共同特点:它们都从用户获取信息,但并不对信息进

    行处理。例如:文件对话框可以帮助用户选择一个用于打开的文件,但它实际

    上只是给程序提供了一个文件路径名,用户的程序必须调用相应的成员函数才

    能打开文件。类似地,字体对话框只是填充一个描述字体的逻辑结构,但它并

    不创建字体。

    *** 例如:制作一个利用CFileDialog(文件对话框)类,弹出“打开”对话框的应用程序

    在打开一个文本文件或是位图文件时,往往要使用到CFileDialog类,CFileDilog类可以创建一个“打开”对话框,再通过调用其成员函数完成相应的功能,这里举出一些常用的函数。

            CFileDialog(BOOL bOpenFileDialog,LPCTSTR lpszDefExt=NULL,

              LPCTSTR lpszFileName=NULL,DWORD dwFlags=

    OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,

    LPCTSTR lpszFilter=NULL,CWnd *pParentWnd=NULL);

    //该函数用于创建CFileDialog类对象,如:

    CFileDialog dlg;

             Virtual int DoModal();//用于显示对话框,如:dlg.DoModal();

             CString GetPathName()const;//用于获取文件名和文件路径名

             CString GetFileName()const;//用于获取文件名

    操作步骤如下:

    1)创建一个单文档应用程序,名为:通用对话框

    2)创建弹出“打开”对话框的菜单命令

    MenuàIDR_MAINFRAMEà双击空白菜单à名处写:打开,ID处写:

    ID_FILEOPEN

    3)将ID_FILEOPEN连到view.cpp中去

    4)添加代码:

    (1)在view.h的public里加:

    CString FilePathname;//用于存储将要打开文件的路径名

    CString FileName; //用于存储文件名

    (2)完成菜单命令函数的代码:

       void CMyView::OnFileopen()

    {

       CFileDialog dlg(TRUE,_T(“TXT”),_T(“*.TXT”),

         OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,

         _T(“文本文件(*.TXT)|*.TXT|”));//设置打开文件为”.TXT”

       if(IDOK==dlg.DoModal())

        {

          FilePathname.Format(“%s %s”,”filepath:”,dlg.GetPathName());

          FileName.Format(“%s %s”,”filename:”,dlg.GetFileName());

    }

            Invalidate();

    }

     (3)在OnDraw()函数中添加如下代码:

    -----

     pDCàTextOut(0,0,FileName);//显示文件名

     pDCàTextOut(0,20,FilePathname);//显示路径名

    5)运行后,点刚建立的菜单,出现对话框,你随便写一个已有的文本文件(*.txt)文件名,便在屏幕上显示处文件名、路径名。

    B)消息对话框

         消息对话框是最简单的一类对话框,它只是用于显示信息。在Visual C++6.0

    的MFC类库中就提供相应的函数实现这样的功能,使用时,只要在用户程序

    任何地方调用它们即可。函数原型如下:

    int AfxMessageBox(LPCTSTR lpszText,UINT nType=MB_OK,UINT nIDHelp=0);

     //是全程函数,可以用在任何地方

    int MessageBox(LPCTSTR lpszText,LPCTSTR lpszCaption=NULL,UINT nType=

         MB_OK); //只能用于控件、对话框、窗口等一些窗口类中。

    参数:IDOK表示用户单击[OK]按钮

          lpszText表示在消息对话框中显示的字符串文本

          lpszCaption表示消息对话框的标题,为NULL时使用默认标题

          nIDHelp表示消息的上下文帮助ID号

          nType表示消息对话框的图标类型以及所包含的按钮类型,这些类型是

    用MFC预先定义的一些标识符来指定的,见书240页表5.29和5.30所示。

    例:消息对话框的使用

    (1)建一个基于对话框的应用程序

    (2)拖入对话框中两个按钮控件:BUTTON1 和BUTTON2

    (3)将这两个控件的映射消息分别加到:CMyDlg.cpp中

         ViewàClassWizard àClassnameàCMyDlgàIDC_BUTT

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/z443538426/archive/2008/01/12/2040082.aspx

    展开全文
  • MFC——对话框的创建及使用

    千次阅读 2020-04-09 17:50:11
    文章目录MFC对话框的创建实例对话框程序的创建过程:对话框应用程序框架介绍设计界面和工具箱MFC常用控件静态文本框CStatic普通按钮 CButton编辑框 CEdit组合框(下拉框)CComboBox列表控件 CListCtrl树控件 ...
  • CTest6_1App:为程序运行的基础,它使程序Windows下可以运行个实例。 CTest6_1Dlg:是对话框类。 MFC AppWizard(exe) —— test6_1 —— 对话框 —— 默认下一步 创建对话框如果应用程序不止一个对话框时,则...
  • HTML5编写格式命令详解

    万次阅读 2015-12-17 21:56:52
    下面是一些网上找的有关HTML的东西: Html网页的代码 很全哦 1)贴图:<img src="图片地址"> 2)加入连接:<a href="所要连接的相关地址"...3)新窗口打开连接:写上要写的字
  • 七、对话框的创建、设置以及布局

    千次阅读 2013-10-28 16:22:35
    对话框主要用于和用户交互,其上有很按钮以及文本输入框,用于执行一些操作系统命令并接受用户发来的消息;  b. 对话框是一种用户自定义的类,而里面的内容(即控件,比如静态文本、编辑框、列表等)都属于它的...
  • 回答:(1)执行经典模式下菜单“格式”|“多线样式”;(2)命令:mlstyle。执行mlstyle后,系统会弹出“多线样式对话框。单击“新建”按钮,系统弹出“创建新的多线样式对话框,输入“新样式名”后,单击“继续”...
  • 此版本包含新的主题文件对话框、可视化主题轻松选择器、改进的脚本控件以及其他新功能和改进!本文将为大家介绍v30.3中发布的新功能!让您对BCG最新发布的版本有一个全新的认识和了解。需要最新版的可以点击这里...
  • 下拉刷新模糊效果AutoLayout富文本图表表相关与Tabbar隐藏与显示HUD与Toast对话框其他UI 具体内容 下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件。SVPullToRefresh - 下拉刷新控件。MJRefresh -...
  • MFC对话框控件的属性、方法、消息

    千次阅读 2017-09-28 21:09:03
    Want return 指定当用户多行编辑框控件中按Enter键时插入一个回车符,否则用户按下的回车符会被解释为选择了对话框中的默认命令按钮,该属性对单行编辑框控件不起作用 Read-only 只读 Uppercase 输入字符全...
  • 文章目录一、基于对话框的程序二、“逃跑”的按钮三、属性表单和向导的创建3.1 创建属性页 一、基于对话框的程序 新建一个基于对话框的项目,项目名称:DlgTest。 生成的项目结构为: 解决方案结构: 类视图下有三...
  • wxpython 控件,对话框,光标,菜单

    千次阅读 2013-04-14 19:54:16
    EVT_TEXT_URL:如果Windows系统上,wx.TE_RICH或wx.TE_RICH2样式被设置了,并且wx.TE_AUTO_URL样式也被设置了,那么当文本控件内的URL上发生了一个鼠标事件时,该事件被触发。 EVT_TEXT_MAXLEN:如果使用...
  • 设置默认adb 调试 用adb调试android设备时,首次连接时,会出现一个授权提示: error: device unauthorized....原来我们的PC机(以windows为例)上启动了adb.exe进程时,adb会本地生成一对密钥adbkey(私钥)与a
  • MSChart控件的属性与属性对话框 1、MSChart控件的属性 (1)ChartType属性:用于设置或返回图表类型,MSChart控件图表类型与对应ChartType属性值如表8.7所示。如,ChartType=1则显示二维直方图,ChartType...
  • CAD中英语词汇及命令大全对于从事CAD相关职业的朋友来说,加深认识CAD中英语词汇及命令是很有必要的,今天小编特意整理了这些东西,希望对大家有帮助!2D Solid 二维实体 2D 实面2D Wireframe 二维线框3D Array 三维...
  • 今天学习啦小编给大家介绍一下...一旦选择此命令,则菜单旁会显示出一个附加的子菜单,该子菜单列出了可用的子命令,从中选择想要的子命令即可执行。三是命令名后面跟;有省略号(…)是一种连接符号,表示一个尚未...
  • 图表类型框中,用户可选择不同的图形,如条(直方图)、线(曲线图)、区域、步骤、组合、饼图、X Y散点图等。还可选择二维平面图形或三维立体图形。 (2)图表选项 图表选项框中,用户可选择下列四个复选框有效...
  • 样式,大家应该不陌生,Word中是一个非常强大的功能,运用得好,可以节省很时间和精力。但如果你不会用,关键时刻可以把你急哭了!前面的“Word样式,简单又实用!”和“Word样式也有“快捷键”,你却不知道!...
  • Windows WMIC命令使用详解(附实例)

    千次阅读 2016-06-22 11:39:11
    Windows WMIC命令使用详解(附实例) 第一次执行WMIC命令时,Windows...这个命令可以XP或 .NET Server的标准命令行解释器(cmd.exe)、Telnet会话或“运行”对话框中执行。这些启动方法可以本地使用,也可以通
  • Windows WMIC命令使用详解

    千次阅读 2018-08-26 09:12:29
    Windows WMIC命令使用详解 第一次执行WMIC命令时,Windows首先...这个命令可以XP或.NET Server的标准命令行解释器(cmd.exe)、Telnet会话或“运行”对话框中执行。这些启动方法可以本地使用,也可以通过.NET S...
  • Auto CAD:CAD软件之上菜单栏(CAD选项设置、常用工具(样条曲线/线/倒角/移动/镜像/偏移/修剪工具/延伸/阵列)、修改工具(拉长/编辑线/对齐命令、注释/标注)简介之详细攻略 目录 菜单栏(CAD选项...
  • Word中设置段落边框能使文章...Word设置文字段落边框和底纹的步骤一、添加边框1、打开Word2010文档页面,选中需要添加边框的一个或个段落。2、“段落”中单击“边框”下三角按钮。Word设置文字段落边框和底纹...
  • 可是那么CAD快捷键命令,记不全怎么办呢?下面小编就先来给大家介绍一下浩辰CAD制图软件中CAD快捷键命令大全之CAD标注快捷键命令吧! CAD标注快捷键命令汇总: 线性标注—DLI(DIMLINEAR) 工具栏:标注工具栏—...
  • Windows WMIC 命令使用详解 (附实例)

    万次阅读 2019-12-24 08:58:00
    第一次执行WMIC命令时,...这个命令可以XP或 .NET Server的标准命令行解释器(cmd.exe)、Telnet会话或“运行”对话框中执行。这些启动方法可以本地使用,也可以通过.NET Server终端服务会话使用。 第一次执行W...
  • auto hot key常用命令

    2011-04-28 14:18:14
    1、MsgBox命令有几种格式? MsgBox, 文本 或 MsgBox [, 类型,标题, 文本, 显示时间 ] 2、MsgBox命令按钮有几种类型? OK—0 OK/Cancel—1 Abort/Retry/Ignore—2 Yes/No/Cancel—3 Yes/No—4 Retry/Cancel—...
  • window常用命令

    2014-04-12 18:32:38
     F3 当你桌面上的时候是打开“查找:所有文件” 对话框  F10或ALT 激活当前程序的菜单栏  windows键或CTRL+ESC 打开开始菜单  CTRL+ALT+DELETE win9x中打开关闭程序对话框  DELETE 删除被选
  • CAD常用命令大全

    2019-09-25 17:47:06
    A 圆弧 L 直线 C 圆 I 插入块 B 创建块 H 图案填充D 标注样式管理器 E 删除F 圆角G 群组M 移动 O 偏移P 平移S 拉伸 W 外部块V 视图对话框 X 分解Z 显示缩放T 多行文字 co 复制 MI 镜像 AR 阵列RO 旋转SC 比例LE 引线...
  • cad的常用命令

    千次阅读 2010-12-08 09:49:00
    CAD常用命令 一、绘图工具栏中的绘图命令(英文名)及命令快捷键 1、直线(line) L... 3、多线(mline)ML: 1、 对正: 1)上:光标下绘制多线。。 2)无(中):将光标作为原点绘

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,741
精华内容 4,296
关键字:

在格式多线样式命令对话框