1.要点
- MFC中已有的控件都是使用GDI/GDI+绘制自身,因此不适合直接从这些已有控件中继承,而应当CWnd中继承,将控件所有外观绘制的工作都交给Direct2D完成;
- 重写OnEraseBkgnd()函数,返回TRUE,已通知框架,控件背景色已由Direct2D负责绘制,框架不需要再绘制背景色;
- 客户端在使用此控件时,需要在窗口初始化时修改窗口的样式为WS_CLIPCHILDREN,以防止客户端干扰控件自身的绘制。
- 在控件内部添加私有的Direct2D绘图相关的接口变量,具体的绘制过程和在窗口中绘图类似。
- 当控件被Resize或客户端设置了控件属性,控件需要立即重绘时,调用Invalidate(FALSE)。
2.一个简单Direct2D控件的实现代码
1: //D2dProgressBar.h2: #pragma once3:4: #include "afxwin.h"5: #include "D2dPrerequisite.h"6:7: class CD2dProgressBar : public CWnd8: {9: public:10: CD2dProgressBar(void);11: ~CD2dProgressBar(void);12:13: private:14: ID2D1Factory* m_pD2d1Factory;15: ID2D1HwndRenderTarget* m_pRenderTarget;16: ID2D1SolidColorBrush* m_pSolidColorBrush;17: ID2D1LinearGradientBrush* m_pLinearGradientBrush;18:19: private:20: BOOL CreateDeviceIndependentResource();21: BOOL CreateDeviceDependentResource();22: void DiscardDeviceDependentResource();23: void DestoryResource();24:25: void Render();26: void ResizeRenderTarget(int width,int height);27:28: public:29: void SetValue(int progressValue);30: int GetValue(void);31:32: private:33: int m_ProgressValue;34:35: public:36: DECLARE_MESSAGE_MAP()37: afx_msg void OnPaint();38: afx_msg BOOL OnEraseBkgnd(CDC* pDC);39: afx_msg void OnSize(UINT nType, int cx, int cy);40: afx_msg void OnLButtonUp(UINT nFlags, CPoint point);41: virtual BOOL PreCreateWindow(CREATESTRUCT& cs);42: };
1: //D2dProgressBar.cpp2: #include "StdAfx.h"3: #include "D2dProgressBar.h"4: using namespace D2D1;5:6: CD2dProgressBar::CD2dProgressBar(void)7: :m_ProgressValue(0)8: ,m_pD2d1Factory(NULL)9: ,m_pRenderTarget(NULL)10: ,m_pSolidColorBrush(NULL)11: ,m_pLinearGradientBrush(NULL)12: {13: }14:15: CD2dProgressBar::~CD2dProgressBar(void)16: {17: DestoryResource();18: }19:20: void CD2dProgressBar::SetValue(int progressValue)21: {22: ASSERT((progressValue>=0) && (progressValue<=100));23: m_ProgressValue = progressValue;24: Invalidate(FALSE); //Repaint25: }26:27: int CD2dProgressBar::GetValue(void)28: {29: return m_ProgressValue;30: }31:32: BEGIN_MESSAGE_MAP(CD2dProgressBar, CWnd)33: ON_WM_PAINT()34: ON_WM_ERASEBKGND()35: ON_WM_SIZE()36: ON_WM_LBUTTONUP()37: END_MESSAGE_MAP()38:39: void CD2dProgressBar::OnPaint()40: {41: CPaintDC dc(this); // device context for painting42: // TODO: Add your message handler code here43: // Do not call CWnd::OnPaint() for painting messages44: Render();45: }46:47: BOOL CD2dProgressBar::OnEraseBkgnd(CDC* pDC)48: {49: // TODO: Add your message handler code here and/or call default50: return TRUE;51:52: //return CWnd::OnEraseBkgnd(pDC);53: }54:55: BOOL CD2dProgressBar::CreateDeviceIndependentResource()56: {57: HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,&m_pD2d1Factory);58: ASSERTHR(hr);59: return SUCCEEDED(hr);60: }61:62: BOOL CD2dProgressBar::CreateDeviceDependentResource()63: {64: ASSERT(m_pD2d1Factory != NULL);65: ASSERT(m_pRenderTarget == NULL);66:67: CRect rc;68: GetClientRect(&rc);69: D2D1_RENDER_TARGET_PROPERTIES prop = RenderTargetProperties();70: HRESULT hr = m_pD2d1Factory->CreateHwndRenderTarget(71: prop,72: HwndRenderTargetProperties(m_hWnd,SizeU(rc.Width(),rc.Height())),73: &m_pRenderTarget);74: ASSERTHR(hr);75: if (SUCCEEDED(hr))76: {77: hr = m_pRenderTarget->CreateSolidColorBrush(ColorF(ColorF::LightSeaGreen),&m_pSolidColorBrush);78: ASSERTHR(hr);79:80: ID2D1GradientStopCollection* pGradientStops = NULL;81: D2D1_GRADIENT_STOP stops[2];82: stops[0].color = ColorF(ColorF::Yellow);83: stops[0].position = 0.0f;84: stops[1].color = ColorF(ColorF::Red);85: stops[1].position = 1.0f;86: HRESULT hr = m_pRenderTarget->CreateGradientStopCollection(87: stops,88: 2,89: D2D1_GAMMA_2_2,90: D2D1_EXTEND_MODE_CLAMP,91: &pGradientStops);92: ASSERTHR(hr);93:94: //Create linear gradient brush95: hr = m_pRenderTarget->CreateLinearGradientBrush(96: LinearGradientBrushProperties(Point2F(0,0),Point2F(0,40)),97: pGradientStops,98: &m_pLinearGradientBrush);99: ASSERTHR(hr);100:101: SafeRelease(&pGradientStops);102: }103: return SUCCEEDED(hr);104: }105:106: void CD2dProgressBar::DiscardDeviceDependentResource()107: {108: SafeRelease(&m_pLinearGradientBrush);109: SafeRelease(&m_pSolidColorBrush);110: SafeRelease(&m_pRenderTarget);111: }112:113: void CD2dProgressBar::DestoryResource()114: {115: DiscardDeviceDependentResource();116:117: SafeRelease(&m_pD2d1Factory);118: }119:120: void CD2dProgressBar::Render()121: {122: ASSERT(m_pD2d1Factory);123: if (m_pRenderTarget == NULL)124: {125: BOOL succeeded = CreateDeviceDependentResource();126: if (!succeeded)127: return;128: }129:130: if (m_pRenderTarget->CheckWindowState()& D2D1_WINDOW_STATE_OCCLUDED)131: return;132:133: CRect rc;134: GetClientRect(&rc);135: //rc.DeflateRect(8,8);136:137: m_pRenderTarget->BeginDraw();138: m_pRenderTarget->Clear(ColorF(ColorF::LightGray));139: m_pRenderTarget->SetTransform(Matrix3x2F::Identity());140: D2D1_ROUNDED_RECT boundRect = D2D1::RoundedRect(RectF(rc.left,rc.top,rc.right,rc.bottom),5,5);141: int width = (rc.right-rc.left)*m_ProgressValue/100;142: D2D1_ROUNDED_RECT filledRect = D2D1::RoundedRect(RectF(rc.left,rc.top,rc.left+width,rc.bottom),5,5);143: //Draw the outline144: m_pRenderTarget->DrawRoundedRectangle(boundRect,m_pSolidColorBrush);145: //Fill the outline146: m_pRenderTarget->FillRoundedRectangle(filledRect,m_pLinearGradientBrush);147: HRESULT hr = m_pRenderTarget->EndDraw();148: if (hr == D2DERR_RECREATE_TARGET)149: {150: DiscardDeviceDependentResource();151: }152: }153:154: void CD2dProgressBar::ResizeRenderTarget(int width,int height)155: {156: if (m_pRenderTarget)157: {158: m_pRenderTarget->Resize(SizeU(width,height));159: }160: }161:162: BOOL CD2dProgressBar::PreCreateWindow(CREATESTRUCT& cs)163: {164: // TODO: Add your specialized code here and/or call the base class165: BOOL succeeded = CreateDeviceIndependentResource();166: ASSERT(succeeded);167:168: return CWnd::PreCreateWindow(cs);169: }170:171: void CD2dProgressBar::OnSize(UINT nType, int cx, int cy)172: {173: CWnd::OnSize(nType, cx, cy);174:175: // TODO: Add your message handler code here176: ResizeRenderTarget(cx,cy);177: Invalidate(FALSE); //Repaint178: }179:180: void CD2dProgressBar::OnLButtonUp(UINT nFlags, CPoint point)181: {182: // TODO: Add your message handler code here and/or call default183: CRect rc;184: GetClientRect(&rc);185: ASSERT(point.x >= rc.left && point.x <= rc.right186: && point.y >= rc.top && point.y <= rc.bottom);187:188: m_ProgressValue = 100*(point.x - rc.left)/rc.Width();189: Invalidate(FALSE); //Repaint190:191: CWnd::OnLButtonUp(nFlags, point);192: }
3.测试所创建的控件
创建一个简单的MFC对话框,在OnInitDialog()函数中添加如下代码:
1: // TODO: Add extra initialization here2: this->ModifyStyle(0,WS_CLIPCHILDREN); //Modify the window style of the parent window3:4: //Create our Direct2D control and set it's properties5: m_ProgressBar.Create(NULL,_T(""),WS_CHILD|WS_VISIBLE,CRect(50,50,150,100),this,1234);6: m_ProgressBar.SetValue(40);
实际运行效果: