C++Builder6.0支持图片插入的TRichEdit增强

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(); //报告输出


这是一个在任何客户端环境都可以执行的很简单的报告输出工具。

你可能感兴趣的:(设计)