1.首先修改一下昨天的画线功能
1.1 主要修改的是获取的DC,因为 CDC 的派生类 CClientDC 、 CWindowDC 等都是自己创建好后自动调用析构函数的,不需要去手动释放,因此一般采用 CDC 的派生类来获取DC;
1.2 其次,对于CDC不同的派生类,我们可以通过改变DC的类型来进行绘制图像窗口的显示,比方说我们使用 CWindowDC 作为获得的DC类型,那么当我们在进行画线时会在整个窗口进行绘制,而不是单单在窗口上绘制;
void CPaintView::OnLButtonUp(UINT nFlags, CPoint point)
{
// 1. 昨天的画图
//CDC *pDC = this->GetDC();
//pDC->MoveTo(pointDown);
//pDC->LineTo(point);
//this->ReleaseDC(pDC);
// 1.2 对其进行修改,一般在MFC画图中使用其派生类dc来获取需要的dc,
// 因为其派生类dc在构造函数中创建dc,在析构函数中释放dc,无需手动释放,比较便利
//CClientDC pDC(this);
//pDC.MoveTo(pointDown);
//pDC.LineTo(point);
// 1.3 修改给定的窗口,可以在不同窗口画图
//CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
//CClientDC pDC(pFrame);
//pDC.MoveTo(pointDown);
//pDC.LineTo(point);
// 1.4 修改dc的类型,可以在不同区域进行画图
/*CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
CWindowDC pDC(pFrame);
pDC.MoveTo(pointDown);
pDC.LineTo(point);*/
// 修改dc并且修改窗口
CWindowDC pDC(GetDesktopWindow());
pDC.MoveTo(pointDown);
pDC.LineTo(point);
CView::OnLButtonUp(nFlags, point);
}
2.在MFC中进行贴图
2.1 首先复习Win32的贴图步骤:
1.新建一个位图资源文件;
2.获取目标DC: HDC dc = ::GetDC(this->m_hWnd) ;
3.创建一个兼容性DC: HDC tdrdc = ::CreateCompatibleDC(dc) ;
4.创建一个位图句柄: HBITMAP btmp = ::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP2)) ;
5.将位图句柄选中到兼容性DC中: ::SelectObject(tdrdc, btmp) ;
6.拷贝位图: ::BitBlt(dc, 0, 0, 100, 100, tdrdc, 0, 0, SRCCOPY) ;
7.删除兼容性DC,释放目标DC: ::DeleteDC(tdrdc) ; ::ReleaseDC(this->m_hWnd, dc) ;
void CPaintView::OnDraw(CDC* /*pDC*/)
{
CPaintDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// 2. 贴图
// 2.1 Win32的贴图方式
HDC dc = ::GetDC(this->m_hWnd);
HDC tdrdc = ::CreateCompatibleDC(dc);
HBITMAP btmp = ::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP2));
::SelectObject(tdrdc, btmp);
::BitBlt(dc, 0, 0, 100, 100, tdrdc, 0, 0, SRCCOPY);
::DeleteDC(tdrdc);
::ReleaseDC(this->m_hWnd, dc);
}
2.2 学习MFC中的贴图步骤
1.首先获取DC: CClientDC pDc(this) (这样得到的DC不用手动释放ReleaseDC);
2.通过对象来创建第三方兼容性DC: CDC tdrdc ; tdrdc.CreateCompatibleDC(&pDC) ;
3.通过对象来创建加载位图: CBitmap btmp ; btmp.LoadBitmap(IDB_BITMAP2) ;
4.选择位图到DC中: tdrdc.SelectObject(btmp) ;
5.贴图: pDC.BitBlt(0, 0, 100, 100, &tdrdc, 0, 0, SRCCOPY) 。
void CPaintView::OnDraw(CDC* /*pDC*/)
{
CPaintDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// 2.2 MFC贴图方式
CClientDC pDC(this);
CDC tdrdc;
tdrdc.CreateCompatibleDC(&pDC);
CBitmap btmp;
btmp.LoadBitmap(IDB_BITMAP2);
tdrdc.SelectObject(btmp);
pDC.BitBlt(0, 0, 100, 100, &tdrdc, 0, 0, SRCCOPY);
}
3.截屏工具
3.1 首先我们来设计该工具的界面
1.首先我们去掉状态栏:在 Frame 类中的 OnCreate 函数中删除第三个if判断及紧接着的一行代码,就可去掉状态栏;
2.设置一个空菜单:在 Frame 类中的 OnCreate 函数中添加一行代码 this->SetMenu(0) ;
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_TOOLBAR1))
{
TRACE0("未能创建工具栏\n");
return -1; // 未能创建
}
// 删除菜单栏
this->SetMenu(0);
// TODO: 如果不需要可停靠工具栏,则删除这三行
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
3.将窗口最大化,在 App 类中 InitInstance 函数中的最后找到 m_pMainWnd->ShowWindow 更改参数为: SW_MAXIMIZE 即可将窗口最大化;这时我们去掉边框,在 Frame 类的 PreCreateWindow 函数中对 cs 的 style 进行修改: cs.style = WS_POPUP (若不将窗口最大化直接去掉边框则窗口不会显示,因此需要先将窗口最大化,再去掉边框);
APP类
BOOL CShutScreenApp::InitInstance()
{
... ...
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_MAXIMIZE);
m_pMainWnd->UpdateWindow();
return TRUE;
}
Frame类
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
cs.style = WS_POPUP;
return TRUE;
}
4.将工具条放置底部,在 Frame 类中的 OnCreate 函数中的第二个if判断中,将 CBRS_TOP 更改为 CBRS_BOTTOM 即可将工具条放到界面下部;
5.创建一个新的工具条用于截屏工具的工具条,首先添加一个 ToolBar 资源,绘制一个“X”用于退出截屏工具的按钮;其次在 Frame 类中的 OnCreate 函数中的第二个if判断中,将 LoadToolBar 的参数进行修改,修改为自己新建的工具条的 ID 即可;
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
... ...
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_BOTTOM | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_TOOLBAR1))
... ...
}
6.给“X”按键添加退出的消息按钮:这需要我们给“X”按钮添加一个消息,我们需要在 Frame 类中添加一个自定义的 ON_COMMAND 消息用于处理退出命令, ON_COMMAND 的第一个参数是“X”按钮的 ID ,第二个参数是发送退出命令的函数的地址: &CMainFrame::OnClickExit ,我们给 Frame 类添加一个成员函数: afx_msg void OnClickExit() ;在该函数中进行发送关闭命令: this->PostMessage(WM_CLOSE) (本来考虑是使用 ::PostQuitMessage(0) 这个函数的,但是只单纯的发送退出消息会导致内存泄漏,因此使用 this->PostMessage(WM_CLOSE) 来避免内存泄漏;其次,不使用 SendMessage 的原因是发送消息之后可能还有其他步骤未执行,需要向队列中投递关闭消息再执行其他语句因此选择 PostMessage );
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_COMMAND(ID_TOOLBAR_EXIT, &CMainFrame::OnClickExit)
END_MESSAGE_MAP()
void CMainFrame::OnClickExit()
{
this->PostMessage(WM_CLOSE);
}
7.添加一个按“ESC”键退出的功能,首先由于按“ESC”键发生在 View 类中,因此我们需要在 View 类中添加一个按键按下消息处理函数 OnKeyDown ;在这个函数中,我们首先判断传入的按键是不是“ESC”,若是则发送关闭命令,否则忽略;
void CShutScreenView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// 添加一个功能:按下esc键退出程序
if (nChar == VK_ESCAPE)
{
CMainFrame *pFrame = (CMainFrame*)AfxGetMainWnd(); // 获取 Frame 窗口的原因是我们是需要对主窗口 Frame 进行关闭的,而不是为了关闭某个副窗口,因此需要先获得Frame的指针
pFrame->PostMessage(WM_CLOSE);
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}