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.
|