从一个文本文件中读出表头项目,表头按树型结构(多叉树)表示,然后编写带界面的ocx控件来显示表格,以供其它应用程序来调用。
1、 表头文本文件
上下边距: 10
左右边距: 10
字间距: 2
行间距: 10
字体: 宋体
字体大小: 10
对齐方式: 0
编号: 1 行数: 2 名称: 中国 北海 子项数: 2
编号: 1.1 行数: 2 名称: 旅顺 基地 子项数: 0
编号: 1.2 行数: 2 名称: 青岛 基地 子项数: 1
编号: 1.2.1 行数: 2 名称: 码头 海港 子项数: 0
编号: 2 行数: 1 名称: 第一集团 子项数: 2
编号: 2.1 行数: 1 名称: 第一纵队 子项数: 0
编号: 2.2 行数: 1 名称: 第二纵队 子项数: 0
编号: 3 行数: 2 名称: 中国 南海 子项数: 3
编号: 3.1 行数: 2 名称: 汕头 基地 子项数: 2
编号: 3.1.1 行数: 2 名称: 大海 海港 子项数: 1
编号: 3.1.1 .1 行数: 2 名称: 大海 海港 子项数: 0
编号: 3.1.2 行数: 2 名称: 山川 海港 子项数: 0
编号: 3.2 行数: 2 名称: 珠海 基地 子项数: 0
编号: 3.3 行数: 2 名称: 深圳 基地 子项数: 1
编号: 3.3.1 行数: 2 名称: 小平 海港 子项数: 0
编号: 4 行数: 3 名称: 中国 东海 第二 子项数: 0
编号: 5 行数: 1 名称: 第二集团 子项数: 3
编号: 5.1 行数: 1 名称: 第一队 子项数: 0
编号: 5.2 行数: 1 名称: 第二队 子项数: 1
编号: 5.2.1 行数: 1 名称: 山川海港 子项数: 1
编号: 5.2.1 .1 行数: 2 名称: 山川 海港 子项数: 1
编号: 5.2.1 .1.1 行数: 1 名称: 山川海港 子项数: 0
编号: 5.3 行数: 1 名称: 第*3纵队 子项数: 0
注:文件规则
l 父一定在子的前面
l 哥哥一定在弟弟的前面
l 父亲的宽度不能小于儿子的宽度和
l 如果在字符间有空格,请用*号代替
2、 生成的表格(见图)
3、 编写每个表格项的类
class CTableNode
{
public:
CTableNode();
virtual ~CTableNode();
public:
char* sNo; //编号
char* sName; //名称
unsigned short nLineNum; //字符所占的行数
unsigned short* nStartChar; //根据行数而确定的起始字符位置数组
unsigned short nChildNum; //子项数
unsigned short nWidth; //宽度
unsigned short nHeight; //高度
POINT pntStart; //起始点坐标
unsigned short* nLevelNum; //分级的层次编号
CTableNode* m_pParentNode; //父结点
void Draw(CDC* pDC, unsigned short left, unsigned short top, unsigned short bottom, int lineDis, int fontHeight, int textAlign);
};
void CTableNode::Draw(CDC* pDC,unsigned short left, unsigned short top, unsigned short bottom, int lineDis, int fontHeight, int textAlign)
{
pDC->MoveTo(pntStart);
pDC->LineTo(pntStart.x + nWidth, pntStart.y);
pDC->MoveTo(pntStart);
pDC->LineTo(pntStart.x, pntStart.y + nHeight);
if(nChildNum == 0)
{
pDC->MoveTo(pntStart.x, pntStart.y + nHeight);
pDC->LineTo(pntStart.x + nWidth, pntStart.y + nHeight);
pDC->MoveTo(pntStart.x, pntStart.y + nHeight);
pDC->LineTo(pntStart.x, bottom);
}
CString str;
CString str1;
str = sName;
int iStart = 0;
int iLineOffset = lineDis + fontHeight;
for(int i = 0; i < nLineNum; i++)
{
if((i + 1) == nLineNum)
{
str1 = str.Mid(iStart);
str1.Replace("*"," ");
}
else
{
str1 = str.Mid(iStart, nStartChar[i] - iStart);
str1.Replace("*", " ");
iStart = nStartChar[i];
}
int iY = (nHeight - (nLineNum*fontHeight + (nLineNum - 1)*lineDis))/2;
iY = iY + pntStart.y;
if(textAlign == 1) //左对齐
pDC->TextOut(left + pntStart.x, iY + iLineOffset*i, str1);
else if(textAlign == 2) //右对齐
{
int iCharW = pDC->GetTextExtent(str1).cx;
pDC->TextOut(pntStart.x + nWidth - left - iCharW, iY + iLineOffset*i, str1);
}
else //居中
{
int iCharW = pDC->GetTextExtent(str1).cx;
pDC->TextOut(pntStart.x + (nWidth - iCharW)/2, iY + iLineOffset*i, str1);
}
}
}
4、 读取文本文件,建立树型结构
FILE* fl = fopen(sFile,"r");
if(fl == NULL) return;
char strBuf[256];
CString strTmp;
int iData;
bool bAdded = false;
CTableNode* lpTableNode = NULL;
while(!feof(fl))
{
fscanf(fl,"%s",strBuf);
strTmp = strBuf;
if(strTmp == "编号:")
{
lpTableNode = new CTableNode();
fscanf(fl,"%s",strBuf);
lpTableNode->sNo = new char[strlen(strBuf) + 1];
strcpy(lpTableNode->sNo ,strBuf);
fscanf(fl,"%s",&strBuf); //行数
fscanf(fl,"%d",&iData);
lpTableNode->nLineNum = iData;
fscanf(fl,"%s",&strBuf); //名称
lpTableNode->nStartChar = new unsigned short[iData - 1];
lpTableNode->sName = new char[100];
for(int j = 0; j < lpTableNode->nLineNum; j++)
{
fscanf(fl,"%s",&strBuf);
if(j == 0)
strcpy(lpTableNode->sName, strBuf);
else
{
lpTableNode->nStartChar[j - 1] = strlen(lpTableNode->sName);
strcat(lpTableNode->sName, strBuf);
}
}
fscanf(fl,"%s",&strBuf);
fscanf(fl, "%d", &iData);
lpTableNode->nChildNum = iData;
m_lstNodes.Add(lpTableNode);
continue;
}
if(strTmp=="上下边距:")
{
fscanf(fl, "%d", &m_nUpDownDis);
continue;
}
if(strTmp=="左右边距:")
{
fscanf(fl, "%d", &m_nLeftRightDis);
continue;
}
if(strTmp=="字体:")
{
fscanf(fl, "%s", m_sFontName);
continue;
}
if(strTmp=="字体大小:")
{
fscanf(fl, "%d", &m_nFontSize);
continue;
}
if(strTmp=="字间距:")
{
fscanf(fl, "%d", &m_nFontDis);
continue;
}
if(strTmp=="行间距:")
{
fscanf(fl, "%d", &m_nLineDis);
continue;
}
if(strTmp == "对齐方式:")
{
fscanf(fl, "%d", &m_nTextAlign);
continue;
}
}
fclose(fl);
设置字体
m_font.DeleteObject();
m_font.CreatePointFont(m_nFontSize*10, m_sFontName);
m_pMemDC->SelectObject(&m_font);
TEXTMETRIC metri;
m_pMemDC->GetTextMetrics(&metri);
m_nFontHeight = metri.tmHeight;
m_nFontWidth = metri.tmAveCharWidth;
m_pMemDC->SetTextCharacterExtra(m_nFontDis);
5、 生成表格
CString str;
int sNoLen = 0;
bool bDepth = false;
unsigned short nTreeDepth = 0;
unsigned short nWidthByDepth[MAX_TREE_DEPTH];
for(int i = 0; i < m_lstNodes.GetSize(); i++)
{
CTableNode* pTableNode = (CTableNode*)m_lstNodes.GetAt(i);
sNoLen = strlen(pTableNode->sNo);
int iDotNum = CalDotNum(pTableNode->sNo);
pTableNode->nLevelNum = new unsigned short[iDotNum + 1];
if(iDotNum > 0)
{//确定父节点
str = pTableNode->sNo;
int ri = str.ReverseFind('.');
str = str.Left(ri);
pTableNode->m_pParentNode = FindParents(str);
str = pTableNode->sNo;
for(int m = 0; m < (iDotNum+1); m++)
{
int istart = str.Find('.');
if(istart == -1)
{
pTableNode->nLevelNum[m] = atoi(str);
break;
}
pTableNode->nLevelNum[m] = atoi(str.Left(istart));
str = str.Mid(istart + 1);
}
}
else
{
pTableNode->nLevelNum[0] = atoi(pTableNode->sNo);
}
//计算不同深度情况下的最大字符数
bDepth = false;
for(int j = 0; j < nTreeDepth; j++)
{
if((iDotNum + 1) <= nTreeDepth)
{
bDepth = true;
break;
}
}
if(!bDepth)
{
m_pDeepNode = pTableNode;
nWidthByDepth[iDotNum] = pTableNode->nLineNum;
nTreeDepth ++;
}
else if(pTableNode->nLineNum > nWidthByDepth[iDotNum])
nWidthByDepth[iDotNum] = pTableNode->nLineNum;
}
//计算高度和没有孩子的节点的宽度
for(i = 0; i < m_lstNodes.GetSize(); i++)
{
CTableNode* pTableNode = (CTableNode*)m_lstNodes.GetAt(i);
sNoLen = strlen(pTableNode->sNo);
int iDotNum = CalDotNum(pTableNode->sNo);
pTableNode->nHeight = m_nUpDownDis*2 + m_nLineDis*(nWidthByDepth[iDotNum]-1) + nWidthByDepth[iDotNum]*m_nFontHeight;
if(pTableNode->nChildNum != 0)
continue;
int nMaxCharNum = 0;
if(pTableNode->nLineNum == 1)
{
nMaxCharNum = strlen(pTableNode->sName);
}
else
{
int nStart = 0;
for(int j = 0; j < pTableNode->nLineNum; j++)
{
CString str = pTableNode->sName;
if((j+1) >= pTableNode->nLineNum)
{
str = str.Mid(nStart);
}
else
{
str = str.Mid(nStart,pTableNode->nStartChar[j]);
nStart = pTableNode->nStartChar[j];
}
if(nMaxCharNum < str.GetLength())
{
nMaxCharNum = str.GetLength();
}
}
}
pTableNode->nWidth = m_nLeftRightDis*2 + m_nFontDis*(nMaxCharNum/2 - 1) + nMaxCharNum*m_nFontWidth;
}
//求有孩子的节点的宽度,倒序遍历
for(i = m_lstNodes.GetSize() - 1; i >= 0; i--)
{
CTableNode* pTableNode = (CTableNode*)m_lstNodes.GetAt(i);
if(pTableNode->m_pParentNode != NULL)
{
pTableNode->m_pParentNode->nWidth += pTableNode->nWidth;
}
}
//计算起始坐标
for(i = 0; i < m_lstNodes.GetSize(); i++)
{
CTableNode* pTableNode = (CTableNode*)m_lstNodes.GetAt(i);
pTableNode->pntStart.x = CalNodesPosX(pTableNode);
unsigned short nTmpHeight = 0;
CalNodesPosY(pTableNode, nTmpHeight);
pTableNode->pntStart.y = nTmpHeight;
}
//没有孩子的节点高度与最深节点对齐
for(i = 0; i < m_lstNodes.GetSize(); i++)
{
CTableNode* pTableNode = (CTableNode*)m_lstNodes.GetAt(i);
if(pTableNode->nChildNum == 0 && (strlen(pTableNode->sNo) != strlen(m_pDeepNode->sNo)))
{
if(pTableNode->m_pParentNode == NULL)
pTableNode->nHeight = m_pDeepNode->pntStart.y + m_pDeepNode->nHeight;
else
pTableNode->nHeight = m_pDeepNode->pntStart.y + m_pDeepNode->nHeight - (pTableNode->m_pParentNode->pntStart.y + pTableNode->m_pParentNode->nHeight);
}
}