C++Builder6.0的TRichEdit控件,默认无法显示图片。在有中文字符的情况下,图片的插入位置也无法很好控制,以下这段代码解决了这两个问题。
这部分代码分为三个部分:
1.让TRichEdit支持图片插入的代码
#include
#include
#include
#include
#include
struct TRichEditOleCallback : public IRichEditOleCallback
{
public:
TRichEditOleCallback()
:pStorage(NULL),m_iNumStorages(0),m_dwRef(0)
{
HRESULT hResult = ::StgCreateDocfile(NULL,
STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE /*| STGM_DELETEONRELEASE */|STGM_CREATE ,
0, &pStorage );
OleCheck( hResult );
}
virtual HRESULT STDMETHODCALLTYPE GetNewStorage(LPSTORAGE* lplpstg)
{
WCHAR tName[50];
wsprintfW(tName, L"REOLEStorage%d", m_iNumStorages++);
HRESULT hResult = pStorage->CreateStorage(tName,
STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE ,
0, 0, lplpstg );
OleCheck( hResult );
return hResult;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject)
{
HRESULT hr = S_OK;
*ppvObject = NULL;
if ( iid == IID_IUnknown ||
iid == IID_IRichEditOleCallback )
{
*ppvObject = this;
AddRef();
hr = NOERROR;
}
else
{
hr = E_NOINTERFACE;
}
return hr;
}
virtual ULONG STDMETHODCALLTYPE AddRef()
{
return ++m_dwRef;
}
virtual ULONG STDMETHODCALLTYPE Release()
{
if ( --m_dwRef == 0 )
{
delete this;
return 0;
}
return m_dwRef;
}
virtual HRESULT STDMETHODCALLTYPE GetInPlaceContext(LPOLEINPLACEFRAME FAR *lplpFrame,
LPOLEINPLACEUIWINDOW FAR *lplpDoc, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE ShowContainerUI(BOOL fShow)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE QueryInsertObject(LPCLSID lpclsid, LPSTORAGE lpstg, LONG cp)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE DeleteObject(LPOLEOBJECT lpoleobj)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE QueryAcceptData(LPDATAOBJECT lpdataobj, CLIPFORMAT FAR *lpcfFormat,
DWORD reco, BOOL fReally, HGLOBAL hMetaPict)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetClipboardData(CHARRANGE FAR *lpchrg, DWORD reco, LPDATAOBJECT FAR *lplpdataobj)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetDragDropEffect(BOOL fDrag, DWORD grfKeyState, LPDWORD pdwEffect)
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetContextMenu(WORD seltyp, LPOLEOBJECT lpoleobj, CHARRANGE FAR *lpchrg,
HMENU FAR *lphmenu)
{
return S_OK;
}
private:
int m_iNumStorages;
IStorage* pStorage;
DWORD m_dwRef;
};
2.第一层封装,以一组函数族的形式实现:
namespace RtfReportHelper
{
//使能RichEditCtrl的bmp图片编辑功能
void RichEdit_Init(TRichEdit *re);
//设置RichEditCtrl的缺省文字格式
void RichEdit_SetDefaultTextStyle(TRichEdit *re);
//得到缺省格式
TTextAttributes *RichEdit_DefaultStyle(TRichEdit *re);
//追加RTF文字
void RichEdit_AppendRtfText(TRichEdit *re, AnsiString text, AnsiString font, int size, TColor color);
//追加缺省格式的RTF文字
void RichEdit_AppendText(TRichEdit *re, AnsiString text);
//追加BMP图片
void RichEdit_AppendBMP(TRichEdit *re, Graphics::TBitmap *Bmp1);
//加载rtf文件
void RichEdit_LoadFromFile(TRichEdit *re, AnsiString filename);
//保存至rtf文件
void RichEdit_SaveToFile(TRichEdit *re, AnsiString filename);
};
namespace RtfReportHelper{};
using namespace RtfReportHelper;
//计算一个字符串中有多少个中文字符
int GetChineseCharCnt(AnsiString text)
{
int cnt = 0;
int byteCnt = text.Length();
for(int i = 1; i< byteCnt; ++i)
{
if(text[i]&0x80)
{
cnt++;
++i;
}
}
return cnt;
}
namespace RtfReportHelper
{
//使能RichEditCtrl的bmp图片编辑功能
void RichEdit_Init(TRichEdit *RichEdit1)
{
TRichEditOleCallback *roc = new TRichEditOleCallback();
RichEdit1->Perform(EM_SETOLECALLBACK,0,LPARAM(roc)); //使能BMP显示
RichEdit1->PlainText = false; //保存或加载时以.rtf格式进行
}
//设置RichEditCtrl的缺省文字格式
void RichEdit_SetDefaultTextStyle(TRichEdit *RichEdit1)
{
RichEdit1->DefAttributes->Color = clBlack;
RichEdit1->DefAttributes->Name = "宋体";
RichEdit1->DefAttributes->Height = 20;
}
//返回缺省格式
TTextAttributes *RichEdit_DefaultStyle(TRichEdit *re)
{
return re->DefAttributes;
}
//追加RTF文字
void RichEdit_AppendRtfText(TRichEdit *RichEdit1, AnsiString text, AnsiString font, int size, TColor color)
{
RichEdit1->SelAttributes->Height = size;
RichEdit1->SelAttributes->Name = font;
RichEdit1->SelAttributes->Color = color;
RichEdit1->SelStart = RichEdit1->Text.Length();
RichEdit1->Lines->Add(text);
RichEdit1->SelLength = RichEdit1->Text.Length() - RichEdit1->SelStart;
}
//追加缺省格式的RTF文字
void RichEdit_AppendText(TRichEdit *RichEdit1, AnsiString text)
{
RichEdit1->Lines->Add(text);
}
//追加BMP图片
void RichEdit_AppendBMP(TRichEdit *RichEdit1, Graphics::TBitmap *Bmp1)
{
RichEdit1->Lines->Add("");
AnsiString info;
int chineseChars = GetChineseCharCnt(RichEdit1->Text);
if(chineseChars)
{
//tricky;RichEdit对中文字符的图片插入处理有误。这里计算中文字符
//的数量然后增补进同样多的英文字符,以矫正插入位置。插入后,新增
//补进来的字符会统统删除。
for(int i = 0; i< chineseChars; ++ i)
{
info += "1";
}
RichEdit1->Lines->Add(info);
RichEdit1->SelStart = RichEdit1->Text.Length() -4; //让出额外的2组\r\b
}
else
{
RichEdit1->SelStart = RichEdit1->Text.Length() -2; //让出一组\r\n
}
Clipboard()->Assign(Bmp1);
RichEdit1->PasteFromClipboard();
if(chineseChars) RichEdit1->Lines->Delete(RichEdit1->Lines->Count-1);
}
//加载rtf文件
void RichEdit_LoadFromFile(TRichEdit *re, AnsiString filename)
{
re->Clear();
re->Lines->LoadFromFile(filename);
}
//保存至rtf文件
void RichEdit_SaveToFile(TRichEdit *re, AnsiString filename)
{
re->Lines->SaveToFile(filename);
}
}
3.第二层封装,一个可以独立使用的类
class SwxxReport
{
public:
//必须挂接到某个屏幕控件,推荐是Form本身
SwxxReport(TWinControl * Owner);
virtual ~SwxxReport();
//创建一份报告
void Create(AnsiString filename);
//添加Rtf文字,font="宋体","黑体"
void AppendRtfText(AnsiString text, AnsiString font, int size, TColor color);
//以缺省字体添加一段文字
void AppendText(AnsiString text);
//追加一张bmp图
void AppendBmp(Graphics::TBitmap *Bmp1);
//保存报告
void Save();
//在指定的RichEdit控件中显示报告
void ShowReport(TRichEdit *re);
//调用外部程序显示报告【本函数在编译器中可能会出现异常,运行时不会出现】
void ShowReport();
private:
TRichEdit *re;
AnsiString filename;
};
SwxxReport::SwxxReport(TWinControl * Owner)
{
re = new TRichEdit(Owner);
re->Parent = dynamic_cast(Owner);
re->Height = 2000;
re->Width = 1024;
//初始化
RichEdit_Init(re);
RichEdit_SetDefaultTextStyle(re);
}
SwxxReport::~SwxxReport()
{
delete re;
}
//创建一份报告
void SwxxReport::Create(AnsiString filename)
{
this->filename = filename;
TRichEdit *RichEdit1 = re;
//文件头
RichEdit_AppendRtfText(RichEdit1, "XX测试报告", "黑体", 28, clBlack);
RichEdit_AppendRtfText(RichEdit1,
"测试时间" + TDateTime::CurrentDateTime().DateTimeString(),
RichEdit1->DefAttributes->Name,
20,
clLtGray);
RichEdit_AppendText(RichEdit1, ""); //这是添加空行的正确写法,空行的间距仍可设置
}
//添加Rtf文字,font="宋体","黑体"
void SwxxReport::AppendRtfText(AnsiString text, AnsiString font, int size, TColor color)
{
TRichEdit *RichEdit1 = re;
RichEdit_AppendRtfText(RichEdit1, text, font, size, color);
}
//以缺省字体添加一段文字
void SwxxReport::AppendText(AnsiString text)
{
TRichEdit *RichEdit1 = re;
RichEdit_AppendText(RichEdit1, text);
}
//追加一张bmp图
void SwxxReport::AppendBmp(Graphics::TBitmap *Bmp1)
{
TRichEdit *RichEdit1 = re;
RichEdit_AppendBMP(RichEdit1, Bmp1);
}
//关闭报告
void SwxxReport::Save()
{
TRichEdit *RichEdit1 = re;
RichEdit_SaveToFile(RichEdit1, filename);
}
#define UNIQUE_FILE "C:\\_Default_563274833.rtf"
//显示报告
void SwxxReport::ShowReport(TRichEdit *re)
{
RichEdit_SaveToFile(this->re, UNIQUE_FILE);
RichEdit_LoadFromFile(re, UNIQUE_FILE);
}
//显示报告
void SwxxReport::ShowReport()
{
Save();
int times = 20;
ReDo:
try
{
SHELLEXECUTEINFO ShExecInfo = {0};
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = "open";
ShExecInfo.lpFile = filename.c_str(); //can be a file as well
ShExecInfo.lpParameters = "";
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_SHOW;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
}
catch(...)
{
if(--times>0) goto ReDo;
}
}
4.最后是调用部分:
//输出报告
SwxxReport sr(this); //必须传入Form指针
filename = ExtractFileDir(Application->ExeName) + "\\测试报告" + TDateTime::CurrentDateTime().FormatString("yyyy_mm_dd_hh_nn_ss")+".rtf";
sr.Create(filename);
sr.AppendText("各板卡工作状态如下图所示:");
sr.AppendBmp(Image1->Picture->Bitmap);
sr.AppendText("测试1:xx板测试:通过。");
sr.AppendText("测试2:xx板测试:通过。");
sr.AppendText("测试3:xx板测试:通过。");
sr.AppendText(""); //空行
sr.Save();
sr.ShowReport(); //报告输出
这是一个在任何客户端环境都可以执行的很简单的报告输出工具。