Skip to content

WIndowsMFC 3.0 ?


提到了虚函数WIndows 的DrawITem 下面是微软官方使用例

c++
void MyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)  // 触发自绘 消息就不用 关心Print消息了
{
    UINT uStyle = DFCS_BUTTONPUSH;

    // This code only works with buttons.
    ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON);

    // If drawing selected, add the pushed style to DrawFrameControl.
    if (lpDrawItemStruct->itemState & ODS_SELECTED)
        uStyle |= DFCS_PUSHED;

    // Draw the button frame.
    ::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem,
        DFC_BUTTON, uStyle);

    // Get the button's text.
    CString strText;
    GetWindowText(strText);

    // Draw the button text using the text color red.
    COLORREF crOldColor = ::SetTextColor(lpDrawItemStruct->hDC, RGB(255, 0, 0));
    ::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(),
        &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
    ::SetTextColor(lpDrawItemStruct->hDC, crOldColor);
}

当然了这只是演示


c++
void MyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)  // 触发自绘 消息就不用 关心Print消息了
{
    if (lpDrawItemStruct->itemState & ODS_SELECTED)  // 判断状态 是否点击
    {
        CDC dc;
        dc.Attach(lpDrawItemStruct->hDC);  // DC 加载到hDC 里面
        CBitmap BitMap;
        CDC bdc;
        BitMap.LoadBitmap(IDB_BITMAP3);
        bdc.CreateCompatibleDC(&dc);
        bdc.SelectObject(BitMap);
        BITMAP map;
        BitMap.GetBitmap(&map);
        dc.BitBlt(0, 0, map.bmWidth, map.bmHeight, &bdc, 0, map.bmWidth * 1, SRCCOPY);
    }
    else {
        CDC dc;
        dc.Attach(lpDrawItemStruct->hDC);  // DC 加载到hDC 里面
        CBitmap BitMap;
        CDC bdc;
        BitMap.LoadBitmap(IDB_BITMAP3);
        bdc.CreateCompatibleDC(&dc);
        bdc.SelectObject(BitMap);
        BITMAP map;
        BitMap.GetBitmap(&map);
        dc.BitBlt(0,0,map.bmWidth,map.bmHeight,&bdc,0,map.bmWidth*0,SRCCOPY);
    }
}

采用了上上章写的 绘制俄罗斯方块 就是那个PAINT消息 只不过是用在了虚函数DrawItem 里面


可以使用 手动设置CheckBox 来进行按块操作

可以在MyButton 设置Private 类 Bool lsMyButton = false; 先进行初始化 然后在ODS_ELECTED那个判断里进行 取反

c++
f (lpDrawItemStruct->itemState & ODS_SELECTED)  // 判断状态 是否点击
{
        dc.BitBlt(0, 0, map.bmWidth, map.bmHeight, &bdc, 0, map.bmWidth * 1, SRCCOPY);
		IsMyButton = true;    
}
    else if (IsMyButton ){
 
        dc.BitBlt(0,0,map.bmWidth,map.bmHeight,&bdc,0,map.bmWidth*0,SRCCOPY);
    }
}

以下为最终版本

c++
 CDC dc;
 dc.Attach(lpDrawItemStruct->hDC);  // DC 加载到hDC 里面
 CBitmap BitMap;
 CDC bdc;
 BitMap.LoadBitmap(IDB_BITMAP3);
 bdc.CreateCompatibleDC(&dc);
 bdc.SelectObject(BitMap);
 BITMAP map;
 BitMap.GetBitmap(&map);
 if (lpDrawItemStruct->itemState & ODS_SELECTED){  // 判断状态
     dc.StretchBlt(0, 0, 32, 32,&bdc, 0, map.bmWidth * 0, 16,16,SRCCOPY);
     MyIsCheck = !MyIsCheck;
 }
 else if (MyIsCheck) {
     dc.StretchBlt(0, 0, 32,32, &bdc, 0, map.bmWidth * 3,16,16,SRCCOPY);
 }
 else {
     dc.StretchBlt(0, 0, 32, 32, &bdc, 0, map.bmWidth * 0,16,16, SRCCOPY);
 }

动态子类化

实现用其他的Button 按钮实现动态子类化 (点击其他按钮触发扫雷的 控件)

现在需要知道一个方法 那就是 对象转为句柄

c++
HWND hMybutton = GetDlgItem(IDOK)->GetSafeHwnd();   //将对象转句柄

当新建一个新类RANGE的宏必须要放在最底下mineDlg.cpp的宏

c++
// 位置mineDlg.cpp
BEGIN_MESSAGE_MAP(CMineDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_QUERYDRAGICON()
	ON_COMMAND(IDM_START, &CMineDlg::OnStart)
	ON_WM_DESTROY()
	ON_BN_CLICKED(IDC_OK, &CMineDlg::OnBnClickedOk)
	ON_COMMAND_RANGE(1000, 2000, &CMineDlg::OnCommandRange)  // 这个必须要放在最底下
END_MESSAGE_MAP()

如果把ON_COMMAND_RANGE 宏放在前面那么后面的宏都会失效


写入

c++
void CMineDlg::OnBnClickedOk()
{
	HWND hWnd = GetDlgItem(IDC_OK)->GetSafeHwnd();  // 对象转句柄
	MyButton* pObj = new MyButton(); //分配
	pObj->SubclassWindow(hWnd); //传入窗口句柄
}

还有 就是SubClassWindow不能创建两次否则会触发断言

c++
ASSERT(m_hWnd == NULL);     // only attach once, detach on destroy

官方说明 只能发送一次 所以我们需要做判断

c++
static MyButton button; // 没有delete只能使用静态
if(button.SubClassWindow != NULL)
    return;

要允许自绘了

c++
static MyButton button;
	HWND GhWnd = GetSafeHwnd();
	HWND hWnd = GetDlgItem(IDC_OK)->GetSafeHwnd();
	if (button.GetSafeHwnd() != NULL) {
		return;
	}
	button.SubclassWindow(hWnd); //传入窗口句柄
	button.SetButtonStyle(BS_OWNERDRAW,1);  //有hWnd后允许自绘

这样Button的按钮就会改变

这是将对象转为句柄 ,接下来是句柄转为对象


句柄转为对象

首先是最简单的 CWnd cWnd 一下 构造给CWnd

c++
CWnd cWnd;s
cWnd.attach(hWnd)  //将句柄附加到对象

复杂的 必须是MFC创建的

c++
CWnd* pWnd = cWnd.FromHandle(hWnd); //返回指向cWnd对象的指针 , 如果没有创建成功 则创建一个临时cWnd的对象

创建一个小窗

c++
 
void CMineDlg::OnAbout()
{
	CAboutDlg  Dlg;
	Dlg.DoModal(); //调用此成员函数以调用模式对话框,并在完成后返回对话框结果。 快速调用
}

在这个对话框里使用控件

正常来说就是GetDlgItem(nID) 对吧 但是麻烦 所以呢就有一个新的获取方法 叫做数据变化绑定

  • 打开刚才定制DLG的界面
  • 在右键类向导 找到成员变量 (提前新增一个BUTTON一个 EDIT)
  • 选择成员变量设置为CString 随便弄个名字我设置的m_csEDIT01(cs的格式类里面)
  • 然后就可以使用了
c++
	CString m_csEdit01;  //设置的结果

为了证明是时刻变化的 我们设置按钮 里 获取 EDIT的值 但是 需要UpdateData更新

c++
void CAboutDlg::OnBnClickedButton1()
{	//数据变化绑定  

	
	UpdateData(TRUE);  // 更新检索
	AfxMessageBox(m_csEdit01);
	
	// 更改EDIT的值
	m_csEdit01 = "ccccccccxk!";  // 修改数据
	UpdateData(FALSE);  //不更新 初始化
}

UpdateData (BOOL) 里面的值 是指示是正在初始化对话框 ( FALSE ) 还是正在检索数据 ( TRUE ) 的标志。

设置多个 也可以实现 按按钮同时修改标题 也可以

c++

void CAboutDlg::OnBnClickedButton1()
{//数据变化绑定  

	m_Button1.SetWindowText("DDF");  // 修改标题
	UpdateData(TRUE);  // 更新检索
	AfxMessageBox(m_csEdit01);
	AfxMessageBox(m_csEDIT2);
	
	m_csEdit01 = "ccccccccxk!";  // 修改数据
	m_csEDIT2 = "ctrl!";  // 修改数据
	UpdateData(FALSE);  //不更新 初始化
}
===

这样就可以实现了 UpdateData 是重要的东西

UpdateData其实调用的是 F12一下就可以或者查看调用堆栈就行

c++
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT, m_csEdit01);
	DDX_Text(pDX, IDC_EDIT02, m_csEDIT2);
	DDX_Check(pDX, IDC_CHECK1, m_Check1);
	DDX_Control(pDX, IDC_BUTTON1, m_Button1);
}

发现是Sub一个窗口

END!!!!