牛骨文教育服务平台(让学习变的简单)
博文笔记

MFC傻瓜式教程

创建时间:2017-04-20 投稿人: 浏览次数:10794

本教程重操作,轻理论,为操作减负。需了解详细原理的朋友可以自行看各种书籍。
  

MFC:Microsoft Foundation Class ,微软基础类库。

对话框的创建和显示

  1. 新建MFC AppWizard(exe)工程,单文档类型。工程名:Mybole。编译运行。
    新建
  2. 点击帮助-关于Mybole。这是MFC自动创建的。
    关于

  3. 创建自己的对话框。点击Insert-Resource。选择Dialog,点击New。VC++自动将其标识设置为IDD_DIALOG1,并自动添加到ResourceView-Dialog项中。Dialog项下还有一个对话框资源标识:IDD_ABOUTBOX,即上一步中的“关于”对话框。
    Insert Resource对话框

    新建的对话框资源

  4. 选中对话框本身,右键点击属性。将Caption设置为“测试”。

  5. 选择View-ClassWizard,点击create a new class,OK。出现下图,并输入下图选项。
    New Class
  6. 在随后出现的MFC ClassWizard对话框上点击OK。
    列表
    注意:看看左侧类列表中是否添加好了CTestDlg,否则会影响后续操作。

接下来,我们希望在程序中显示这个对话窗口。

  1. 点击右侧菜单Menu,选中IDR_MAINFRAME。点击帮助旁边的虚线框。
    Menu

  2. 对虚线框右键属性,修改为下图。
    属性

  3. 关闭属性。点击View-ClassWizard(中文是建立类向导),选择CMyboleView,用COMMAND命令消息响应函数。如图。
    COMMAND

模态对话框的创建

  需要调用CDialog类的成员函数:DoModal,它能创建并显示一个模态对话框,其返回值将作为CDialog类的另一个成员函数:EndDialog的参数,后者功能是关闭模态对话框。

  在FileView中选择MyboleView.cpp,编写程序。
  记得在开头添加头文件 #include “testdlg.h” (头文件大小写问题,linux区分,windows不区分)
编程
  显示模态对话框的具体实现代码:

void CMyboleView::OnDialog() 
{
    // TODO: Add your command handler code here
    CTestDlg dlg;
    dlg.DoModal();
}

编译运行,点击对话框。会发现若不确认该窗口,将无法点击其他窗口。
模态对话框1

模态对话框2

非模态对话框的创建

将上面的模态对话框代码注释掉。

改为:

void CMyboleView::OnDialog() 
{
    // TODO: Add your command handler code here
    //CTestDlg dlg;
    //dlg.DoModal();

    CTestDlg *pDlg = new CTestDlg;
    pDlg->Create(IDD_DIALOG1,this);
    pDlg->ShowWindow(SW_SHOW);
}

注意:需要把之前运行的对话框关掉才能编译成功。

然而,当它生命周期结束时,所保存的内存地址就丢失了,那么程序中也就无法再引用到它所指向的那块内存。于是,我们这样解决该问题。
MFC ClassWizard

注意:Message里双击添加函数或者点击add Class…

void CTestDlg::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class
delete this;
CDialog::PostNcDestroy();
}

区别:点击确定,对话框都会消失。但是,模态对话框窗口对象被销毁了。对非模态对话框来说,只是隐藏起来了,并未被销毁。
因此,若要销毁对话框,若有一个ID为IDOK的按钮,就必须重写基类的OnOK这个虚函数,并在重写的函数中调用DestroyWindow函数,完成销毁。并不要再调用基类的OnOK函数。
同样地,若有一个ID为IDCANCEL的按钮,也必须重写基类的OnCancel虚函数,并在重写的函数中调用DestroyWindow函数,完成销毁。并不要再调用基类的OnCancel函数。

注释掉非模态对话框代码,还原模态对话框代码。

点击ResourceView-IDD_DIALOG1,打开资源,用鼠标拖出控件面板上的Button按钮控件,对按钮右键,选择属性,设置如下。
按钮

接下来,我们实现当单击Add按钮时,在对话框中动态创建一个按钮这一功能。

  1. 为CTestDlg类添加一个私有的CButton成员变量。
      点击ClassView标签页右键,如图点击。
    ClassView

      填入信息。
    添加成员变量

  2. 添加Add按钮单击消息的响应函数。
      按钮点右键,选ClassWizard(建立类向导),如图。
    建立类向导

      单击Edit Code,即可定位到该函数定义处。
      添加一下代码:

void CTestDlg::OnBtnAdd() 
{
    // TODO: Add your control notification handler code here
    m_btn.Create("New",BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,
                 CRect(0,0,100,100),this,123);
}

  为避免多次点击Add出现非法操作,我们需要进行如下步骤。

  1. 为CTestDlg类增加一个私有的BOOL类型成员变量。
    变量类型:BOOL
    变量名称:m_bIsCreated
    Access: private

  2. 在TestDlg.cpp中找到构造函数,将m_bIsCreated初始为FALSE。如图所示。
    这里写图片描述

      或者改为如下亦可。
    Static BOOL bIsCreated = FALSE;

  3. 回到Add,双击它,进入代码部分,改之。

void CTestDlg::OnBtnAdd() 
{
    // TODO: Add your control notification handler code here
    if(m_bIsCreated==FALSE)
    {
    m_btn.Create("New",BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,
                CRect(0,0,100,100),this,123);
    m_bIsCreated = TRUE;
    }
    else
    {
        m_btn.DestroyWindow();
        m_bIsCreated = FALSE;
    }

}

  或者以下亦能实现。

void CTestDlg::OnBtnAdd() 
{
    // TODO: Add your control notification handler code here
    if(!m_btn.m_hWnd)
    {
    m_btn.Create("New",BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,
                CRect(0,0,100,100),this,123);
    }
    else
    {
        m_btn.DestroyWindow();
    }

}

  效果:
  这里写图片描述

  点击Add出现New窗口,再点击就销毁。

控件的访问

控件的调整

用Layout-Align,Layout-Make Same Size,Layout-Space Evenly里的选项进行调整。
这里写图片描述

静态文本控件

  查看三个静态文本框,它们ID相同。我们可以更改第一个静态文本框ID为IDC_NUMBER1,再打开ClassWizard,可以在ObjectIDs看到新ID。
这里写图片描述
  对BN_CLICKED进行Add Function,并Edit Code:

  此时运行程序点击第一个静态文本框并没有反应。这是因为:静态文本控件在默认状态下是不发送通告消息的

  为了该控件能向父窗口发送鼠标事件,我们对该文本框右键-属性,切换到styles选项卡,勾上Notify。
这里写图片描述

  现在可以显示了:
  点击就改变。
这里写图片描述

  总结:为了使一个静态文本控件能够响应鼠标单击消息,那么需要进行两个特殊的步骤:第一步,改变它的ID;第二步,在它的属性对话框中选中Notify选项。

编辑框控件

利用上面的对话框实现这样的功能:在前两个编辑框中分别输入一个数字,然后单击Add按钮,对前两个编辑框中的数字求和,并将结果显示在第三个编辑框中。

第一种方式

void CTestDlg::OnBtnAdd() 
{
    int num1, num2, num3; 
    char ch1[10], ch2[10], ch3[10]; 
    GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10); 
    GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10);

    num1 = atoi(ch1); 
    num2 = atoi(ch2); 
    num3 = num1 + num2; 

    itoa(num3,ch3,10);
    GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);
}

C语言转换函数:atoi 将一个由数字组成的字符串转换为相应的数值
itoa 数值转换为文本
itoa函数的第三个参数表示转换的进制,数字10表示十进制。

效果:
这里写图片描述

第二种方式

  代码如下:

void CTestDlg::OnBtnAdd() 
{
    int num1, num2, num3; 
    char ch1[10], ch2[10], ch3[10]; 
    //GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10); 
    //GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10);

    GetDlgItemText(IDC_EDIT1,ch1,10);
    GetDlgItemText(IDC_EDIT2,ch2,10);

    num1 = atoi(ch1); 
    num2 = atoi(ch2); 
    num3 = num1 + num2; 

    itoa(num3,ch3,10);
    //GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);
    SetDlgItemText(IDC_EDIT3,ch3);
}

  GetDlgItemText 将返回对话框中指定ID的控件上的文本,相当于将上面的GetDlgItem和GetWindowText这两个函数功能组合起来了。
与之对应的是SetDlgItemText,用来设置对话框中指定ID的控件上的文本。

第三种方式

void CTestDlg::OnBtnAdd() 
{
    int num1, num2, num3; 
    //char ch1[10], ch2[10], ch3[10]; 
    //GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10); 
    //GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10);

    //GetDlgItemText(IDC_EDIT1,ch1,10);
    //GetDlgItemText(IDC_EDIT2,ch2,10);

    num1 = GetDlgItemInt(IDC_EDIT1);
    num2 = GetDlgItemInt(IDC_EDIT2);

    //num1 = atoi(ch1); 
    //num2 = atoi(ch2); 
    num3 = num1 + num2; 

    //itoa(num3,ch3,10);
    //GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);
    //SetDlgItemText(IDC_EDIT3,ch3);
    SetDlgItemInt(IDC_EDIT3,num3);
}

第四种方式

  将这三个编辑框分别与对话框类的三个成员变量相关联,然后通过这些成员变量来检索和设置编辑框的文本,这是最简单的访问控件的方式。
打开ClassWizard对话框,切换到Member Variables选项卡,如图。
这里写图片描述
  首先为IDC_EDIT1编辑框添加一个关联的成员变量,方法是在Control IDs列表中选中IDC_EDIT1,再单击Add Variable按钮,如图。
这里写图片描述
这里写图片描述

  同样地,为IDC_EDIT2和IDC_EDIT3分别添加好成员变量。
  接着修改代码:

void CTestDlg::OnBtnAdd() 
{
    UpdateData();
    m_num3 = m_num1 + m_num2;
    UpdateData(FALSE);
}

  对编辑框控件中输入的数值设定一个范围:
  打开ClassWizard-Member Variable,选中IDC_EDIT1,下方输入0和100。同样为IDC_EDIT2也设置好。
这里写图片描述

第五种方式

  将编辑框控件再与一个变量相关联,代表控件本身。为IDC_EDIT1增加一个控件类型的变量:m_edit1,类别为Control。同样地,也为IDC_EDIT2和IDC_EDIT3添加。
这里写图片描述

  修改代码:

void CTestDlg::OnBtnAdd() 
{
    int num1, num2, num3; 
    char ch1[10], ch2[10], ch3[10]; 

    m_edit1.GetWindowText(ch1,10);
    m_edit2.GetWindowText(ch2,10);

    //num1 = GetDlgItemInt(IDC_EDIT1);
    //num2 = GetDlgItemInt(IDC_EDIT2);

    num1 = atoi(ch1); 
    num2 = atoi(ch2); 
    num3 = num1 + num2; 

    itoa(num3,ch3,10);
    m_edit3.SetWindowText(ch3);
}

第六种方式

  修改代码:

void CTestDlg::OnBtnAdd() 
{
    int num1, num2, num3; 
    char ch1[10], ch2[10], ch3[10]; 

    ::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd, WM_GETTEXT, 10, (LPARAM)ch1);
    ::SendMessage(m_edit2.m_hWnd, WM_GETTEXT, 10, (LPARAM)ch2);

    num1 = atoi(ch1); 
    num2 = atoi(ch2); 
    num3 = num1 + num2; 

    itoa(num3,ch3,10);
    m_edit3.SendMessage(WM_SETTEXT, 0, (LPARAM)ch3);
}

第七种方式

  修改代码:

void CTestDlg::OnBtnAdd() 
{
    int num1, num2, num3; 
    char ch1[10], ch2[10], ch3[10]; 

    SendDlgItemMessage(IDC_EDIT1, WM_GETTEXT, 10, (LPARAM)ch1);
    SendDlgItemMessage(IDC_EDIT2, WM_GETTEXT, 10, (LPARAM)ch2);

num1 = atoi(ch1); 
    num2 = atoi(ch2); 
    num3 = num1 + num2; 

    itoa(num3,ch3,10);
    SendDlgItemMessage(IDC_EDIT3, WM_SETTEXT, 0, (LPARAM)ch3);
}

  获得编辑框复选的内容:
  在上述代码最后添加:
SendDlgItemMessage(IDC_EDIT3, EM_SETSEL, 0, -1); //0,-1表示全选若1,3表示选中1-3位复选
m_edit3.SetFocus();

  效果:
这里写图片描述

总结

1 GetDlgItem()->Get(Set)WindowTest()
2 GetDlgItemText()/SetDlgItemText()
3 GetDlgItemInt()/SetDlgItemInt()
4 将控件和整型变量相关联
5 将控件和控件变量相关联
6 SendMessage()
7 SendDlgItemMessage()
  最常用是1、4、5。在利用MFC编程时,6、7用得少。

对话框伸缩功能的实现

  对话框上再添加一个按钮,Caption设置为“收缩<<”点击ClassWizard,添加一个命令相应函数(BN_CLICKED)。具体实现代码为:

void CTestDlg::OnButton1() 
{
    CString str; 
    if(GetDlgItemText(IDC_BUTTON1,str), str == "收缩<<")
    {
        SetDlgItemText(IDC_BUTTON1, "拓展>>");
    }
    else
    {
        SetDlgItemText(IDC_BUTTON1, "收缩<<");
    }
}

  拖动一个图像控件来划分对话框中要动态切除的部分。
这里写图片描述

  修改该控件ID为IDC_SEPATATOR,styles选项卡勾上Sunken选项。
  修改代码:

void CTestDlg::OnButton1() 
{
    CString str; 
    if(GetDlgItemText(IDC_BUTTON1,str), str == "收缩<<")
    {
        SetDlgItemText(IDC_BUTTON1, "拓展>>");
    }
    else
    {
        SetDlgItemText(IDC_BUTTON1, "收缩<<");
    }
    static CRect rectLarge;
    static CRect rectSmall;

    CRect rect1(10,10,10,10);
    CRect rect2(0,0,0,0);

    if(rectLarge.IsRectNull())
    {
        CRect rectSeparator;
        GetWindowRect(&rectLarge);
        GetDlgItem(IDC_SEPARATOR)->GetWindowRect(&rectSeparator);

        rectSmall.left=rectLarge.left;
        rectSmall.top=rectLarge.top;
        rectSmall.right=rectLarge.right;
        rectSmall.bottom=rectSeparator.bottom;
    }
    if(str == "收缩<<")
    {
        SetWindowPos(NULL, 0, 0, rectSmall.Width(), rectSmall.Height(), SWP_NOMOVE | SWP_NOZORDER);
    }
    else
    {
        SetWindowPos(NULL, 0, 0, rectLarge.Width(), rectLarge.Height(), SWP_NOMOVE | SWP_NOZORDER);
    }
}

  效果:
这里写图片描述

  点击“收缩<<”:
这里写图片描述

  若希望隐藏分隔条,则设置属性去掉“Visible”前的勾。

输入焦点的传递

  为了屏蔽掉默认的回车键关闭对话框这一功能,应该在对话框子类(此处是CTestDlg类)中重写OK按钮的消息响应函数。
  首先点击OK按钮,添加鼠标单击消息响应函数。注释掉原有函数。

法一

  在ClassView选项卡的CTestDlg类添加WM_INITDIALOG消息的响应函数。对类右键,选择Add Windows Message Handler,在弹出的框左侧选择WM_INITDIALOG,直接单击Add and Edit,跳转。
  修改代码为:

void CTestDlg::OnOK() 
{
    // TODO: Add extra validation here

    //CDialog::OnOK();
}


WNDPROC prevProc;
    LRESULT CALLBACK NewEditProc(
        HWND hwnd,
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
        )
    {
        if(uMsg == WM_CHAR && wParam == 0x0d)
        {
            ::SetFocus(GetNextWindow(hwnd,GW_HWNDNEXT));
            return 1;
        }
        else
        {
            return prevProc(hwnd,uMsg,wParam,lParam);
        }
    }

BOOL CTestDlg::OnInitDialog() 
{
    CDialog::OnInitDialog();
    prevProc=(WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,
        GWL_WNDPROC, (LONG)NewEditProc);
    return TRUE;
}

  查看第一个编辑框的属性,打开styles选项卡,勾上MultiLine(多行)。即可实现焦点的传递。

法二

  只需要改变一行代码:

WNDPROC prevProc;
    LRESULT CALLBACK NewEditProc(
        HWND hwnd,
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
        )
    {
        if(uMsg == WM_CHAR && wParam == 0x0d)
        {
            //::SetFocus(GetNextWindow(hwnd,GW_HWNDNEXT));
            SetFocus(::GetWindow(hwnd,GW_HWNDNEXT));
            return 1;
        }
        else
        {
            return prevProc(hwnd,uMsg,wParam,lParam);
        }
    }

法三

  编辑框属性有一个WS_TABSTOP,如果勾选了,则在对话框中按下Tab键后,输入焦点可以转移到此控件上。

  修改一行代码:

WNDPROC prevProc;
    LRESULT CALLBACK NewEditProc(
        HWND hwnd,
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
        )
    {
        if(uMsg == WM_CHAR && wParam == 0x0d)
        {
            SetFocus(::GetNextDlgTabItem(::GetParent(hwnd),hwnd,FALSE));
            //::SetFocus(GetNextWindow(hwnd,GW_HWNDNEXT));
            //SetFocus(::GetWindow(hwnd,GW_HWNDNEXT));
            return 1;
        }
        else
        {
            return prevProc(hwnd,uMsg,wParam,lParam);
        }
    }

  三种方法的缺点:只修改了第一个编辑框的窗口过程,因此从第二到第三个编辑框的焦点转移无法实现,除非继续修改第二个编辑窗口。

  再介绍一种方法解决这个问题。

法四

  在MFC中,默认情况下,当在对话框窗口中按下回车键时,会调用对话框的默认按钮的响应函数,我们可以在此默认按钮的响应函数中把焦点依次向下传递。

  首先取消第一个编辑框的MultiLine。
  接着修改OnOK函数为:

void CTestDlg::OnOK() 
{
    // TODO: Add extra validation here
    //GetDlgItem(IDC_EDIT1)->GetNextWindow()->SetFocus();
    //GetFocus()->GetNextWindow()->SetFocus();
    //GetFocus()->GetWindow(GW_HWNDNEXT)->SetFocus();
    GetNextDlgTabItem(GetFocus())->SetFocus();
    //CDialog::OnOK();
}```

  注释掉的部分是各种失败的尝试,各有各的bug。现在程序是正常的。

    **注意:然而该屏蔽回车键的方法并非是常规做法,应该在PreTranslateMessage中进行拦截。(return TRUE即拦截)**
  具体做法:
  现在Testdlg.h中添加:
```C++
class CTestDlg : public CDialog
{

protected:
    virtual BOOL PreTranslateMessage(MSG* pMsg);

public:
    virtual void OnOK();
……




<div class="se-preview-section-delimiter"></div>

  接着:

CTestDlg::PreTranslateMessage(MSG* pMsg)
{
    //屏蔽ESC关闭窗体
    if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
    {
        return TRUE;
    }
    //屏蔽回车关闭窗体,但会导致回车在窗体上失效.
    /*
    if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN && pMsg->wParam)
    {
        return TRUE;
    }
    */
    else 
    {
        return CDialog::PreTranslateMessage(pMsg);
    }
}

void CTestDlg::OnOK() 
{
    // TODO: Add extra validation here

    //CDialog::OnOK();
}




<div class="se-preview-section-delimiter"></div>

  点击Layout-Tab order,这些序号就是各控件的Tab顺序。顺序可改变,依次点击希望的顺序控件即可。

  调用顺序:当用户按下回车键时,Windows将查看对话框中是否存在指定的默认按钮,如果有,就调用该默认按钮单击消息的响应函数。如果没有,就会调用虚拟的OnOK函数,即使对话框没有包含默认的OK按钮(这个默认OK按钮的ID是IDOK)。

C语言对文件操作的支持

  新建单文档类型的MFC应用程序,工程名为File,并为主菜单添加一个子菜单,名称为“文件操作”,然后为其添加两个菜单项,并分别为它们添加相应的命令响应函数(通过COMMAND),让CFileView类接收这些菜单项的命令响应。
这里写图片描述
这里写图片描述

文件的打开和写入

  代码:

void CFileView::OnFileWrite() 
{
    FILE *pFile = fopen("1.txt","w");
    fwrite("http://www.sunxin.org", 1, strlen("http://www.sunxin.org"), pFile); 
}




<div class="se-preview-section-delimiter"></div>

  编译后可看到文件夹中生成了1.txt,打开有一行网址。

文件的关闭

  增加一行代码:

void CFileView::OnFileWrite() 
{
    FILE *pFile = fopen("1.txt","w");
    fwrite("http://www.sunxin.org", 1, strlen("http://www.sunxin.org"), pFile); 
    fclose(pFile);
}




<div class="se-preview-section-delimiter"></div>

文件指针定位

  代码:

void CFileView::OnFileWrite() 
{
    FILE *pFile = fopen("1.txt","w");
    fwrite("http://www.sunxin.org", 1, strlen("http://www.sunxin.org"), pFile); 
    fwrite("欢迎访问", 1, strlen("欢迎访问"), pFile);
    fclose(pFile);
}




<div class="se-preview-section-delimiter"></div>

  显示:http://www.sunxin.org欢迎访问

  将文件指针移动到文件的开始位置处:
  代码:

void CFileView::OnFileWrite() 
{
    FILE *pFile = fopen("1.txt","w");
    fwrite("http://www.sunxin.org", 1, strlen("http://www.sunxin.org"), pFile); 
    fseek(pFile, 0, SEEK_SET);
    fwrite("ftp:", 1, strlen("ftp:"),pFile);
    //fwrite("欢迎访问", 1, strlen("欢迎访问"), pFile);
    fclose(pFile);
}




<div class="se-preview-section-delimiter"></div>

  显示:ftp:://www.sunxin.org

文件的读取

  在OnFileRead函数中写入代码:

void CFileView::OnFileRead() 
{
    FILE *pFile = fopen("1.txt","r");
    char ch[100];
    fread(ch, 1, 100, pFile);
    fclose(pFile);
    MessageBox(ch);

}




<div class="se-preview-section-delimiter"></div>

  编译运行:
  
这里写图片描述

  原因:C语言以“”结束。

  解决方法:
  法一:
  修改代码:

void CFileView::OnFileWrite() 
{
    FILE *pFile = fopen("1.txt","w");
    char buf[22] = "http://www.sunxin.org";
    buf[21] = "";
    fwrite(buf, 1, 22, pFile);
    fclose(pFile);
}




<div class="se-preview-section-delimiter"></div>

  先点击写入文件,再点击读取文件,就可以看到正确的内容。
  缺点:增加了文件大小。

法二:

void CFileView::OnFileRead() 
{
    FILE *pFile = fopen("1.txt","r");
    char ch[100];
    memset(ch, 0, 100);
    fread(ch, 1, 100, pFile);
    fclose(pFile);
    MessageBox(ch);

}




<div class="se-preview-section-delimiter"></div>

法三:
  读取文件时,不知道文件大小时的做法。

void CFileView::OnFileRead() 
{
    FILE *pFile = fopen("1.txt","r");
    char *pBuf;
    fseek(pFile, 0, SEEK_END);
    int len=ftell(pFile);
    pBuf = new char[len+1];
    rewind(pFile);
    fread(pBuf, 1, len, pFile);
    pBuf[len] = 0;
    fclose(pFile);
    MessageBox(pBuf);
}




<div class="se-preview-section-delimiter"></div>

二进制文件和文本文件

  代码:

void CFileView::OnFileWrite() 
{
    FILE *pFile = fopen("2.txt", "w");
    char ch[3];
    ch[0] = "a";
    ch[1] = 10;
    ch[2] = "b";
    fwrite(ch, 1, 3, pFile);
    fclose(pFile);
}

void CFileView::OnFileRead() 
{
    FILE *pFile = fopen("2.txt","r");
    char ch[100];
    fread(ch, 1, 3, pFile);
    ch[3] = 0;
    fclose(pFile);
    MessageBox(ch);
}




<div class="se-preview-section-delimiter"></div>

  效果:
  
这里写图片描述

  文本方式:10实际上是换行符的ASCII码。

  以文本方式和二进制方式读取文件是有明显的区别的。

文本方式和二进制方式

  二进制方式:换行是由两个字符组成的,即ASCII码10(回车符)和13(换行符)。
  写入和读取文件时要保持一致。如果采用文本方式写入,应采用文本方式读取;如果采用二进制方式写入数据,在读取时也应采用二进制方式。

  面试题:给你一个整数,如:98341,将这个整数保存到文件中,要求在以记事本程序打开该文件时,显示的是:98341。
  法一:

void CFileView::OnFileWrite() 
{
    FILE *pFile = fopen("3.txt", "w");
    char ch[5];
    ch[0] = 9 + 48;
    ch[1] = 8 + 48;
    ch[2] = 3 + 48;
    ch[3] = 4 + 48;
    ch[4] = 1 + 48;

    fwrite(ch, 1, 5, pFile);
    fclose(pFile);

}




<div class="se-preview-section-delimiter"></div>

  或

void CFileView::OnFileWrite() 
{
    FILE *pFile = fopen("3.txt", "w");
    int i = 98341;
    char ch[5];
    itoa(i, ch, 10);

    fwrite(ch, 1, 5, pFile);
    fclose(pFile);

}




<div class="se-preview-section-delimiter"></div>

  面试题:给定一个字符串,其中既有数字字符,又有26个英文字母中的几个字符,让你判断一下哪些是数字字符。

  对这种问题,实际上就是判断各字符的ASCII码,对于数字字符来说,它们的ASCII码大于等于48,小于等于57。

C++对文件操作的支持

void CFileView::OnFileWrite() 
{
    ofstream ofs("4.txt");
    ofs.write("http://www.sunxin.org",strlen("http://www.sunxin.org"));
    ofs.close;

}

void CFileView::OnFileRead() 
{
    ifstream ifs("4.txt");
    char ch[100];
    memset(ch, 0, 100);
    ifs.read(ch,100);
    ifs.close();
    MessageBox(ch);
}




<div class="se-preview-section-delimiter"></div>

Win32 API 对文件操作的支持

文件的创建、打开和写入

void CFileView::OnFileWrite() 
{
    //定义一个句柄变量
    HANDLE hFile;
    //创建文件
    hFile = CreateFile("5.txt", GENERIC_WRITE, 0, NULL, CREATE_NEW, 
        FILE_ATTRIBUTE_NORMAL, NULL);
    //接收实际写入的字节数
    DWORD dwWrites;
    //写入数据
    WriteFile(hFile,"http://www.sunxin.org",strlen("http://www.sunxin.org"),
        &dwWrites, NULL);
    //关闭文件句柄
    CloseHandle(hFile);
}




<div class="se-preview-section-delimiter"></div>

文件的读取

void CFileView::OnFileRead() 
{
    HANDLE hFile;
    //打开文件
    hFile = CreateFile("5.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    //接收实际收到的数据
    char ch[100];
    //接收实际读取到的字节数
    DWORD dwReads;
    //读取数据
    ReadFile(hFile, ch, 100, &dwReads, NULL);
    //设置字符串结束字符
    ch[dwReads] = 0;
    //关闭打开的文件对象的句柄
    CloseHandle(hFile);
    //显示读取到的数据
    MessageBox(ch);
}




<div class="se-preview-section-delimiter"></div>

菜单命令响应函数

  新建一个单文档的MFC AppWizard(exe)工程,工程名为Menu。Build运行。

这里写图片描述
  左上角点击按钮,可以让属性框始终显示,不会因为点击对话框以外的地方就消失。
  去掉Pop-up弹出前的勾,将ID改为ID_TEST。给Test添加响应函数在CMainFrame中,在函数中加入 MessageBox(“MainFrame Clicked”);
  效果:
这里写图片描述

菜单命令的路由

程序类对菜单命令的响应顺序

  响应Test
  菜单项命令的顺序依次是:视类、文档类、框架类,最后才是应用程序类。

Windows消息的分类

  凡是从CWnd派生的类,它们既可以接收标准消息,也可以接收命令消息和通告消息。而对于那些从CCmdTarget派生的类,则只能接收命令消息和通告消息,不能接收标准消息。
本例中的文档类(CMenuDoc)和应用程序类(CWinApp),因为它们都派生于CCmdTarget类,所以它们可以接收菜单命令消息。但它们不是从CWnd类派生的,所以不能接收标准消息。

菜单命令的路由

  菜单命令消息路由的具体过程:当点击某个菜单项时,最先接收到这个菜单命令消息的是框架类。框架类将把接收到的这个消息交给它的子窗口,即视类,由视类首先进行处理。视类首先根据命令消息映射机制查找自身是否对此消息进行了响应,如果响应了,就调用相应响应函数对这个消息进行处理,消息路由过程结束;如果视类没有对此命令消息做出响应,就交由文档类,文档类同样查找自身是否对这个菜单命令进行了响应,如果响应了,就由文档类的命令消息响应函数进行处理,路由过程结束。如果文档类也未做出响应,就把这个命令消息交还给视类,后者又把该消息交还给框架类。框架类查看自己是否对这个命令消息进行了响应,如果它也没有做出响应,就把这个菜单命令消息交给应用程序类,由后者来进行处理。

基本菜单操作

标记菜单

  运行刚才创建的Menu程序,点击查看,前面都有一个对号,这种类型就是标记菜单。
在CMainFrame类的OnCreate的return语句之前添加这句代码 GetMenu()->GetSubMenu(0)->CheckMenuItem(0, MF_BYPOSITION | MF_CHECKED); 或者GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW, MF_BYCOMMAND | MF_CHECKED);
  Build并运行,可发现新建左边已添加一个复选标记。

默认菜单项

  在刚才的代码下,添加 GetMenu()->GetSubMenu(0)->SetDefaultItem(1, TRUE); 或者GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN, FALSE); 编译运行,会发现“打开”变成了粗体。

  注意:“打印”的索引是5,不是4。计算菜单项索引时,一定要把分割栏菜单项计算在内。并且,一个子菜单只能有一个默认菜单项。

图形标记菜单

  Insert-Resource-Bitmap,创建一个位图资源。如图。
这里写图片描述
  为CMainFrame类添加一个CBitmap类型的成员变量:m_bitmap。

  接着添加代码:
CString str;
str.Format(“x=%d”,y=%d”, GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK));
MessageBox(str);
m_bitmap.LoadBitmap(IDB_BITMAP1);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0, MF_BYPOSITION, &m_bitmap, &m_bitmap);

禁用菜单项

  通常把MF_GRAYED和MF_DISABLED这两个标志放在一起使用。不过这么做并不是必需的。
  删除之前的代码,写入 GetMenu()->GetSubMenu(0)->EnableMenuItem(1, MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
  打开“文件”子菜单,发现“打开”菜单栏变灰,点击不起作用。

移除和装载菜单

  再添加一行代码: SetMenu(NULL); 此时菜单栏被移除了。
  再添加几行代码:
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();
  此时菜单栏又装载了。

    CMenu menu;
    menu.CreateMenu();
    GetMenu()->AppendMenu(MF_POPUP, (UINT)menu.m_hMenu, "Test1");
    menu.AppendMenu(MF_STRING, 111, "Hello");
    menu.AppendMenu(MF_STRING, 112, "Bye");
    menu.AppendMenu(MF_STRING, 113, "Mybole");


    menu.Detach();

    CMenu menu1;
    menu1.CreateMenu();
    GetMenu()->InsertMenu(2, MF_POPUP | MF_BYPOSITION, (UINT)menu1. m_hMenu,"Test");


    menu1.Detach();

    GetMenu()->GetSubMenu(2)->AppendMenu(MF_STRING, 118, "Welcome");
    GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING, 114, "Welcome");
    GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN, MF_BYCOMMAND | MF_STRING, 115, "VC编程");




<div class="se-preview-section-delimiter"></div>

MFC菜单命令更新机制

这里写图片描述

  MFC命令更新机制:当要显示菜单时,操作系统发出WM_INITMENUPOPOP消息,然后由程序窗口的基类如CFrameWnd接管,它会创建一个CCmdUI对象,并与程序的第一个菜单项相关联,调用该对象的一个成员函数DoUpdate()。这个函数发出CN_UPDATE_COMMAND_UI消息,这条消息带有一个指向CCmdUI对象的指针。这时,系统会判断是否存在一个ON_UPDATE_COMMAND_UI宏去捕获这个菜单项消息。如果找到这样一个宏,就调用相应的消息响应函数进行处理,在这个函数中,可以利用传递过来的CCmdUI对象去调用相应的函数,使该菜单项可以使用,或禁用该菜单项。当更新完第一个菜单项后,同一个CCmdUI对象就设置为与第二个菜单项相关联,依此顺序进行,直到完成所有菜单项的处理。

  添加代码:

void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI) 
{
    // TODO: Add your command update UI handler code here
    pCmdUI->Enable();
}




<div class="se-preview-section-delimiter"></div>

  编辑-剪切 可用了。
  如果要把工具栏上的一个工具按钮与菜单栏中的某个菜单项相关联,只要将它们的ID设置为同一个标识就可以了。

  如果希望禁用文件-新建,为ID_FILE_NEW添加UPDATE_COMMAND_UI消息响应函数。
  代码如下:


void CMainFrame::OnUpdateFileNew(CCmdUI* pCmdUI) 
{
    // TODO: Add your command update UI handler code here
    pCmdUI->Enable(FALSE);
}




<div class="se-preview-section-delimiter"></div>

  或者

void CMainFrame::OnUpdateFileNew(CCmdUI* pCmdUI) 
{
    if (2 == pCmdUI->m_nIndex)
    pCmdUI->Enable();
}




<div class="se-preview-section-delimiter"></div>

快捷菜单

  1. 新增一个新的菜单资源。点开,顶级菜单设置任意的文本,如abc。添加两个菜单项:
  显示 IDM_SHOW
  退出 IDM_EXIT
  2. 给CMenuView类添加WM_RBUTTONDOWN消息响应函数。

void CMenu2View::OnRButtonDown(UINT nFlags, CPoint point) 
{
    CMenu menu;
    menu.LoadMenu(IDR_MENU1);
    CMenu* pPopup = menu.GetSubMenu(0);
    ClientToScreen(&point);
    pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);

    CView::OnRButtonDown(nFlags, point);
}




<div class="se-preview-section-delimiter"></div>

  效果:
  

  3.对“显示”右键ClassWizard,可以取消创建新类的询问。分别为CMainFrame类和CMenuView类添加一个响应。
  代码:

void CMenu2View::OnShow() 
{
    MessageBox("View show");    
}




<div class="se-preview-section-delimiter"></div>
void CMainFrame::OnShow() 
{
    MessageBox("Main show");
}

  结果是显示“View show”。说明只有视类才能对快捷菜单项命令做出响应。若想让CMainView类对此快捷菜单项进行响应的话,修改代码:

void CMenu2View::OnRButtonDown(UINT nFlags, CPoint point) 
{
    CMenu menu;
    menu.LoadMenu(IDR_MENU1);
    CMenu* pPopup = menu.GetSubMenu(0);
    ClientToScreen(&point);
    //pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
    pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, GetParent());

    CView::OnRButtonDown(nFlags, point);
}

  同时删去视类的显示。

动态菜单操作

### 添加菜单项目
  在CMainFrame类的OnCreate函数中添加代码:

    CMenu menu;
    menu.CreateMenu();
    GetMenu()->AppendMenu(MF_POPUP, (UINT)menu.m_hMenu, "Test");
    menu.Detach();

插入菜单项目

    CMenu menu;
    menu.CreateMenu();
    /*GetMenu()->AppendMenu(MF_POPUP, (UINT)menu.m_hMenu, "Test");
    menu.Detach();*/
    GetMenu()->InsertMenu(2, MF_POPUP | MF_BYPOSITION, (UINT)menu. m_hMenu,"Test");
    menu.Detach();

  如果要在新插入的子菜单中添加菜单项的话,同样可以使用AppendMenu函数来实现。

CMenu menu;
    menu.CreateMenu();
    /*GetMenu()->AppendMenu(MF_POPUP, (UINT)menu.m_hMenu, "Test");
    menu.Detach();*/
    GetMenu()->InsertMenu(2, MF_POPUP | MF_BYPOSITION, (UINT)menu. m_hMenu,"Test");

    menu.AppendMenu(MF_STRING, 111, "Hello");
    menu.AppendMenu(MF_STRING, 112, "Bye");
    menu.AppendMenu(MF_STRING, 113, "Mybole");
    menu.Detach();

  111、112、113是随便赋予的ID号。
  
这里写图片描述

  若要在“文件”子菜单下添加一个菜单项Welcome,再添加一行代码: GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING, 114, “Welcome”);
  若要在“文件”中的“新建”和“打开”插入一个菜单项VC编程,再添加一行代码:
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN, MF_BYCOMMAND | MF_STRING, 115, “VC编程”);

删除菜单

  删除“编辑”:在CMainFrame类的OnCreate函数最后(return之前)添加:
  GetMenu()->DeleteMenu(1, MF_BYPOSITION);
  删除“文件”下的“打开”:
  GetMenu()->GetSubMenu(0)->DeleteMenu(2, MF_BYPOSITION);

动态添加的菜单项的命令响应

  Resource.h中添加新ID

#define IDM_HELLO   111
将menu.AppendMenu(MF_STRING, 111, “Hello”); 改为 menu.AppendMenu(MF_STRING, IDM_HELLO, “Hello”);

  三部曲:
  1.    点开MainFrm.h,增加为
```C++
//{{AFX_MSG(CMainFrame)
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnShow();
    //}}AFX_MSG
    afx_msg void OnHello();
    DECLARE_MESSAGE_MAP()




<div class="se-preview-section-delimiter"></div>

  2. 点开MainFrm.cpp,增加为


BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    ON_WM_CREATE()
    ON_COMMAND(IDM_SHOW, OnShow)
    ON_COMMAND(IDM_HELLO, OnHello)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()




<div class="se-preview-section-delimiter"></div>

  3. CMainFrame类中添加

void CMainFrame::OnHello()
{
    MessageBox("Hello");
}




<div class="se-preview-section-delimiter"></div>

电话本示例程序

  删除之前写入CMainFrame类的OnCreate函数,留下原始函数。

动态添加子菜单的实现

  利用ClassWizard添加WM_CHAR消息。在Menu2View.h中添加:

private:
    int m_nIndex;
    CMenu m_menu;




<div class="se-preview-section-delimiter"></div>

在Menu2View.cpp里,添加:

CMenu2View::CMenu2View()
{
    // TODO: add construction code here
    m_nIndex = -1;
}




<div class="se-preview-section-delimiter"></div>

显示输入的字符

添加菜单项及其命令响应函数

  在资源编辑器中打开程序的菜单,在“帮助”后添加一个新菜单abc,添加4个菜单项。名称为1,ID为IDM_PHONE1,以此类推。用ClassWizard为CMenu2View类分别加上这四个菜单项的命令响应函数。
  修改CMenu2View类的头文件,如下:

protected:
    //{{AFX_MSG(CMenu2View)
    afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
    afx_msg void OnPhone1();
    afx_msg void OnPhone2();
    afx_msg void OnPhone3();
    afx_msg void OnPhone4();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()




<div class="se-preview-section-delimiter"></div>

CMenu2View.cpp中,

    //{{AFX_MSG_MAP(CMenu2View)
    ON_WM_CHAR()
    //}}AFX_MSG_MAP
    ON_COMMAND(IDM_PHONE1, OnPhone1)
    ON_COMMAND(IDM_PHONE2, OnPhone2)
    ON_COMMAND(IDM_PHONE3, OnPhone3)
    ON_COMMAND(IDM_PHONE4, OnPhone4)
    // Standard printing commands
    ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()




<div class="se-preview-section-delimiter"></div>
void CMenu2View::OnPhone1() 
{
    CClientDC dc(this);
    dc.TextOut(0, 0, m_strArray.GetAt(0));

}

void CMenu2View::OnPhone2() 
{
    CClientDC dc(this);
    dc.TextOut(0, 0, m_strArray.GetAt(1));

}

void CMenu2View::OnPhone3() 
{
    CClientDC dc(this);
    dc.TextOut(0, 0, m_strArray.GetAt(2));

}

void CMenu2View::OnPhone4() 
{
    CClientDC dc(this);
    dc.TextOut(0, 0, m_strArray.GetAt(3));

}

框架类窗口截获菜单命令消息

  右键单击CMainFrame,选择Add Virtual Functions-OnCommand,单击Add Handler,再点击Edit Existing。
这里写图片描述
  代码:

BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam) 
{
    int MenuCmdID = LOWORD(wParam);
    CMenu2View *pView = (CMenu2View *)GetActiveView();
    if (MenuCmdID >= IDM_PHONE1 && MenuCmdID < IDM_PHONE1 + pView->m_strArray.GetSize())
    {
    //MessageBox("Test");
    CClientDC dc(pView);
    dc.TextOut(0, 0, pView->m_strArray.GetAt(MenuCmdID - IDM_PHONE1));
    return TRUE;
    }
    return CFrameWnd::OnCommand(wParam, lParam);
}

  将MainFrm.cpp里添加#include “Menu2View.h” 。
  将Menu2View.cpp中的#include “Menu2Doc.h”剪切到Menu2View.h文件的前部(#endif // _MSC_VER > 1000下面)。

  最终代码:


void CMenu2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    CClientDC dc(this);
    if (0x0d == nChar)
    {
        if (0 == ++m_nIndex)
        {
            m_menu.CreatePopupMenu();
            GetParent()->GetMenu()->AppendMenu(MF_POPUP, (UINT)m_menu.m_hMenu, "PhoneBook");
            GetParent()->DrawMenuBar();
        }
        m_menu.AppendMenu(MF_STRING, IDM_PHONE1 + m_nIndex, m_strLine.Left(m_strLine.Find(" ")));
        m_strArray.Add(m_strLine);
        m_strLine.Empty();
        Invalidate();
    }
    else
    {
        m_strLine += nChar;
        dc.TextOut(0, 0, m_strLine);
    }
    CView::OnChar(nChar, nRepCnt, nFlags);
}

  效果:
这里写图片描述

MFC消息映射机制

  与消息有关的三处信息:1.头文件XXXX.h中 2.源文件XXXX.cpp中 3.源文件XXXX.cpp的响应函数中

绘制线条

  对CDrawView右键点击Add Member Variable,变量名称:m_ptOrigin,类型:CPoint,访问权限设置:Private。
  代码:

void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) 
{
    m_ptOrigin = point;
    CView::OnLButtonDown(nFlags, point);
}
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    //首先获得窗口的设备描述表
    HDC hdc;
    hdc = ::GetDC(m_hWnd);
    //移动到线条的起点
    MoveToEx(hdc, m_ptOrigin.x, m_ptOrigin.y, NULL);
    //画线
    LineTo(hdc, point.x, point.y);
    //释放设备描述表
    ::ReleaseDC(m_hWnd, hdc);

    CView::OnLButtonUp(nFlags, point);
}

这里写图片描述

利用MFC的CDC类实现画线功能

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    /*//首先获得窗口的设备描述表
    HDC hdc;
    hdc = ::GetDC(m_hWnd);
    //移动到线条的起点
    MoveToEx(hdc, m_ptOrigin.x, m_ptOrigin.y, NULL);
    //画线
    LineTo(hdc, point.x, point.y);
    //释放设备描述表
    ::ReleaseDC(m_hWnd, hdc);*/

    CDC* pDC = GetDC();
    pDC->MoveTo(m_ptOrigin);
    pDC->LineTo(point);
    ReleaseDC(pDC);

    CView::OnLButtonUp(nFlags, point);
}

利用MFC的CWindowDC类实现画线功能

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    CWindowDC dc(GetParent());
    dc.MoveTo(m_ptOrigin);
    dc.LineTo(point);

    CView::OnLButtonUp(nFlags, point);
}

这里写图片描述

在桌面窗口中画线

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    CWindowDC dc(GetDesktopWindow());
    dc.MoveTo(m_ptOrigin);
    dc.LineTo(point);

    CView::OnLButtonUp(nFlags, point);
}

这里写图片描述

  注意:在桌面上画图需要权限(一般写代码时需要避免软件以外的操作)。

绘制彩色线条

  在程序中,当构造一个GDI对象后,该对象并不会立即生效,必须选入设备描述表,它才会在以后的绘制操作中生效。
一般情况下,在完成绘图操作之后,都要利用SelectObject函数把之前的GDI对象选入设备描述表,以便使其恢复到先前的状态。

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    CPen pen(PS_SOLID, 1, RGB(255, 0, 0));
    CClientDC dc(this);
    CPen* pOldPen = dc.SelectObject(&pen);
    dc.MoveTo(m_ptOrigin);
    dc.LineTo(point);
    dc.SelectObject(pOldPen);

    CView::OnLButtonUp(nFlags, point);
}

  运行的效果是红色线条。

  改为 CPen pen(PS_DASH, 1, RGB(255, 0, 0)); 是虚线。(其中第二个参数需小于等于10)
CPen pen(PS_DOT, 1, RGB(255, 0, 0)); 是点线。

使用画刷绘图

简单画刷

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    //创建一个红色画刷
    CBrush brush(RGB(255, 0, 0));
    //创建并获得设备描述表
    CClientDC dc(this);
    //利用红色画刷填充鼠标拖拽过程中形成的矩形区域
    dc.FillRect(CRect(m_ptOrigin, point),&brush);

    CView::OnLButtonUp(nFlags, point);
}```
 ![这里写图片描述](https://img-blog.csdn.net/20170420230440190?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHVib2ppbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

### 位图画刷
  Insert-Resource-Bitmap-New,在这里发挥灵魂画手的天赋吧!
  代码:
```C++
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    //创建位图对象
    CBitmap bitmap;
    //加载位图资源
    bitmap.LoadBitmap(IDB_BITMAP1);
    //创建位图画刷
    CBrush brush(&bitmap);
    //创建并获得设备描述表
    CClientDC dc(this);
    //利用位图画刷填充鼠标拖拽过程中形成的矩形区域
    dc.FillRect(CRect(m_ptOrigin, point),&brush);

    CView::OnLButtonUp(nFlags, point);
}




<div class="se-preview-section-delimiter"></div>

这里写图片描述
  我画的是不是很滑稽(手动滑稽)

透明画刷

  先进行一种尝试:

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    //创建并获得设备描述表
    CClientDC dc(this);
    //绘制一个矩形
    dc.Rectangle(CRect(m_ptOrigin,point));

    CView::OnLButtonUp(nFlags, point);
}




<div class="se-preview-section-delimiter"></div>

这里写图片描述
  如果希望矩形内部是透明的,能够看到被遮挡的图形,就要创建一个透明画刷。

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    //创建并获得设备描述表
    CClientDC dc(this);
    //创建一个空画刷
    CBrush *pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
    //将空画刷选入设备描述表
    CBrush *pOldBrush = dc.SelectObject(pBrush);
    //绘制一个矩形
    dc.Rectangle(CRect(m_ptOrigin, point));
    //恢复先前的画刷
    dc.SelectObject(pOldBrush);

    CView::OnLButtonUp(nFlags, point);
}




<div class="se-preview-section-delimiter"></div>

这里写图片描述

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。