TreeviewのD&Dについて


  2012-02-20 22:41:28  No: 41647

お世話になります。

現在、D7でTreeViewのD&Dに挑戦しているのですが、考え方というか方法が判らずに悩んでいます。

たとえば、子の部分にドロップした場合は子の子になってしまうのですが
同階層でしたに入れたい場合どのような感じになるのでしょうか?

よく、項目の間に線が出てきて、そこに挿入しささるというものを見かけるのですが、あれの実装が希望です。

すいませんが、御指南くださいませ。


a  2012-02-21 08:18:04  No: 41648

Google 「Delphi TreeView D&D」でいっぱいあります


  2012-02-21 11:08:07  No: 41649

>Google 「Delphi TreeView D&D」でいっぱいあります

それがいっぱい無いんですよ。

子に移動のD&Dのやりかたではなく、間に入れる方法はまったくでてきません。
親自体にドロップし、子としての移動はできますが子と子の間に移動。親と親の間が不明なんです。


Mr.XRAY  2012-02-21 15:32:29  No: 41650

こんにちは,Mr.XRAYです.

TreeViewのItemのDrag and Dropは,ノードの階層下に追加するのが仕様のハズです.
変更したければ,マウスの現在位置から,判定して,その任意のレベルのノードにItemを追加する
という作業になるのではないかと思います.
ただし,個人的には疑問です.ユーザが

「あれ!? ドラッグでノードの下に追加なるんじゃなかったっけ? 同じレベルに追加されちゃった」

ということにもなりかねませんから.

>よく、項目の間に線が出てきて、

これはカスタム描画がが必要です.GetDragImagesという関数を使用しますが,
これは,protected部にあります.
したがって,TreeViewを継承したクラス(つまり,コンポーネント)を作成するか,
protected部にアクセスする仕組みが必要です.
参考コードが以下にあります.

http://www.delphidabbler.com/tips/119

面倒であれば,TreeViewを拡張したコンポーネントを提供しているところがあります.
それを利用してみてはいかがでしょうか.

VirtualTreeViewというコンポーネントを使用した nanaTreeというのがあります.
これはソースコード付きですから,ダウンロードして研究してみてはいかがでしょうか.

nanaTreeの参考ページ

NanaTree Ver1.05の公開とオープンソース化のお知らせ
http://yourcolor.seesaa.net/article/134778238.html

NanaTree について
http://mrxray.on.coocan.jp/bbs/DelphiBBS/mrxray_delphifan_coffe.cgi?tree=s2848#2848


Mr.XRAY  2012-02-21 15:53:13  No: 41651

>ノードの階層下に追加するのが仕様のハズです.

だと思います.
すでに,Drag and Dropのテストをしているのであれば理解しているとは思いますが,
OnDragDropイベントで処理しますから,いくらでも仕様の変更は可能です.


Mr.XRAY  2012-02-22 08:24:13  No: 41652

こんにちは.Mr.XRAYです.

朝の寝ぼけたレスで,ちょと変な説明でスミマセン.
言いたかったのは,以下のようなことです.

ネット上のサンプルコードを見ると,OnDragDropイベントにコードが書いてあると思います.
このイベントのコード次のように,何も処理がないコードにします.

procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
///
end;

すると,ドラッグ・ドロップしても,ノードの追加はしてくれません.
つまり,実際にノード(Item)を追加するのは,このOnDragDrop内に書いたコードが行っているわけです.
これは,自分の希望のItemなり,ノードの追加等のコードを,ここに自由に書けることを意味しています.

ということです.


monaa  2012-02-23 09:40:05  No: 41653

>よく、項目の間に線が出てきて、そこに挿入しささるというものを見かけるのですが、あれの実装が希望です。
以前見たなーと感じたのでお役にたてば。
http://www2.big.or.jp/~osamu/Delphi/tips.cgi?index=0316.txt
これを少し変更すればいけると思います。


Mr.XRAY  2012-02-26 10:09:17  No: 41654

こんにちは,Mr.XRAYです.

>http://www2.big.or.jp/~osamu/Delphi/tips.cgi?index=0316.txt
>これを少し変更すればいけると思います。

ですね.後は工夫次第ですね.
私が前に提示した,GetDragImagesは,残念ながら,ImageListでビットマップを設定していないとダメでした.
スミマセン.

子ノードとしてではなく,(移動)挿入ですが,
1つの考え方として,各ノードにアイコン(画像)が表示されているものとして,
ドラッグする時に,このアイコンでドラッグ開始した時はインサート.テキスト部分からドラッグ開始した
場合は,子ノード追加というような仕様にするとすれば,
例えば次のようなコードで可能です.

これも,動作仕様をいろいろ検討する必要があるように思います.

procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  SourceNode : TTreeNode;
  DestNode   : TTreeNode;
  AHit       : THitTests;
begin
  //選択ノード
  SourceNode :=TreeView1.Selected;
  //ターゲットノード
  DestNode := TreeView1.DropTarget;

  AHit := TreeView1.GetHitTestInfoAt(X, Y);

  //もしノードの画像(ビットマップ)でマウスダウンしてドラッグしてきたら
  //DestNodeにインサートする
  if (htOnIcon in AHit) then begin
    SourceNode.MoveTo(DestNode, naInsert);

  //それ以外,つまりテキスト部分であったら子ノードとして追加する
  end else begin
    SourceNode.MoveTo(DestNode, naAddChild);
  end;
end;


Mr.XRAY  2012-02-26 10:15:55  No: 41655

別の考え方として,横線を描画するのであれば,
横線が,ドロップ先のアイコンとアイコンの間にあれば挿入,それ以外では子ノードに追加.
というのもありかも知れません.

この場合,子ノードに追加の状態であれば,横線は表示しないとか.いや,してもいいのかな?
残念ですが,想像力が働きません.


Mr.XRAY  2012-02-26 22:47:42  No: 41656

こんにちは.Mr.XRAYです.

折角の質問ですので,できればサンプルプログラムを作ってみたいと考えています.
動作仕様のいいアイディアがありましたらお願いします.
どういう場合に,ノードの追加,または子ノードとして追加とするか,
アイコンが表示されている場合と,アイコンがない場合の動作をどうするか,等ですね.

なお,{Ctrl] キーを押した場合とか,キー操作は伴わないものとします.


Mr.XRAY  2012-02-27 00:27:10  No: 41657

>横線が,ドロップ先のアイコンとアイコンの間にあれば挿入,それ以外では子ノードに追加.
>というのもありかも知れません.

あっ,これダメですね.いいアイディアだと思ったんですが,
既に子ノードがあるとき,一番上の子ノードと,その親ノードの間に横線がある時にどうするか,
判定基準が...  う〜む.難しい.

このノードの挿入でよくあるのは,上下の矢印があってっ,それで,上とか下に移動というのはありますね.
やっぱり確定的な仕様の作成は難しいかな.


Nov  2012-02-27 02:14:17  No: 41658

Acrobatの場合は、マウスカーソルが、追加先のアイテムのイメージ部にポイントすると、イメージの下まで選択線が延びて同階層に追加されます。文字列部にポイントしたときには、文字列の下のみ選択線が描画され、子ノードに追加されます。


Mr,XRAY  2012-02-27 02:53:29  No: 41659

こんにちは.
Novさんどうもです.

>Acrobatの場合は、

なるほど,目次の部分ですね.
訊いてみるもんです.気が付きませんでした.

質問された方には,悪いことを言ってしまいましたね.スミマセン.
言い訳になりますが,TreeViewというと,エクスプローラぐらしか頭に入ってきませんでした.
この方針で,やってみたいと思います.
ありがとうございます.


KHE00221  2012-02-27 04:14:36  No: 41660

[CTRL] 押しながらで COPY 押してないと MOVE
Selected と DropTarget が同じ場合 同階層 違う場合 Child にする
コピー先に同名があれば ~1 ~2 と付けて行く

procedure TForm3.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
    I,J: Integer;
    B: Boolean;
begin

    if TreeView1.DropTarget = TreeView1.Selected  then
    begin
      //[CTRL] が押されている場合のみ
      if (GetAsyncKeyState(VK_CONTROL) and $8000) <> 0 then
      begin
        J := 1;
        while J <> 0 do
        begin
          B := True;
          for I := 0 to TreeView1.DropTarget.Owner.Count - 1 do
          begin
            if TreeView1.DropTarget.Owner.Item[I].Text = TreeView1.Selected.Text + '~' + IntToStr(J) then B := False;
          end;
          if B = False then
          begin
            Inc(J);
          end
          else
          begin
            TreeView1.Items.Add(TreeView1.DropTarget,TreeView1.Selected.Text + '~'+IntToStr(J));
            J := 0;
          end;
        end;
      end;
    end
    else
    begin
      //同名があるか確認
      B := True;
      for I := 0 to TreeView1.DropTarget.Count - 1 do
      begin
        if TreeView1.DropTarget.Item[I].Text = TreeView1.Selected.Text then B := False;
      end;
      //同名が無い場合
      if B = True then
      begin
        TreeView1.Items.AddChild(TreeView1.DropTarget,TreeView1.Selected.Text);
      end
      else
      begin
        //同名がある場合
        J := 1;
        while J <> 0 do
        begin
          B := True;
          for I := 0 to TreeView1.DropTarget.Count - 1 do
          begin
            if TreeView1.DropTarget.Item[I].Text = TreeView1.Selected.Text + '~' + IntToStr(J) then B := False;
          end;
          if B = False then
          begin
            Inc(J);
          end
          else
          begin
            TreeView1.Items.AddChild(TreeView1.DropTarget,TreeView1.Selected.Text + '~' + IntToStr(J));
            J := 0;
          end;
        end;
      end;
      //MOVEの場合移動元を削除
      if (GetAsyncKeyState(VK_CONTROL) and $8000) = 0 then
      begin
        TreeView1.Selected.Delete;
      end;
    end;

end;

procedure TForm3.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
    Accept := True;
    if (GetAsyncKeyState(VK_CONTROL) and $8000) = 0 then
    begin
      TreeView1.DragCursor := crDefault;
    end
    else
    begin
      TreeView1.DragCursor := crDrag;
    end;
end;


Mr.XRAY  2012-02-27 05:45:22  No: 41661

KHE00221さんどうも.コードサンクスです.
早速やってしまいました.

>[CTRL] 押しながらで COPY 押してないと MOVE

これは私も考えたんですよね.

>Selected と DropTarget が同じ場合 同階層 違う場合 Child にする

なるほど.この仕様もありですね.

>コピー先に同名があれば ~1 ~2 と付けて行く

うんうん.エクスプローラ風ですね.
これはちょっと考えたいですね.
どんな状況で,どんなアプリで使えかにもよりますが,いっそのこと同名はコピーできないとか....

まだまだアイディア募集中です.って,最初の質問者を無視していいのか?
当方はのんびりやります.切っ掛となった質問者はお隠れになっているようですので(笑).
スレッドを乗っ取ってしまったのかな?

ところで,KHE00221さんのサイト,コンテンツが少し少なくなっているよう気もしますが.
整備中なのでしょうか.気のせいかしら.


au  2012-02-27 21:27:12  No: 41662

とりあえず、移動のみのサンプル書いてみました。
http://d.hatena.ne.jp/au2010/20120227/1330313151


Mr.XRAY  2012-02-27 23:38:18  No: 41663

こんにちは.Mr.XRAYです.

>とりあえず、移動のみのサンプル書いてみました。
>http://d.hatena.ne.jp/au2010/20120227/1330313151

早っ!!  いですね.
私なんかまだ,PDFの目次部分の操作仕様を観ているだけです.

そうですか.「移動先の表示はTVM_SETINSERTMARKメッセージ」を使うんですか.
図があるとどんな動作をするコードか理解できていいですねぇ.
サンプルコード使わせてください.
例によって,いつになるか分かりませんが,当サイトにも掲載したいのです.
よろしく!! m(_ _)m

動作仕様はこれで決まりですね.


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加