MFC综合实验二学习记录

文章目录

    • 虚函数和纯虚函数的区别?
    • MFC中什么是UPDATE_COMMAND_UI 消息
    • 如何查看控件对应的成员变量
    • 模态对话框的理解
    • HGDIOBJ" 类型的值不能用于初始化 "CBrush *" 类型的实体错误
    • MFC编程中CDC类型和HDC类型有什么区别?
    • 关于WIDING和ALTERNATE填充方式的问题

这是MFC中CdcdrawView函数中的消息映射宏,我的问题是如何找到ID_SET对应的是什么菜单项资源


MFC综合实验二学习记录_第1张图片


最后的解决方案比较简单,只需要使用 Ctrl + F,然后输入 ID_SET 来搜索,注意到要调整搜索范围为当前项目,就可以找到对应的文件了!


MFC综合实验二学习记录_第2张图片
MFC综合实验二学习记录_第3张图片


注意要点击查找全部,可以看到搜索到的结果完美的符合要求, ID_SET是设置菜单项对应的ID。


MFC综合实验二学习记录_第4张图片


虚函数和纯虚函数的区别?

虚函数(Virtual Function)

定义:虚函数是在基类中使用关键字 virtual 声明的函数。它可以在派生类中被重写(Override)。

实现:虚函数在基类中通常有具体的实现。这意味着即使派生类没有重写这个函数,也可以使用基类中的实现。

目的:虚函数的主要目的是允许在派生类中对基类方法进行扩展或修改,同时使用基类指针或引用来实现多态。

示例

class Base {
public:
    virtual void display() {
        std::cout << "Display of Base" << std::endl;
    }
};

多态:通过基类指针或引用调用虚函数时,会根据对象的实际类型来调用相应的函数实现,实现多态。

纯虚函数(Pure Virtual Function)

定义:纯虚函数是在基类中使用 virtual 关键字声明的,但没有提供任何实现,并以 = 0 结尾。例如,virtual void display() = 0;

实现:纯虚函数在基类中没有实现,派生类必须重写这个函数,除非派生类也是抽象类。

目的:纯虚函数的主要目的是定义一个接口,强制派生类实现特定的函数。这是创建抽象基类(即不能直接实例化的类)的一种方式。

示例

class Base {
public:
    virtual void display() = 0; // 纯虚函数
};

抽象类:包含至少一个纯虚函数的类称为抽象类。不能创建抽象类的实例。这是因为抽象类定义了一个尚未完全实现的接口。

总结

虚函数允许在基类中有一个默认实现,而派生类可以选择是否提供新的实现。

纯虚函数不在基类中提供实现,强制派生类提供自己的实现。包含纯虚函数的类成为抽象类,无法实例化。


MFC中什么是UPDATE_COMMAND_UI 消息


MFC综合实验二学习记录_第5张图片

在 MFC (Microsoft Foundation Classes) 应用程序中,UPDATE_COMMAND_UI 消息是一种用于实时更新用户界面元素状态的机制。它不是由用户直接操作触发的,而是由框架在特定时刻自动发送,用于查询命令(如菜单项、工具栏按钮等)的当前状态。这允许程序动态地调整用户界面元素,以反映当前的应用程序逻辑和数据状态。

作用
UPDATE_COMMAND_UI 消息的主要作用是维护用户界面的一致性和正确性。例如,根据当前文档的状态,一个菜单项可能需要被启用或禁用,或者一个工具栏按钮可能需要显示为被选中或未选中状态。

如何工作
当 MFC 应用程序的主消息循环运行时,框架会定期发送 UPDATE_COMMAND_UI 消息给所有的用户界面元素(如菜单项和工具栏按钮)。接收到这个消息的用户界面元素需要决定自己的显示状态。这通常涉及以下几个方面:

是否启用:元素是否可用或灰显(禁用)。
是否选中:例如,切换按钮的选中/未选中状态。
其他视觉状态:如文本标签的变更等。

示例
假设您的应用程序有一个 “保存” 菜单项,您希望在文档被修改后启用它,在文档未修改时禁用它。您可以添加一个处理 UPDATE_COMMAND_UI 消息的函数来实现这一点:

void CMyApp::OnUpdateFileSave(CCmdUI* pCmdUI)
{
    // 检查文档是否已修改
    bool isModified = /* 获取文档状态 */;
    
    // 根据文档的修改状态启用或禁用 "保存" 菜单项
    pCmdUI->Enable(isModified);
}

在这个例子中,每当用户界面需要更新时(例如,当用户编辑文档时),这个函数会根据文档的当前修改状态来启用或禁用 “保存” 菜单项。



如何查看控件对应的成员变量


打开类向导中的对话框类对,可以看到成员变量和对应的控件ID。


MFC综合实验二学习记录_第6张图片

模态对话框的理解

HGDIOBJ" 类型的值不能用于初始化 “CBrush *” 类型的实体错误

在CGraph类的绘制函数中,在选中画笔画刷的过程中出现了一点小问题,错误是

严重性 代码 说明 项目 文件 行 禁止显示状态
错误(活动) E0144 “HGDIOBJ” 类型的值不能用于初始化 “CBrush *” 类型的实体 dcdraw E:\LearningMaterial\大四上\OO报告\综合实验二\202000800341_XXX_综合2_1\CGraph.cpp 448

	//选中画笔画刷
	CPen* pOldpen=pDC->SelectObject(&bpen);
	if (bfill) { //填充
		CBrush* pOldBrush=pDC->SelectObject(&br);
	}
	else{
		CBrush* pOldBrush= pDC->SelectObject(GetStockObject(NULL_BRUSH));
	}

可以看到上下两行的画刷选中代码几乎一致,为什么会出现这样的问题呢?

在 MFC 中,CDC::SelectObject 方法期望一个与设备上下文相关的对象(例如 CBrushCPen 等),但是 GetStockObject 函数返回的是一个 HGDIOBJ一个更通用的GDI对象句柄)。HGDIOBJ 是 Windows API 中的一个术语,是 “Handle to a GDI Object” 的缩写。

因此,从 HGDIOBJ 转换到 CBrush* 需要显式的类型转换,而不是隐式的。修改成如下的形式,即没有问题。

//选中画笔画刷
	CPen* pOldpen=pDC->SelectObject(&bpen);
	if (bfill) { //填充
		CBrush* pOldBrush=pDC->SelectObject(&br);
	}
	else{
		CBrush* pOldBrush= (CBrush*)pDC->SelectObject(GetStockObject(NULL_BRUSH));
	}

MFC编程中CDC类型和HDC类型有什么区别?

在Windows编程中,CDC 和 HDC 是两种不同的,但相关的概念,它们都用于图形绘制,但在不同层面上提供功能:

  1. HDC (Handle to Device Context):
  • HDC 是一个指向设备上下文的句柄。在Windows API(特别是在底层的C或C++编程中)中使用。
  • 它是一个低层次的图形绘制接口,提供了直接访问GDI(图形设备接口)的功能。
  • HDC 可以用于屏幕、内存位图、打印机等多种绘图表面。
  • 使用 HDC 通常涉及直接的Windows API调用,例如 MoveToEx, LineTo, TextOut, Ellipse 等。
  1. CDC (Class for Device Context):
  • CDC 是MFC(Microsoft Foundation Classes)框架中的一个类,它封装了 HDC。
  • 它是一个更高层次的、面向对象的接口,使得在使用MFC编写的C++程序中绘图变得更简单和更直观。
  • CDC 提供了更多的安全性和易用性,因为它自动管理资源,如选择和取消选择GDI对象(例如画笔、字体)。
  • CDC 类包含了多个用于绘图的成员函数,这些函数实际上是对 HDC 函数的封装。例如,CDC::LineTo 是 LineTo 的封装。

简而言之,HDC 是一个更接近于Windows底层的、基于句柄的设备上下文表示,而 CDC 是MFC框架中的一个类,它提供了一个更高级别和更面向对象的接口来处理设备上下文。在MFC程序中,通常使用 CDC 而不是直接操作 HDC,因为它简化了代码并提高了安全性。但在底层的WinAPI编程中,HDC 是必不可少的。


关于WIDING和ALTERNATE填充方式的问题

这个问题其实并不重要,但是没想到随便一搜迟迟难以找到答案,给我精神造成了极大的打击,务必要记录下来。

  1. ALTERANATE:从左到右水平扫描!系统只填充每个扫描行的多边行的奇数边到偶数边的部分,不填充偶数边到奇数边的部分;
    MFC综合实验二学习记录_第7张图片

MSDN中的原话如下:

When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and even-numbered polygon sides on each scan line. That is, GDI fills the area between the first and second side, between the third and fourth side, and so on.

水平扫描

就是显示器上的从左到右一行一行的水平扫描,假如你客户区大小是 500 × 600 500\times600 500×600,那就是从 ( 0 , 0 ) (0,0) (0,0) ( 500 , 0 ) (500,0) (500,0) 这是第一次水平扫描,…一直到 ( 0 , 600 ) (0,600) (0,600) ( 500 , 600 ) (500,600) (500,600)。共扫描了600次就扫描完了客户

奇数边和偶数边

这里说的奇数和偶数边完全是相对而言的啊,只是我们便于理解的一种方法,就是说在水平扫描一行时,第一次遇到对线就把它定义为第一条边(注意:同一条线的话在不同的扫描行上相对的边可能不是一样的,这点一定要注意思了),第二次遇到的线就是第二条边,以此类推;那么对于一个矩形( 就单单一个矩形),左是第一条边,右是第二条边,上下它就什么都不是了,不会存在第三条边和第四条边的.

  1. WINDING模式下:填充奇数边到偶数边和ALTERANATE模式一样,但填充偶数边到奇数边的部分就不同了,你必须记主画线的方向,你可以取一个方向为正向,用一个计数器 cnt = 0,当线经过正向时cnt就加1,反向时cnt就减1,如果最后cnt为0就填充这个区域,不为0就不填充!

MSDN中的原话如下:

When the fill mode is WINDING, GDI fills any region that has a nonzero winding value. This value is defined as the number of times a pen used to draw the polygon would go around the region. The direction of each edge of the polygon is important.

MFC综合实验二学习记录_第8张图片
这是WIDING模式下的填充结果,可以看到存在奇数边到偶数边问题的只有4和5。

  • 我们首先观察4,根据从区域4得到的射线,设定正方向为顺时针,经过的第一条边-1,第二条边+1, cnt = 0,所以区域4不填充
  • 接着从区域5观察,设定正方向为顺时针,经过的第一条边+1,第二条边+1,所以区域5填充

一种简单的理解方式是看能不能环绕成功,能环绕成功则可以填充,下图五角星是一笔画绘制的,可以自己思考一下

MFC综合实验二学习记录_第9张图片

你可能感兴趣的:(mfc,学习,c++)