1.要点
- MFC中已有的控件都是使用GDI/GDI+绘制自身,因此不适合直接从这些已有控件中继承,而应当CWnd中继承,将控件所有外观绘制的工作都交给Direct2D完成;
- 重写OnEraseBkgnd()函数,返回TRUE,已通知框架,控件背景色已由Direct2D负责绘制,框架不需要再绘制背景色;
- 客户端在使用此控件时,需要在窗口初始化时修改窗口的样式为WS_CLIPCHILDREN,以防止客户端干扰控件自身的绘制。
- 在控件内部添加私有的Direct2D绘图相关的接口变量,具体的绘制过程和在窗口中绘图类似。
- 当控件被Resize或客户端设置了控件属性,控件需要立即重绘时,调用Invalidate(FALSE)。
2.一个简单Direct2D控件的实现代码
- <pre name="code" class="cpp">//D2dProgressBar.h
- #pragma once
- #include "afxwin.h"
- #include <d2d1.h>
- #include <d2d1helper.h>
- #include <d2derr.h>
- #pragma comment(lib, "d2d1.lib")
- // CD2dProgressBar
class CD2dProgressBar : public CWnd
{
public:
CD2dProgressBar(void);
~CD2dProgressBar(void);
private:
ID2D1Factory* m_pD2d1Factory;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1SolidColorBrush* m_pSolidColorBrush;
ID2D1LinearGradientBrush* m_pLinearGradientBrush;
private:
BOOL CreateDeviceIndependentResource();
BOOL CreateDeviceDependentResource();
void DiscardDeviceDependentResource();
void DestoryResource();
void Render();
void ResizeRenderTarget(int width,int height);
public:
void SetValue(int progressValue);
int GetValue(void);
private:
int m_ProgressValue;
public:
DECLARE_MESSAGE_MAP()
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
};
- // D2dProgressBar.cpp : implementation file
- //
- #include "stdafx.h"
- #include "D2dProgressBar.h"
- using namespace D2D1;
- //
- // 释放模板.
- //
- void SafeRelease(Type& pObjToRelease)
{
if(pObjToRelease)
{
pObjToRelease->Release();
pObjToRelease = 0;
}
}
// CD2dProgressBar
//D2dProgressBar.cpp
CD2dProgressBar::CD2dProgressBar(void)
:m_ProgressValue(0)
,m_pD2d1Factory(NULL)
,m_pRenderTarget(NULL)
,m_pSolidColorBrush(NULL)
,m_pLinearGradientBrush(NULL)
{
}
CD2dProgressBar::~CD2dProgressBar(void)
{
DestoryResource();
}
void CD2dProgressBar::SetValue(int progressValue)
{
ASSERT((progressValue>=0) && (progressValue<=100));
m_ProgressValue = progressValue;
//Repaint
}
int CD2dProgressBar::GetValue(void)
{
return m_ProgressValue;
}
BEGIN_MESSAGE_MAP(CD2dProgressBar, CWnd)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
ON_WM_SIZE()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
void CD2dProgressBar::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CWnd::OnPaint() for painting messages
Render();
}
BOOL CD2dProgressBar::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
return TRUE;
//return CWnd::OnEraseBkgnd(pDC);
}
BOOL CD2dProgressBar::CreateDeviceIndependentResource()
{
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,&m_pD2d1Factory);
ASSERT(!hr);
return SUCCEEDED(hr);
}
BOOL CD2dProgressBar::CreateDeviceDependentResource()
{
ASSERT(m_pD2d1Factory != NULL);
ASSERT(m_pRenderTarget == NULL);
CRect rc;
GetClientRect(&rc);
D2D1_RENDER_TARGET_PROPERTIES prop = RenderTargetProperties();
HRESULT hr = m_pD2d1Factory->CreateHwndRenderTarget(
prop,
HwndRenderTargetProperties(m_hWnd,SizeU(rc.Width(),rc.Height())),
&m_pRenderTarget);
ASSERT(!hr);
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateSolidColorBrush(ColorF(ColorF::LightSeaGreen),&m_pSolidColorBrush);
ASSERT(!hr);
ID2D1GradientStopCollection* pGradientStops = NULL;
D2D1_GRADIENT_STOP stops[2];
stops[0].color = ColorF(ColorF::Yellow);
stops[0].position = 0.0f;
stops[1].color = ColorF(ColorF::Red);
stops[1].position = 1.0f;
HRESULT hr = m_pRenderTarget->CreateGradientStopCollection(
stops,
2,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&pGradientStops);
ASSERT(!hr);
//Create linear gradient brush
hr = m_pRenderTarget->CreateLinearGradientBrush(
LinearGradientBrushProperties(Point2F(0,0),Point2F(0,40)),
pGradientStops,
&m_pLinearGradientBrush);
ASSERT(!hr);
SafeRelease(pGradientStops);
}
return SUCCEEDED(hr);
}
void CD2dProgressBar::DiscardDeviceDependentResource()
{
SafeRelease(m_pLinearGradientBrush);
SafeRelease(m_pSolidColorBrush);
SafeRelease(m_pRenderTarget);
}
void CD2dProgressBar::DestoryResource()
{
DiscardDeviceDependentResource();
SafeRelease(m_pD2d1Factory);
}
void CD2dProgressBar::Render()
{
ASSERT(m_pD2d1Factory);
if (m_pRenderTarget == NULL)
{
BOOL succeeded = CreateDeviceDependentResource();
if (!succeeded)
return;
}
if (m_pRenderTarget->CheckWindowState()& D2D1_WINDOW_STATE_OCCLUDED)
return;
CRect rc;
GetClientRect(&rc);
//rc.DeflateRect(8,8);
m_pRenderTarget->BeginDraw();
m_pRenderTarget->Clear(ColorF(ColorF::LightGray));
m_pRenderTarget->SetTransform(Matrix3x2F::Identity());
D2D1_ROUNDED_RECT boundRect = D2D1::RoundedRect(RectF(rc.left,rc.top,rc.right,rc.bottom),5,5);
int width = (rc.right-rc.left)*m_ProgressValue/100;
D2D1_ROUNDED_RECT filledRect = D2D1::RoundedRect(RectF(rc.left,rc.top,rc.left+width,rc.bottom),5,5);
//Draw the outline
m_pRenderTarget->DrawRoundedRectangle(boundRect,m_pSolidColorBrush);
//Fill the outline
m_pRenderTarget->FillRoundedRectangle(filledRect,m_pLinearGradientBrush);
HRESULT hr = m_pRenderTarget->EndDraw();
if (hr == D2DERR_RECREATE_TARGET)
{
DiscardDeviceDependentResource();
}
}
void CD2dProgressBar::ResizeRenderTarget(int width,int height)
{
if (m_pRenderTarget)
{
m_pRenderTarget->Resize(SizeU(width,height));
}
}
BOOL CD2dProgressBar::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Add your specialized code here and/or call the base class
BOOL succeeded = CreateDeviceIndependentResource();
ASSERT(succeeded);
return CWnd::PreCreateWindow(cs);
}
void CD2dProgressBar::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
ResizeRenderTarget(cx,cy);
//Repaint
}
void CD2dProgressBar::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CRect rc;
GetClientRect(&rc);
ASSERT(point.x >= rc.left && point.x <= rc.right
&& point.y >= rc.top && point.y <= rc.bottom);
m_ProgressValue = 100*(point.x - rc.left)/rc.Width();
//Repaint
CWnd::OnLButtonUp(nFlags, point);
}
3.测试所创建的控件
创建一个简单的MFC对话框,在OnInitDialog()函数中添加如下代码:
实际运行效果: