void CScoresView::DrawScore(CDC *dc,std::vector<double>,int number)
在图形绘制前,我们先限定整个直方图的大小。先创建一个矩形对象,并用GetClientRect函数获取当前客户区的大小。
函数 |
功能 |
GetClientRect |
获取当前客户区的大小 |
DeflateRect(int x,int y) |
朝CRect的中心移动以缩小,矩形宽度缩小2x,高度缩小2y |
CRect rc;
GetClientRect(rc);
rc.DeflateRect(40, 40);
2.画笔预设
在MFC中,CGdiObject类是GDI(图形设备接口)对象的基类,有以下派生类:CBitmap、CPalette、CRgn、CBrush、CFont和CPen,我们在此了解CBrush,CPen。
2.1 CBrush类
CBrush类的设置,可以对矩形进行填充。
函数 |
功能 |
CBrush 标识符(画刷类型,颜色RGB) |
创建画刷 |
dc.SelectObject(画刷) |
选择画刷作当前画刷,返回旧画刷 |
颜色一般用16进制表示,如#66CCFF,转化颜色值就是(96, 192, 255)
画刷类型:

2.2 CPen类
CPen类的设置,用于绘制矩形的边缘。
函数 |
功能 |
CPen 标识符(笔的类型,宽度,颜色RGB) |
创建画笔 |
dc.SelectObject(画笔) |
选择画笔作为当前画笔,返回旧画笔 |
画笔类型:

一些注意事项详细可参考:https://baike.baidu.com/item/CPen/7827245?fr=aladdin
内框线的特点:https://www.cnblogs.com/cgwolver/archive/2009/12/29/1635358.html
2.3 本例代码
CBrush brush1(HS_BDIAGONAL, RGB(96, 192, 255));
CBrush brush2(HS_FDIAGONAL, RGB(96, 192, 255));
CPen pen(PS_INSIDEFRAME, 2, RGB(96, 192, 255));
3.数据预处理
在绘制直方图过程中,我们需要知道区间的宽度为多少,直方图中一个人代表的矩形高度为多少,接下来我们就先处理这些数据。
int n = 5;
int width = rc.Width() / n;
int s[] = { 0,0,0,0,0 };
for (int i = 0; i < number; i++)
{
int td = (int)db[i] / 10;
if (td < 6) s[0]++;
else if (td < 7) s[1]++;
else if (td < 8) s[2]++;
else if (td < 9) s[3]++;
else s[4]++;
}
int max_s = s[0];
for (int i = 0; i < n; i++)
if (max_s < s[i]) max_s = s[i];
int per_Height = rc.Height() / max_s;
4.绘制直方图
4.1CString类型
CString类型是MFC中最常见的类之一,用于封装字符串数据结构。字符串的连接可以用+,+=号实现,
函数 |
功能 |
CString str0(const CString& str1) |
将str1复制到str0 |
CString str0(const CString& str1,n) |
将str1的前n个字符复制到str0 |
str0.Format(_T("%d"),i) |
将int类型的i以十进制转化为CString并赋值str0 |
int i = _ttoi(str) |
将str转化为int类型并赋值给i |
float f =_ttof(str) |
将str转化为float类型并赋值给f |
Format函数中的格式字符
格式字符 |
含义 |
%c |
单个字符 |
%d |
十进制整型(int) |
%ld |
十进制长整型(long) |
%f |
十进制浮点数(float) |
%lf |
十进制浮点数(double) |
%o |
八进制 |
%x |
十六进制 |
%s |
字符串 |
%u |
无符号十进制数 |
_T是使用宏,自动加载当前环境的字符环境(Unicode或其他),是当前代码具有可移植性。
4.2绘制直方图相关函数
函数 |
功能 |
CRect rect(rc) |
用矩形rc来初始化rect,rect获得rc的参数 |
rect.OffsetRect(int x,int y) |
将rect向右移动x,向下移动y(负值反向) |
dc->Rectangle(rect) |
dc为cdc *类型,在客户区绘制rect矩形 |
dc->DrawText(str,rect,格式) |
在矩形位置写入格式化文本 |
DrawText中的常用格式
格式符号 |
意义 |
DT_BOTTOM |
将正文调整到矩形底部。此值必须和DT_SINGLELINE组合。 |
DT_SINGLELINE |
显示正文的同一行 |
DT_CENTER |
使正文在矩形中水平居中 |
DT_VCENTER |
使正文在矩形中垂直居中 |
DT_LEFT |
正文左对齐 |
DT_RIGHT |
正文右对齐 |
DT_TOP |
正文顶端对齐 |
详细参考:https://www.cnblogs.com/spiritofcloud/p/3982652.html
CRect ps_rect(rc);
CRect str_rect(rc);
ps_rect.right = ps_rect.left + width;
CBrush* oldBrush = dc->SelectObject(&brush1);
CPen* oldPen = dc->SelectObject(&pen);
CString str[5] = { _T("<60"),_T("60-70"),_T("70-80"),_T("80-90"),_T(">=90") };
for(int i=0;i<n;i++)
{
ps_rect.top = ps_rect.bottom - per_Height * s[i] - 2;
if (i % 2) dc->SelectObject(&brush2);
else dc->SelectObject(&brush1);
dc->Rectangle(ps_rect);
if (s[i] > 0)
{
CString str1;
str1.Format(_T("%d人"), s[i]);
dc->DrawText(str1, ps_rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
str_rect = ps_rect;
str_rect.bottom = ps_rect.bottom + 20;
str_rect.top = ps_rect.bottom + 2;
dc->DrawText(str[i], str_rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
ps_rect.OffsetRect(width, 0);
}
dc->SelectObject(&oldBrush);
dc->SelectObject(&oldPen);
在这里左边与平时坐标有点不同,坐标的加减是下面这个方向的。

5.读取文件数据
绘制直方图的函数已经写完,接下来在View类中的OnDraw函数中调用绘制直方图的函数便可以了。我们在调用前需要先获得数据,这里可以用回输入输出流的方法读取文件,需要包含相应的头文件
void CScoresView::OnDraw(CDC* pDC)
{
CScoresDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
std::ifstream file("D:\\score.txt", std::ios::in);
std::vector<double> datas;
double data;
while (file >> data)
datas.push_back(data);
DrawScore(pDC, datas, datas.size());
}
6.测试
文本中如下

直方图结果
