TRichEdit & TMemo itp...

Menu
  1. Wyszukiwanie tekstu w RichEdit.

  2. Drukowanie zawartości Memo.

  3. Odczyt i zmiana zawartości pojedynczej linii obiektu RichEdit.

  4. Odczyt numeru linii wskazywanej myszą w RichEdit.

  5. Zaznaczanie linii wskazywanej myszą.

  6. Zmiana wartości lewego i prawego marginesu.

  7. Wczytywanie plików większych niż 64Kb do RichEdit.

  8. Zaznaczanie tekstu za pomocą kodu.

  9. Przesuwanie wstawki korektora za pomocą kodu.

  10. Skok do wybranej linii obiektu RichEdit.

  11. Zapisywanie zawartości Memo do pliku *.ini.

  12. Przeciąganie i upuszczanie plików na RichEdit.

  13. Bitmapa jako tło w Memo.

  14. Przepisywanie zawartości z jednego RichEdit do drugiego z zachowaniem formatowania.

  15. Wyświetlanie zawartości konsoli CMD.exe w Memo - wg. pomysłu kinio.

  16. Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu kinio.

 

 

    Wyszukiwanie tekstu w RichEdit.

 

    Obiekt RichEdit posiada wszystkie funkcje potrzebne do wyszukiwania w nim tekstu. Przedstawię najpierw sposób na wyszukiwanie tekstu bez rozróżniania wielkości liter. Dowyszukiwania posłużę się obiektem Edit1 w którym będzie wpisywany poszukiwany tekst i przyciskiem Button1 inicjującym wyszukiwanie.

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 TSearchTypes st;

 String fText = Edit1->Text.Trim().LowerCase();

 if(RichEdit1->SelLength)
  RichEdit1->SelStart += 1;

 int fPos = RichEdit1->FindText(fText, RichEdit1->SelStart, RichEdit1->Text.LowerCase().Length(), st);
 RichEdit1->SelStart = fPos;
 RichEdit1->SelLength = fText.Length();
 RichEdit1->SetFocus();

 if(fPos != -1)
 {
  RichEdit1->SelStart = fPos;
  RichEdit1->SelLength = fText.Length();
 }
 else
 {
  MessageBox(NULL, "Przeszukano cały dokument", "Zakończono wyszukiwanie", MB_OK);
  RichEdit1->SelStart = 0;
 }
}

//--------------------------------

 

Kod jest banalnie prosty i w zasadzie opiera się na funkcjach zawartych w obiekcie RichEdit.  Teraz przedstawię kod pozwalający na wyszukiwanie tekstu z uwzględnieniem wielkości liter i z rozróżnianiem tylko dla całych wyrazów:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 TSearchTypes st;
 st << stMatchCase << stWholeWord;

 String fText = Edit1->Text.Trim();

 if(RichEdit1->SelLength)
  RichEdit1->SelStart += 1;

 int fPos = RichEdit1->FindText(fText, RichEdit1->SelStart, RichEdit1->Text.Length(), st);
 RichEdit1->SelStart = fPos;
 RichEdit1->SelLength = fText.Length();
 RichEdit1->SetFocus();

 if(fPos != -1)
 {
  RichEdit1->SelStart = fPos;
  RichEdit1->SelLength = fText.Length();
 }
 else
 {
  MessageBox(NULL, "Przeszukano cały dokument", "Zakończono wyszukiwanie", MB_OK);
  RichEdit1->SelStart = 0;
 }
}

//--------------------------------

 

Jak widać kody różnią się między sobą tylko nieznacznie. Do ustawiania wyszukiwania z rozróżnieniem wielkości liter służy parametr stMatchCase, a do wyszukiwania tylko całych wyrazów parametr stWholeWord. W przykładzie pokazany jest kod wprowadzania tych parametrów, wyprowadzanie ich, czyli wyłączanie przebiega podobnie z tą różnicą, że używa się innego operatora:
st >> stMatchCase >> stWholeWord;
Można oczywiście stosować kombinacje i np. jeden parametr wprowadzać, a drugi wyprowadzać.

...powrót do menu. 

    Odczyt i zmiana zawartości pojedynczej linii obiektu RichEdit.

 

Odczyt:

// Plik źródłowy np. Unit1.cpp.
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 String tekst = RichEdit1->Lines->Strings[numer_linii_liczony_od_0];
}

//--------------------------------

 

Zmiana:

// Plik źródłowy np. Unit1.cpp.
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 RichEdit1->Lines->Strings[numer_linii] = "tekst"; 
}

//--------------------------------

 

...powrót do menu. 

    Odczyt numeru linii wskazywanej myszą w RichEdit.

 

Posłużę się zdarzeniem OnMouseDown dla RichEdit, ale równie dobrze można wykorzystać zdarzenia OnMouseUp lub OnMouseMove:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------
void __fastcall TForm1::RichEdit1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)

{
 unsigned short int Linia = RichEdit1->Perform(EM_LINEFROMCHAR, RichEdit1->SelStart, 0);
}

//--------------------------------

 

...powrót do menu. 

    Zaznaczanie linii wskazywanej myszą.

 

W celu zaznaczenia całej linii wykorzystam zdarzenie OnMouseDown dla obiektu RichEdit. Cała linia zostanie zaznaczona po kliknięciu na niej:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

void __fastcall TForm1::RichEdit1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)

{
 unsigned short int Linia = RichEdit1->Perform(EM_LINEFROMCHAR, RichEdit1->SelStart, 0);
 RichEdit1->SelStart = RichEdit1->Perform(EM_LINEINDEX, Linia, 0);
 RichEdit1->SelLength = RichEdit1->Lines->Strings[Linia].Length();
}

//--------------------------------

 

...powrót do menu. 

    Zmiana wartości lewego i prawego marginesu.

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 TRect R;
 int LeftMargin = 20;
 int RightMargin = 10;
 R = RichEdit1->ClientRect;
 R.Left = R.Left + LeftMargin;
 R.Top = R.Top + 2;
 R.Bottom = R.Bottom - 2;
 R.Right = R.Right - RightMargin;
 SendMessage(RichEdit1->Handle, EM_SETRECT, 0, Longint(&R));
}

//--------------------------------

 

...powrót do menu. 

    Wczytywanie plików większych niż 64Kb do RichEdit.

 

Standardowo obiekt RichEdit nie wczytuje plików większych niż 64Kb, ale można to zmienić za pomocą prostego kodu:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
 RichEdit1->Perform(EM_EXLIMITTEXT, 0, 4194176);
}

//--------------------------------

 

...powrót do menu. 

 

     Zaznaczanie tekstu za pomocą kodu.

 

Sposób zaznaczania tekstu za pomocą myszki każdy zna, dlatego pokażę jak można zaznaczyć tekst w dowolnym obiekcie, w którym jest to możliwe za pomocą kodu. Do zaznaczania tekstu można wykorzystać funkcję Perform przekazując jej jako argumenty komunikat o rodzaju wykonywanej akcji, oraz początkową i końcową wartość zaznaczenia. Dla lepszego zobrazowania tego zadania proponuję umieścić na formularzu obiekty Memo1, CSpinEdit1 i CSpinEdit2. Do obiektu Memo1 wstawiamy jakiś tekst, obiekt CSpinEdit1 będzie podawał początkową wartość zaznaczenia, natomiast obiekt CSpinEdit2 końcową, reszta jest banalnie prosta, dla przejrzystości do zaznaczania stworzę własną funkcję, którą nazwę SelectText:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

void SelectText(TMemo *Memo, int start, int meta)
{
 Memo->Perform(EM_SETSEL, start, meta);
 Memo->SetFocus();
}
//--------------------------------
void __fastcall TForm1::CSpinEdit1Change(TObject *Sender)
{
 SelectText(Memo1, CSpinEdit1->Value, CSpinEdit2->Value);
}
//--------------------------------
void __fastcall TForm1::CSpinEdit2Change(TObject *Sender)
{
 SelectText(Memo1, CSpinEdit1->Value, CSpinEdit2->Value);
}
//--------------------------------

 

Jak widać na przykładzie zmiana zaznaczenia odbywa się poprzez zmianę wartości Value dla obiektów CSpinEdit1 i CSpinEdit2, myślę, że nie wymaga to dalszych wyjaśnień.

W powyższym przykładzie widać, że z funkcji SelectText może skorzystać tylko obiekt typu TMemo, ponieważ jako jeden z argumentów funkcja pobiera właśnie odwołanie do tego typu obiektu, ale można to zmienić. Wykorzystując funkcję ogólną możemy zdefiniować odwołanie do bliżej nieokreślonego typu obiektu, oczywiście ten bliżej niezdefiniowany obiekt musi obsługiwać funkcję Perform. Opis funkcji ogólnych został zamieszczony w dziale Funkcje ogólne. Realizacja całego zadania jest banalnie prosta:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

template <class T> void SelectText(T *Obiekt, int start, int meta)
{
 Obiekt->Perform(EM_SETSEL, start, meta);
 Obiekt->SetFocus();
}
//--------------------------------
void __fastcall TForm1::CSpinEdit1Change(TObject *Sender)
{
 SelectText(Memo1, CSpinEdit1->Value, CSpinEdit2->Value);
}
//--------------------------------
void __fastcall TForm1::CSpinEdit2Change(TObject *Sender)
{
 SelectText(Memo1, CSpinEdit1->Value, CSpinEdit2->Value);
}
//--------------------------------

 

W ten oto sposób możemy przekazać do funkcji jako pierwszy argument dowolny typ obiektu obsługujący funkcję Perform. Można by to oczywiście zrealizować jeszcze za pomocą rzutowanie jednego typu obiektu na drugi co jeszcze upraszcza zadanie:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

void SelectText(TMemo *Memo, int start, int meta)
{
 Memo->Perform(EM_SETSEL, start, meta);
 Memo->SetFocus();
}
//--------------------------------
void __fastcall TForm1::CSpinEdit1Change(TObject *Sender)
{
 SelectText((TMemo *)RichEdit1, CSpinEdit1->Value, CSpinEdit2->Value);
}
//--------------------------------
void __fastcall TForm1::CSpinEdit2Change(TObject *Sender)
{
 SelectText((TMemo *)RichEdit1, CSpinEdit1->Value, CSpinEdit2->Value);
}
//--------------------------------

 

...powrót do menu. 

    Przesuwanie wstawki korektora za pomocą kodu.

 

Wstawka korektora to tak zwany kursor piszący z angielskiego carret. Kursor w oknie edycji dowolnego obiektu typu TMemo, TRichEdit, TEdit itp. można przesuwać np. za pomocą klawiszy strzałek, ale można to też robić za pomocą kodu. Zadanie w realizacji jest bardzo podobne do porady zaznaczanie tekstu za pomocą kodu. Posłużymy się tutaj obiektem Memo1 i SCpinEdit1:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

template <class M> void MoveCarret(M *Obiekt, int start)
{
 Obiekt->Perform(EM_SETSEL, start, start);
 Obiekt->SetFocus();
}
//--------------------------------
void __fastcall TForm1::CSpinEdit1Change(TObject *Sender)
{
 MoveCarret(Memo1, CSpinEdit1->Value);
}
//--------------------------------

 

Jak widać w poradzie wykorzystałem funkcję ogólną ze względu na jej uniwersalny charakter, oraz funkcję Perform przekazując jej jako argumentu komunikat o zaznaczaniu tekstu, chociaż w rzeczywistości tekst nie jest zaznaczany a to dlatego, że pozostałe dwa argumenty określające początek zaznaczenia i jego koniec mają taką samą wartość.

...powrót do menu. 

    Skok do wybranej linii obiektu RichEdit.

 

W obiektach takich jak np. ListBox skok do wybranej lini nie stanowi większego problemu ponieważ można posłużyć się funkcją ItemIndex, niestety obiekty typu TRichEdit i TMemoEdit nie posiadają tej właściwości, dlatego żeby wykonać skok do wybranej linii z zaznaczeniem całej linii trzeba stworzyć własną funkcję. Do realizacji tego zadania stworzę funkcję ogólną by można ją było bez problemu wykorzystać zarówno dla obiektu typu TRichEdit jak i TMemo. Do zmiany numeru linii posłużę się obiektem CSpinEdit1 z zakładki Samples:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

template <class M> void GoToLine(M *Obiekt, int line)
{
 Obiekt->SelStart = Obiekt->Perform(EM_LINEINDEX, line - 1, 0);
 int length = Obiekt->Lines->Strings[line - 1].Length();
 Obiekt->SelLength = length;
 Obiekt->Perform(EM_SCROLLCARET, 0, 0);
 Obiekt->SetFocus();
}
//--------------------------------
void __fastcall TForm1::CSpinEdit1Change(TObject *Sender)
{
 GoToLine(RichEdit1, CSpinEdit1->Value);
}
//--------------------------------

 

Działanie kodu jest bardzo proste. Funkcja SelStart pobiera początek linii i to właśnie tej funkcji przekazujemy numer linii do której chcemy skoczyć. Funkcja SelLength pobiera długość linii do zaznaczenia, w tym przypadku jest to po prostu długość całej wybranej linii, funkcja Perform przesuwa wstawkę korektora na koniec linii zaznaczając przy okazji tekst. Jeżeli chcemy skoczyć do wybranej linii bez zaznaczania tekstu należy przekazać funkcji SelLength wartość 0 (Obiekt->SelLength = 0;). Przedstawiona funkcja ma jeszcze jedną zaletę powoduje przewijanie okna z tekstem.

...powrót do menu. 

    Zapisywanie zawartości Memo do pliku *.ini

 

W tej poradzie chcę pokazać jak można zapisać zawartość obiektu Memo1 do pliku *.ini, może się to przydać w sytuacji gdy chcemy zbudować w prosty sposób bazę danych zawierającą wszelakiego rodzaju opisy, ładowane np. do obiektu Memo1 przy odwołaniu się do wybranego rekordu, możliwości jest wiele. Wykorzystanie pliku typu *.ini ma tą zaletę, że łatwo jest indeksować jego zawartość poprzez tworzenie kluczy i podkluczy. W przykładzie zawartość Memo1 będzie właśnie indeksowana w wybranym podkluczu klucza głównego. Istnieje jednak pewne ograniczenie co do wielkości zapisywanego łańcucha znaków, otóż klasa TIniFile nie obsłuży łańcucha dłuższego niż 2047 + kilkanaście znaków własnych, oznacza to że do każdego podklucza możemy wpisać maksymalnie 2047 znaków, dlatego dobrze jest ustawić właściwość MaxLength obiektu Memo1 na 2047, co zapobiegnie wpisywaniu do niego zbyt dużej ilości tekstu, czyli takiej, która nie zmieści się w podkluczu. Ktoś może sobie pomyśleć co to za filozofia wpisać łańcuch znaków do pliku ini, nawet z obiektu Memo, skoro jest to typ AnsiString, a klasa TIniFiles posiada funkcje WriteString i ReadString. To prawda nie stanowi to większego problemu, ja jednak przedstawię dwie funkcje, które będą zapisywały zawartość Memo1 do pliku *.ini z zachowaniem końca linii i z łamaniem tekstu, co oznacza że po wczytaniu Memo1 zachowa swój pierwotny układ tekstu:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

AnsiString StrToIniStr(const AnsiString Str)
{
 char Buffer[8096];
 PChar B, S;

 if(Str.Length() > sizeof(Buffer))
  throw Exception("AnsiString jest zbyt duży, żeby go zapisać do pliku INI.");
 S = Str.c_str();
 B = Buffer;
 while(*S != '\0')
 {
  switch (*S)
  {
   case 13:
   case 10:
    if((*S == 13) && (S[1] == 10)) S++;
    else if((*S == 10) && (S[1] == 13)) S++;
     *B = '\\';
      B++;
     *B = 'n';
      B++;
      S++;
   break;
   default:
     *B = *S;
      B++;
      S++;
  }
 }
 *B = '\0';
 return String(Buffer);
}
//--------------------------------
String IniStrToStr(const String Str)
{
 char Buffer[8096];
 PChar B, S;

 if(Str.Length() > sizeof(Buffer))
  throw Exception("Wczytywanie AnsiString z pliku INI.");
   S = Str.c_str();
   B = Buffer;
 while(*S != '\0')
 {
  if((S[0] == '\\') && (S[1] == 'n'))
  {
   *B = 13;
    B++;
   *B = 10;
    B++;
    S++;
    S++;
  }
  else
  {
   *B = *S;
    B++;
    S++;
  }
 }
 *B = '\0';
 return String(Buffer);
}
//--------------------------------

#include <Inifiles.hpp>
//Zapisz do ini
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 TIniFile *memoIni = new TIniFile(ExtractFilePath(Application->ExeName) + "Memo.ini");
 memoIni->WriteString("MEMO", "TEKST", StrToIniStr(Memo3->Text.SubString(0, 2047)));
 delete memoIni;
}
//--------------------------------
//Wczytaj z ini

void __fastcall TForm1::Button3Click(TObject *Sender)
{
 TIniFile *memoIni = new TIniFile(ExtractFilePath(Application->ExeName) + "Memo.ini");
 Memo4->Text = IniStrToStr(memoIni->ReadString("MEMO", "TEKST", ""));
 delete memoIni;
}
//--------------------------------

 

...powrót do menu. 

 

    Przeciąganie i upuszczanie plików na RichEdit.

 

    Do ładowaniu plików do obiektów typu TRichEdit, TMemoEdit, TListBox itp. służy funkcja LoadFromFile, o czym zapewne wszyscy wiedzą. Tym razem chcę pokazać na przykładzie obiektu RichEdit jak załadować plik metodą przeciągnij - upuść. Zadanie polega na przeciągnięciu pliku np. z pulpitu, eksploratora lub innego dowolnego menadżera plików do obiektu RichEdit i wyświetleniu w nim jego zawartości. Zadanie jest trochę złożone, na początek w pliku nagłówkowym (np. Unit1.h) umieszczamy mapę komunikatów odpowiedzialną za przechwycenie komunikatu o przeciągnięciu pliku, a w sekcji private umieszczamy deklarację funkcji przechwytującej:

 

// Plik nagłówkowy np. Unit1.h.
//--------------------------------

private:
        void OnDropFiles(TMessage &Message);

public:
        __fastcall TForm1(TComponent* Owner);

BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_DROPFILES, TMessage, OnDropFiles);
END_MESSAGE_MAP(TForm);
};
//--------------------------------

Umieszczamy na formularzu obiekt RichEdit1, a następnie tworzymy definicję funkcji przechwytującej, funkcja ta o przechwyceniu komunikatu o przeciągnięciu wykonuje zlecone działanie, może to być dowolne zadanie, ja jednak umieszczę instrukcję ładującą plik do RichEdit. Konieczne jest również umieszczenie w zdarzeniu OnCreate dla formularza funkcji DragAcceptFile akceptującej przeciąganie, Funkcja pobiera dwa argumenty, pierwszy to uchwyt do okna, które ma obsługiwać przechwytywanie, drugi parametr to zmienna typu bool włączająca lub wyłączająca przechwytywanie. W zdarzeniu OnCLoseQuery umieszczamy funkcję wyłączającą akceptowanie przeciąganych plików.

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

void TForm1::OnDropFiles(TMessage &Message)
{
 char buffer[256];

 DragQueryFile((HDROP)Message.WParam, 0, buffer, 256);
 RichEdit1->Lines->LoadFromFile((AnsiString)buffer);

 DragFinish((HDROP)Message.WParam);
}
//--------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
 DragAcceptFiles(Handle, true);
}
//--------------------------------
void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
{
 DragAcceptFiles(Handle, false);
}
//--------------------------------


...powrót do menu. 

   Bitmapa jako tło w Memo.

 

Wykorzystując metodę WindowProc możemy w obiekcie typu TMemo umieścić jako tło grafikę w formacie *.bmp. Przedstawiony sposób działa prawidłowo, jednak nie jest doskonały, wadą jest np. brak płynnego przesuwania "pasków przewijania", ma to związek z blokadą odświeżania, poza tym w komputerach ze słabą kartą graficzną tło może migać podczas przewijania. Pomimo podobieństwa Memo do RichEdit, to w tym drugim nie będzie działać to prawidłowo, tło się pojawi, ale nie będzie tekstu.

W pliku nagłówkowym umieszczamy deklaracje:

 

// Plik nagłówkowy np. Unit1.h
public:
        __fastcall TForm1(TComponent* Owner);
        TWndMethod OldMemoWP;
        Graphics::TBitmap *Bmp;

        void __fastcall NewMemoWP(TMessage &Msg);
        void __fastcall WMCtlColorEdit(TMessage &Msg);

BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_CTLCOLOREDIT, TMessage, WMCtlColorEdit)
END_MESSAGE_MAP(TForm)


Definicje w pliku źródłowym:

 

// Plik nagłówkowy np. Unit1.h
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) // konstruktor klasy
        : TForm(Owner)
{
 Memo1->DoubleBuffered = true;
 OldMemoWP = Memo1->WindowProc;
 Memo1->WindowProc = NewMemoWP;
 Bmp = new Graphics::TBitmap;
 Bmp->LoadFromFile(ExtractFilePath(ParamStr(0)) + "Fantasy1.bmp");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::NewMemoWP(TMessage &Msg)
{
 if(Msg.Msg == WM_PAINT)
  InvalidateRect(Memo1->Handle, NULL, false);

 OldMemoWP(Msg);

 if(Msg.Msg == WM_ERASEBKGND)
 {
  LockWindowUpdate(Memo1->Handle);
  HDC Hdc = (HDC)Msg.WParam;
  StretchBlt(Hdc, 0, 0, Memo1->Width, Memo1->Height, Bmp->Canvas->Handle, 0, 0, Bmp->Width, Bmp->Height, SRCCOPY);
  Msg.Result = 0;
  LockWindowUpdate(0);
  return;
 }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WMCtlColorEdit(TMessage &Msg)
{
 if((HWND)Msg.LParam == Memo1->Handle)
 {
  HDC Hdc = (HDC)Msg.WParam;
  ::SetTextColor(Hdc, ColorToRGB(Memo1->Font->Color));
  ::SetBkMode(Hdc, TRANSPARENT);
  Msg.Result = (LONG)Memo1->Brush->Handle;
 }
 else
  TForm::Dispatch(&Msg);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
 Memo1->WindowProc = OldMemoWP;
}
//---------------------------------------------------------------------------

 

Jak wcześniej wspomniałem, kod uniemożliwia płynne przesuwanie pasków przewijania, trzeba cały czas klikać żeby się przesuwały. Można ten błąd wyeliminować usuwając z funkcji NewMemoWP funkcje LockWindowUpdate(Memo1->Handle) i LockWindowUpdate(0). Wspomniane funkcje blokują odświeżanie obiektu Memo1, ale tylko w czasie odrysowywania tła, niemniej jednak ma to wpływ na paski przewijania. Funkcja Memo1->DoubleBuffered zapobiega pojawiania się artefaktów w postaci powielonych linii tekstu w Memo1, jednak nie zawsze może prawidłowo działać. No i największa wada to, że funkcja działa prawidłowo tylko w systemach Windows NT/2000/XP, w Win95/95 wystąpią problemy z wyświetlaniem tekstu.

...powrót do menu. 

    Przepisywanie zawartości z jednego RichEdit do drugiego z zachowaniem formatowania.

 

Zawartość jednego RichEdit do drugiego można bez problemu przepisać poprzez jego właściwość Text lub Lines, jednak przy takim przepisywaniu okaże się że tekst co prawda się przeniósł, ale formatowanie tekstu już nie.
Do przeniesienia tekstu wraz z formatowaniem potrzebny jest obiekt typu TMemoryStream do któego pierwszy RichEdit przepisze swoją zawartość i z którego drugi RichEdit tą zawartość pobierze:

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 RichEdit1->Lines->LoadFromFile("c:\\overview.rtf");

 TMemoryStream* s = new TMemoryStream();

 RichEdit1->Lines->SaveToStream(s);
 s->Position = 0;
 RichEdit2->Lines->LoadFromStream(s);

 delete s;
}
//--------------------------------

 

...powrót do menu. 

 

   Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu kinio.

 

Komponenty takie jak Memo, RichEdit itp. nie wyświetlają do końca całego pliku ponieważ taka jest natura typu char. Jeżeli masz łańcuch tekstu jak tablice typu char* to zostanie on wyświetlony do momentu kiedy zostanie napotkany znak NULL. W poniższym kodzie jak widać jest on zamieniony na spacje. Możesz go dowolnie zmienić, lub nawet usunać.

 

// Plik źródłowy np. Unit1.cpp.
//--------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 int iFileHandle;
 int iFileLength;
 int iBytesRead;
 int iBytesWrite = 0;
 char *pszBuffer;

 if(OpenDialog1->Execute())
 {
  try
  {
   iFileHandle = FileOpen(OpenDialog1->FileName, fmOpenRead);
   iFileLength = FileSeek(iFileHandle, 0, 2);
   FileSeek(iFileHandle, 0, 0);
   pszBuffer = new char[iFileLength+1];
   iBytesRead = FileRead(iFileHandle, pszBuffer, iFileLength);
   FileClose(iFileHandle);

   while(iBytesWrite < iBytesRead)
   {
    if(pszBuffer[iBytesWrite] == 0)
    pszBuffer[iBytesWrite] = ' ';
    iBytesWrite++;
   }

   Memo1->Lines->SetText(pszBuffer);
   delete [] pszBuffer;
  }
  catch(...)
  {
   Application->MessageBox("Can't perform one of the following file operations: Open, Seek, Read, Close.", "File Error", IDOK);
  }
 }
}
//--------------------------------

 

...powrót do menu.