☆TreeViewのお手軽なUndo処理
前回のTreeViewのDrag&Dropのコードを使って、お手軽なUndo処理(手抜き?)を紹介します。
一番お手軽なのは、SaveToFile、LoadFromFileを使って一時ファイルでやり取りする方法です。しかしこれでは、展開状態を別に保存する必要があり面倒です。そこでもう一つTreeViewを用意してクローンを作成することにします。
まず、前回のコードの中で、TreeView1DragDropを次のように修正します。(2行追加するだけ)
フォームにTreeViewを1つ、SpeedButtonを2つ追加します。
そして、次のコードを最後に追加します。
あとは実行するだけです。移動や削除をしてからUndoボタンを押すと、Undoできます。処理前にUndoRegistを呼び出せば、追加、挿入時もこのUndo機能が使えます。 Undo後は、MakeVisibleで、Undo前の選択ノード表示していますが、TopItemにこだわるのであれば、(B)をコメントアウトして、(A)を有効にします。 TopItemへの設定は、TreeView1.Items.BeginUpdate~EndUpdate内では無効みたいなので、ちょっとちらつきます。
TreeViewのアイテム数にもよりますが、最近のマシンでは、これぐらいのメモリの無駄は問題ないと思います。しかし、これはあくまで一つのサンプルなので、きちんとしたアプリでは、クラスとしてUndoList、RedoListを作成する 、ファイルを利用する等で、リソースが「MOTTAINAI」と言われないような処理を心掛けたいですね(笑)
[2007/08/04 am10:54 全面的に訂正しました]
展開状態を設定し忘れていたからです(^^;
一番お手軽なのは、SaveToFile、LoadFromFileを使って一時ファイルでやり取りする方法です。しかしこれでは、展開状態を別に保存する必要があり面倒です。そこでもう一つTreeViewを用意してクローンを作成することにします。
まず、前回のコードの中で、TreeView1DragDropを次のように修正します。(2行追加するだけ)
procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer); var srcNode, dstNode: TTreeNode; HT: THitTests; begin // Explorer等は、ファイル名でソートされていますが、 // ソートではなく意図したノードの順番に移動させたい場合があります。 // そのような時に私のアプリでは、 // アイコンで挿入移動、文字列で子追加移動という仕様にしています。 // これって結構便利だと思うんですけど、あまり採用されてないみたい。 srcNode :=TreeView1.Selected; // 選択ノード dstNode := TreeView1.DropTarget; // ドロップターゲットノード // Undo用TreeViewに登録させます。 //←追加 UndoRegist; //←追加 HT := TreeView1.GetHitTestInfoAt(X, Y); if (htOnIcon in HT) then srcNode.MoveTo(dstNode, naInsert) // アイコンを選択している場合 else srcNode.MoveTo(dstNode, naAddChild); // 項目を選択している場合 end;次にUndoの処理です。
フォームにTreeViewを1つ、SpeedButtonを2つ追加します。
そして、次のコードを最後に追加します。
var idx1: Integer; CanUndo: Boolean = False; // TopIndex: Integer; // (A) // Undo用TreeViewに登録させます。 procedure TForm1.UndoRegist; var I: Integer; F: Boolean; begin TreeView1.Items.BeginUpdate; try // TopIndex := TreeView1.TopItem.AbsoluteIndex; // (A) if TreeView1.Selected = nil then idx1 := -1 else idx1 := TreeView1.Selected.AbsoluteIndex; TreeView2.Items.Assign(TreeView1.Items); // 展開状態の保存 for I := TreeView1.Items.Count -1 downto 0 do begin F := TreeView1.Items[I].Expanded; if TreeView2.Items[I].Expanded <> F then TreeView2.Items[I].Expanded := True; end; CanUndo := True; finally TreeView1.Items.EndUpdate; end; end; // Undo procedure TForm1.SpeedButton1Click(Sender: TObject); var I: Integer; F: Boolean; begin // Undoできない場合の処理 if not CanUndo then Exit; TreeView1.Items.BeginUpdate; try TreeView1.Items.Assign(TreeView2.Items); // 展開状態の再現 for I := TreeView2.Items.Count -1 downto 0 do begin F := TreeView2.Items[I].Expanded; if TreeView1.Items[I].Expanded <> F then TreeView1.Items[I].Expanded := True; end; if idx1 >= 0 then TreeView1.Items[idx1].Selected := True; TreeView1.Selected.MakeVisible; // (B) CanUndo := False; finally TreeView1.Items.EndUpdate; // TreeView1.TopItem := TreeView1.Items[TopIndex]; // (A) end; end; // Delete procedure TForm1.SpeedButton2Click(Sender: TObject); begin if TreeView1.Selected <> nil then begin // Undo用TreeViewに登録させます。 UndoRegist; TreeView1.Selected.Delete; end; end;
あとは実行するだけです。移動や削除をしてからUndoボタンを押すと、Undoできます。処理前にUndoRegistを呼び出せば、追加、挿入時もこのUndo機能が使えます。 Undo後は、MakeVisibleで、Undo前の選択ノード表示していますが、TopItemにこだわるのであれば、(B)をコメントアウトして、(A)を有効にします。 TopItemへの設定は、TreeView1.Items.BeginUpdate~EndUpdate内では無効みたいなので、ちょっとちらつきます。
TreeViewのアイテム数にもよりますが、最近のマシンでは、これぐらいのメモリの無駄は問題ないと思います。しかし、これはあくまで一つのサンプルなので、きちんとしたアプリでは、クラスとしてUndoList、RedoListを作成する 、ファイルを利用する等で、リソースが「MOTTAINAI」と言われないような処理を心掛けたいですね(笑)
[2007/08/04 am10:54 全面的に訂正しました]
展開状態を設定し忘れていたからです(^^;
| 固定リンク