如何获取TRichEdit的进度?

How to get the progress of a TRichEdit?

我有一个简单的 'brut' 大文本文件 (20MB)。我想在 TRichEdit 中显示它。问题是显示需要 6 秒。我想在应用程序的底部放置一个进度条,以避免这种糟糕的用户体验设计。

我的问题是如何获取TRichEdit显示的进度?使用 TRichEdit::LoadFromStream 方法,它从 0% 到 100% 的速度很快(不到 1 秒),但在应用程序之后,在第一次显示时等待 6 秒。

我用 TFileStream 继承创建了这个 class FileStreamProgress。我覆盖 TFileStream::Read()

    int __fastcall FileStreamProgress::Read(void *Buffer, int Count)
    {
        __int64 previousPosition = this->Position;
        int ret = TFileStream::Read(Buffer, Count);
        if (this->Position == 0 || this->Position == this->Size || (previousPosition/128000) != (this->Position/128000)) {
            ProgressCallBack(ProgressCallBackParam1, this->Position, this->Size);
        }
        return ret;
    }
    static void FileStreamProgress::ProgressCallBack(void*thiz, int i, int max)
    {
        TProgressBar* ProgressBar = (TProgressBar*)thiz;
        if (ProgressBar)
        {
            if (max > 0)
            {
                ProgressBar->Position = int(i * 100 / max);
            }

            if (Application)
            {
                Sleep(1);
                Application->ProcessMessages();
            }
        }
    }        

我是这样测试的:

void MyApp::CreatePage(AnsiString filename)
{
    ProgressBar->Visible = true;
    FileStreamProgress::ProgressCallBackParam1 = (void*)this->ProgressBar;
    TFileStream * stream = new FileStreamProgress(filename.c_str(), fmOpenRead);
    TPageMemo* Page = new TPageMemo(this);

    Page->Parent = PControl;
    Page->PageControl = PControl;

    MessageDlg("111",mtError,TMsgDlgButtons()<<mbOK,0);
    Page->Texte->Lines->LoadFromStream(stream);
    MessageDlg("222",mtError,TMsgDlgButtons()<<mbOK,0);

    PControl->ActivePage = Page;
}

2个消息对话框“111”和“222”之间有7秒的间隔。我的进度条在 100% 时等待 6 秒(在放映期间)

我试图更深入地了解 win32 API 的 SendMessage 和 Handle,但没有得到预期的结果。

最后,我昨天使用了 TMemo,因为它是粗俗的文本。它非常快(即时打开)但缺少一些功能,如 FindTextW()。我重写了它。谢谢

http://docwiki.embarcadero.com/RADStudio/Rio/en/Memo_and_Rich_Edit_Controls

很好奇,所以我测试了 TRichEdit 并想出了这个:

//---------------------------------------------------------------------------
void load_text(TRichEdit *re,AnsiString filename,TProgressBar *pb)
    {
    // variables
    int hnd,adr,siz,len,i;
    const int _buf=128*1024;                    // buffer size
    AnsiString s,s0;
    char buf[_buf+1];
    // open file and detect size
    hnd=FileOpen(filename,fmOpenRead); if (hnd<0) return;
    siz=FileSeek(hnd,0,2);
        FileSeek(hnd,0,0);
    // prepare progress bar
    pb->Position=0;
    pb->Max=siz;
    pb->Visible=true;
    // process txt file by chunks
    for (s0="",adr=0;adr<siz;)
        {
        // update progress bar and GUI
        pb->Position=adr;
        Application->ProcessMessages();
        // load chunk
        len=FileRead(hnd,buf,_buf);
        adr+=len; buf[len]=0;
        // handle cutted lines by chunk size
        s=s0; s0="";
        // ignore last 2 lines for chunks (except last chunk)
        if (len==_buf)
            {
            // skip last line
            for (i=len-1;i>=0;i--)
             if ((buf[i]==13)||(buf[i]==10))
                {
                i--;
                if (i>=0)
                 if (buf[i]!=buf[i+1])              // different eol code to ignore empty line
                  if ((buf[i]==13)||(buf[i]==10))   // eol code
                   i--;
                break;
                }
            // skip another line to avoid inserting empty line if eol is cutted
            for (;i>=0;i--)
             if ((buf[i]==13)||(buf[i]==10))
                {
                s0+=buf+i+1;                        // copy last 2 lines into s0
                i--;
                if (i>=0)
                 if (buf[i]!=buf[i+1])              // different eol code to ignore empty line
                  if ((buf[i]==13)||(buf[i]==10))   // eol code
                   i--;
                i++; if (i<0) i=0; buf[i]=0;        // end of string
                break;
                }
            }
        // last chunk ignore last eol
        else{
            // skip last line
            i=len-1;
            if ((buf[i]==13)||(buf[i]==10))         // eol code
                {
                i--;
                if (buf[i]!=buf[i+1])               // different eol code to ignore empty line
                 if ((buf[i]==13)||(buf[i]==10))    // eol code
                  i--;
                i++; if (i<0) i=0; buf[i]=0;        // end of string
                }
            }
        // add actual chunk
        s+=buf;
        re->Lines->Add(s);
        }
    // tidy up
    pb->Visible=false;
    FileClose(hnd); hnd=-1;
    }
//---------------------------------------------------------------------------

看起来它可以在没有你描述的结束暂停的情况下工作,但这可能与 IDE/VCL/compiler 我正在使用 BDS2006 Turbo C++ 的版本有关。在 ~23 MByte STL 文件上测试时,加载时间为 ~10 秒(TMemo 需要两倍的天知道为什么)...

保存的文件(PlainText=true)与加载的文件相同,因此代码应该是正确的。

这里是预览动画GIF:

像这样使用时:

void __fastcall TForm1::FormActivate(TObject *Sender)
    {
    //tbeg();
    load_text(re_txt,"in.txt",pb_progress);
    //tend();
    //Caption=tstr();
    re_txt->Lines->SaveToFile("out.txt");
    }

其中 pb_progressTProgressBarre_txtTRichEdit

如您所见,不需要回调...

PS. 如果你想像我一样测量时间(注释行),tbeg/tend/tstr 函数的实现在这里: