Delphi Programming / Object Pascal

[掲載 2012年05月08日] [更新 2012年08月24日] Delphi サンプルプログラム集 |
| 673_自アプリ内の TRichEdit のリッチテキスト ( RTF ) 送受信 |
| 動作確認等 |
Windows XP(SP3) + Delphi XE Pro
Windows 7 U64(SP1) + Delphi XE Pro |
 |
673_RichEdit_Stream.zip [1,916 KB] 2012年08月24日版 (EXE 同梱) |
- 2012年08月24日
- STREAMINを使用するコードを追加
|
自アプリ、同じアプリケーション内の TRichEdit のテキストの全体または一部を、書式付き、つまりリッチテキストとして送信または受信するサンプルです。
テキストのみのコピーや貼り付けは、プロパティやメソッドでも操作可能です。書式付のコピーや貼り付けも専用のメソッドがあります。EM_XXX 関係のメッセージでも処理できます。
本ページのサンプルは、主にストリームを使用したコードとなっています。 |
| 01_TRichEdit のリッチテキストのコード |
| RTF 形式のテキストを、メモリストリームに保存し、それを TRichEdit で読み込むと書式付きのテキストとして表示することができます。この確認のテストです。もちろん、拡張子が RTF のファイルを、直接 TRichEdit に読み込めば書式付きのテキストとして表示できます。 |
 |
図1
設計時画面 |
 |
図2
[Button1] クリック
- TMemo に RTF ファイルを読み込む
- RTF 形式の文字列が表示される
|
 |
図3
[Button2] クリック
- TMemo の文字列をメモリストリームに格納
- TRichEdit で、そのメモリストリームを読み込むと、書式付きテキストとして表示される
|
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Memo1: TMemo;
Splitter1: TSplitter;
RichEdit1: TRichEdit;
OpenDialog1: TOpenDialog;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
InitDir : String;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.ScrollBars := ssVertical;
RichEdit1.ScrollBars := ssBoth;
RichEdit1.HideSelection := False;
end;
//=============================================================================
// [Button1]クリック
// リッチテキスト形式のテキストをTMemoに読み込んで表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AFilePath : String;
begin
OpenDialog1.Filter := 'RTFファイル|*.RTF';
if InitDir = '' then begin
OpenDialog1.InitialDir := ExtractFileDir(Application.ExeName);
end;
if OpenDialog1.Execute then begin
AFilePath := OpenDialog1.FileName;
Memo1.Lines.LoadFromFile(AFilePath);
end;
end;
//=============================================================================
// [Button2]クリック
// Memo1に表示しているリッチテキストのコードをストリームに格納
// RichEditで、そのストリームを読み込むと、書式付きのテキストとして表示される
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
AStream : TMemoryStream;
begin
AStream := TMemoryStream.Create;
try
Memo1.Lines.SaveToStream(AStream);
RichEdit1.Clear;
AStream.Position := 0;
RichEdit1.Lines.LoadFromStream(AStream);
finally
AStream.Free;
end;
end;
end.
| 02_EM_STREAMOUT と EM_STREAMIN メッセージによる送受信 |
TRichEdit のリッチテキストを、他の TRichEdit のリッチテキストとして送信、転送します。
EM_STREAMOUT メッセージを使用します。このメッセージのコールバック関数内で送信元のリッチテキストをメモリストリームに格納します。受信側の TRichEditでは、EM_STREAMIN メッセージで、このメモリストリームを読み込みます。
クリップボードは使用しません。選択範囲だけの転送や、受信側では、挿入モードで受信可能なようにしてみました。
もし、常に全てのリッチテキストを転送するのであれば、EM_STREAMOUT メッセージを使用する必要はあません。また、受信する TRichEdit 側も、全てを受け取り、上書きモードでいいのであれば、次のコードのように、単にメモリストリームを仲介するだけで済みます。必要に応じて CopyFrom 等の関数類も使用できます。 |
リスト2
リッチテキストを他の TRichEdit に代入 |
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
FStream : TMemoryStream;
begin
FStream := TMemoryStream.Create;
try
//RichEdit1のリッチテキストをFStreamに格納
FStream.Position := 0;
RichEdit1.Lines.SaveToStream(FStream);
//FStreamの内容をRichEdit2に読み込む
FStream.Position := 0;
RichEdit2.Lines.LoadFromStream(FStream);
finally
FreeAndNil(FStream);
end;
end;
 |
図4
設計時画面
- 上が送信元の RichEdit
- 下が受信側の RichEdit
|
 |
図5
[読み込み] ボタンをクリック
- RTF ファイルを選択して表示
- 図は添付の RTF ファイルを開いたところ
|
 |
図6
[転送] ボタンをクリック
- 下の RichEdit に全て転送される
- [挿入で受信] をチェックすると、受信側では、受信した RTF 文書を現在のカーソル位置に挿入することができる
- 送信側の選択部分だけを挿入することもできる
|
 |
図7
選択範囲の転送
- 任意の範囲を選択
- [転送] ボタンをクリック
- 選択範囲だけが、下の RichEdit に表示
|
このサンプルでは、複数のリッチテキストを結合するテストもできます。
通常、リッチテキストの文書は、単純にストリームに続けて格納しただけでは、複数のリッチテキストをまとめて 1 つの文書にはできません。
- (1) [読み込み] ボタンでリッチテキストの文書を読み込む
- (2) [転送] ボタンをクリックすると、下の TRichEdit にその文書が表示される
- (3) [読み込み] ボタンで、別のリッチテキスト文書を読み込む
- (4) [挿入で受信] をチェックして、[転送] ボタンをクリックする
の手順で操作します。
下の TRichEdit の文書が 1 つの文書となっていることが確認できます。もちろん、選択部分だけを挿入することもできます。
最初にリッチテキスト文書を送信すると、受信側の入力位置、つまりカーソルは、文書の最後尾になります。ここが挿入位置となります。 |
リスト3
EM_STREAMOUT と EM_STREAMIN メッセージを使用したリッチテキストの送受信 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls, StdActns, Buttons;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
RichEdit1: TRichEdit;
Splitter1: TSplitter;
RichEdit2: TRichEdit;
OpenDialog1: TOpenDialog;
SpeedButton1: TSpeedButton;
CheckBox1: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
private
{ Private 宣言 }
InitDir : String;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses RichEdit;
{$R *.dfm}
var
FStream : TMemoryStream;
FStreamRec : TEditStream;
//-----------------------------------------------------------------------------
// EM_STREAMINメッセージで使用するコールバック関数
// FStreamからリッチテキストのデータを取得
// このコールバック関数は、複数回実行されることがある(データサイズによる)
//-----------------------------------------------------------------------------
function EditStreamCallBackStreamIn(dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): Longint; stdcall;
begin
try
pcb := FStream.Read(pbBuff^, cb);
Result:= 0;
except
Result := 1;
end;
end;
//-----------------------------------------------------------------------------
// EM_STREAMOUTメッセージで使用するコールバック関数
// FStreamにリッチテキストのデータが格納される
// このコールバック関数は、複数回実行されることがある(データサイズによる)
//-----------------------------------------------------------------------------
function EditStreamCallBackStreamOut(dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): Longint; stdcall;
begin
try
pcb := FStream.Write(pbBuff^, cb);
Result:= 0;
except
Result := 1;
end;
end;
//=============================================================================
// フォーム生成時の処理
// リッチテキストデータ格納用のTMemoryStreamを作成
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
RichEdit1.ScrollBars := ssBoth;
RichEdit2.ScrollBars := ssBoth;
RichEdit1.HideSelection := False;
RichEdit2.HideSelection := False;
FStream := TMemoryStream.Create;
end;
//=============================================================================
// フォームのOnDestroyイベント処理
// リッチテキストデータ格納用のTMemoryStreamを破棄
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FStream);
end;
//=============================================================================
// [読み込み]ボタンクリック
// リッチテキスト形式のテキストをRichEdit1に読み込んで表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AFilePath : String;
begin
OpenDialog1.Filter := 'RTFファイル|*.RTF';
if InitDir = '' then begin
OpenDialog1.InitialDir := ExtractFileDir(Application.ExeName);
end;
if OpenDialog1.Execute then begin
AFilePath := OpenDialog1.FileName;
RichEdit1.Lines.LoadFromFile(AFilePath);
RichEdit1.SetFocus;
end;
end;
//=============================================================================
// [転送]ボタン
//
// RichEdit1の内容をRichEdit2へ送信
// 受信側でEM_STREAMINメッセージを使用してFStreamから取得するコード
// FStreamRec(TEditStream)の使用には、usesにRichEditが必要
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
Flags : Integer;
begin
//選択中のテキストがあれば、選択部分のみ送信
if RichEdit1.SelText = '' then begin
Flags := SF_RTF;
end else begin
Flags := SF_RTF or SFF_SELECTION;
end;
//送り側のリッチテキストをEM_STREAMOUTメッセージを使用してFStreamに格納
FStream.Clear;
FStreamRec.pfnCallback := @EditStreamCallBackStreamOut;
//RichEdit1.Perform(EM_STREAMOUT, Flags, LPARAM(@FStreamRec));でも同じ
SendMessage(RichEdit1.Handle, EM_STREAMOUT, Flags, LPARAM(@FStreamRec));
//受信側のリッチテキストでEM_STREAMINメッセージを使用してFStreamから取得
//メモリストリームの位置を先頭に
FStream.Position := 0;
//フラグの設定
//SFF_SELECTIONは挿入モードで受け取る設定
if CheckBox1.Checked then begin
Flags := SF_RTF or SFF_SELECTION;
end else begin
Flags := SF_RTF;
end;
FStreamRec.pfnCallback := @EditStreamCallBackStreamIn;
//RichEdit2.Perform(EM_STREAMIN, Flags, LPARAM(@FStreamRec));でも同じ
SendMessage(RichEdit2.Handle, EM_STREAMIN, Flags, LPARAM(@FStreamRec));
end;
end.
もし、送信側のリッチテキストの全てを受信側に送るのであれば、EM_STREAMOUT メッセージを使用する必要はありません。リスト2 のように単にメモリストリームに格納するだけです。
次のコードは、送信側の全てのリッチテキスト文書を、受信側で挿入モードで受け取ります。したがって、メモリストリームに格納する送信側のリッチテキストを変更して、ループ処理すれば、複数のリッチテキスト文書を結合することができます。FStream (TMemoryStream) はグローバル変数にしておく必要があります。 |
リスト4
送信側の全てのリッチテキストを送信し、受信側では挿入モードで受け取る場合 |
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
Flags : Integer;
begin
//送り側のリッチテキストをFStreamに格納
FStream.Clear;
RichEdit1.Lines.SaveToStream(FStream);
//受信側のリッチテキストでEM_STREAMINメッセージを使用してFStreamから取得
//メモリストリームの位置を先頭に
FStream.Position := 0;
//受信側では常に挿入モードで受信する場合のフラグ
Flags := SF_RTF or SFF_SELECTION;
FStreamRec.pfnCallback := @EditStreamCallBackStreamIn;
//RichEdit2.Perform(EM_STREAMIN, Flags, LPARAM(@FStreamRec));でも同じ
SendMessage(RichEdit2.Handle, EM_STREAMIN, Flags, LPARAM(@FStreamRec));
end;
| 03_ [ コピー ] [ 貼り付け ] [元に戻す] 機能の実装 - TActionManager |
RichEdit 間の送受信は、クリップボードを使用した、コピー、貼り付け操作でも可能です。
例えば次のコードは、RichEdit1 の全てを、RichEdit2 に転送します。選択範囲だけをコピーするのであれば、SelectAll は要りません。 |
リスト5
クリップボードを使用したリッチテキストの送信 |
procedure TForm1.SpeedButton2Click(Sender: TObject);
begin
RichEdit1.SelectAll;
RichEdit1.CopyToClipboard;
RichEdit2.PasteFromClipboard;
end;
本題からはそれますが、TRichEdit のような入力関係コントロールに [コピー] や [貼り付け] 機能を実装するには、TActionListやTActionManager コンポーネントを使用すると便利です。
次の図は、TActionManager の設定画面です。[標準アクションの新規追加]で、[編集] で項目を選択すると、フォームに配置したボタン類、あるいは、TPopupMenu の Action プロパティに、該当する Action を設定するだけで、それらの機能が使用可能となります。 |
- 図8
- アプリケーションに [コピー] や [貼り付け] 機能を実装する
|
 |
TActionMamager コンポーネントを使用した、[コピー] と [貼り付け] 機能のテストです。
設計時には、SpeedButton の Caption は設定していません。実行時に TActionManager コンポが自動で設定します。もちろん、変更も可能です。また、ボタンクリックのイベントは必要ありません。 |
 |
図9
設計時画面 |
 |
図10
[読み込み] ボタンをクリック
|
 |
図11
RichEdit1 にフォーカス移動
- RichEdit11 にフォーカス移動してから
- [全て選択] をクリック
- [コピー] をクリック
|
 |
図12
RichEdit2 にフォーカス移動
- RichEdit2 (下) にフォーカスを移動
- [貼り付け] をクリック
|
リスト6
[コピー] や [貼り付け] [元に戻す] 機能のテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls, StdActns, ActnMan, Buttons,
ActnList, PlatformDefaultStyleActnCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
RichEdit1: TRichEdit;
Splitter1: TSplitter;
RichEdit2: TRichEdit;
OpenDialog1: TOpenDialog;
ActionManager1: TActionManager;
EditCut1: TEditCut;
EditCopy1: TEditCopy;
EditPaste1: TEditPaste;
EditSelectAll1: TEditSelectAll;
SpeedButton1: TSpeedButton;
SpeedButton2: TSpeedButton;
SpeedButton3: TSpeedButton;
EditUndo1: TEditUndo;
SpeedButton4: TSpeedButton;
FileOpen1: TFileOpen;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
InitDir : String;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// RichEditにフォーカスを持たせたいのでTSpeedButtonを使用
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
//実際にはオブジェクト・インスペクタで設定できる
RichEdit1.ScrollBars := ssBoth;
RichEdit2.ScrollBars := ssBoth;
RichEdit1.HideSelection := False;
RichEdit2.HideSelection := False;
//各スピードボタンにActionを割当て
//実際にはオブジェクト・インスペクタで設定できる
SpeedButton1.Action := EditSelectAll1;
SpeedButton2.Action := EditCopy1;
SpeedButton3.Action := EditPaste1;
SpeedButton4.Action := EditUndo1;
end;
//=============================================================================
// [読み込み]ボタンクリック
// リッチテキスト形式のテキストをRichEdit1に読み込んで表示
//
// ファイルを開くも標準アクション[ファイル][TFileOpne]で設定可能
// それを使用すれば以下のコードも不要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AFilePath : String;
begin
OpenDialog1.Filter := 'RTFファイル|*.RTF';
if InitDir = '' then begin
OpenDialog1.InitialDir := ExtractFileDir(Application.ExeName);
end;
if OpenDialog1.Execute then begin
AFilePath := OpenDialog1.FileName;
RichEdit1.Lines.LoadFromFile(AFilePath);
RichEdit1.SetFocus;
end;
end;
end.
|