TreeViewの特定ノードのテキストカラーを変更するには?

解決


tt  2013-08-08 22:49:09  No: 44995

TreeViewの特定ノードのテキストカラーを変更したい場合、OnCustomDrawItemイベント内で
--
procedure TForm1.TreeView1CustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
    if Node.AbsoluteIndex = 1 then
    begin
        with Sender.Canvas.Font do
        begin
             Color := clRed;
        end;
    end;
end;
--
上記だと問題なく出来るのですが、OnCustomDrawItemイベントではない例えばOnClickイベント内
記述する場合(下記例は特定でなく全てのノードの色変更)、
procedure TForm1.Button1Click(Sender: TObject);
begin
    TreeView1.Canvas.Font.Color := clRed;
end;
色変更できません。TreeView1.Font.Color := clRed;だと変更できます。
TreeView1.Canvas.Font.Color := clRed;
TreeView1.Font.Color := clRed;
2例で何が違うのでしょうか?


Syake  2013-08-09 00:51:27  No: 44996

えー!
TTreeViewに限らず・・・・

TCanvasについて
http://support.embarcadero.com/article/36425

ご承知の通りTreeViewも内容はCanvasで描画されています。

Object.Font.Color
はそのオブジェクトのプロパティー値を変えています。
描画する時にCanvasは最初にそのオブジェクトのFont.Colorを参照します。
OnClickイベントなどで変更した場合、そのように全体の色が変わります。

Object.Canvas.Font.Color
はCanvasのFont.Colorの値を変えていることになります。
これから描画しようとしている文字列の色を指定することになります。
普通は、OnDrawに類するイベントで自身で描画する場合にそうします。
こうすることで、内容によって描画する文字の色を変更することもできます。

な、感じでいかがでしょう?


tt  2013-08-09 01:14:27  No: 44997

早速お返事ありがとうございました。
理解力が乏しく申し訳ないのですが、結局はObject.Canvas.Font.ColorはOnDrawに類するイベント内でしか
使えず、特定ノードの色変更の処理はOnDraw系イベントでしか書けないのでしょうか?
OnClick内で書きたい場合はどうすれば良いでしょう...


Syake  2013-08-09 01:46:41  No: 44998

OnDrawに類するイベント内でしか使えないわけではありません。

ただTreeViewの場合は結果的にそうだと思います。
他にもTStringListなども・・・。
多分、独自に描画したい(例えば内容によって色を変えたい)
等のご希望がおありのようですので通常はそうします。

OnClick内でどうしても書きたい場合は
そのイベント内で全てを描画処理を記述すれば良いでしょう。
with (Sender as TTreeView).Canvas do
begin
   //描画内容を全て記述
end;
でもまず、無駄でしょう...
データを変更した場合などや再表示時など再描画が必要な時などは
OnDraw何某かのイベントよばれます。
結果的にそこ描画処理を記述する事になります。  よね!(^_^)

OnClick内での描画処理に関係する記述は
強制的に再描画させる
(Sender as TTreeView).Repaint;
くらいでしょう。

と言うことでOnDraw何某かで描画処理を行うのが最も良いと思います。

説明がもとおりませんが、こんな感じで


Harry  2013-08-09 07:29:22  No: 44999

Syakeさん、ttさんが混乱しちゃうかもしれないので、、、

>他にもTStringListなども・・・。

↑これ、TListViewの書き間違いですよね。
TreeViewのオーナードローの仕組みは、TListViewと大変よく似ています。
以下はttさんと似たような相談内容ですので、参考になるでしょう。

ListViewで特定の値に一致する場合に太字するには?
https://www.petitmonte.com/bbs/answers?question_id=8048

>TreeView1.Canvas.Font.Color := clRed;  …(a)
>TreeView1.Font.Color := clRed;            …(b)
>2例で何が違うのでしょうか?

(a) はTreeView1.Canvas.Font.ColorをclRedにします。  (b) はTreeView1.Font.ColorをclRedにします。
そのままかよ! …って、そうなんです。それぞれ、これらだけでは特定のアイテムだけを任意の色にする
ということは出来ません。

>結局はObject.Canvas.Font.ColorはOnDrawに類するイベント内でしか使えず、
>特定ノードの色変更の処理はOnDraw系イベントでしか書けないのでしょうか?

どこでも使えますよ。でも、あるアイテムの色を変更して描画する目的ならOnDraw系イベント内に書きます。

原理と理屈を書きますと、おおよそこんな感じです。
1. 一つ一つのアイテムごと、その描画直前に…
    イベントハンドラが設定されていない場合 … デフォルトの描画が行われる。これでおしまい。
    イベントハンドラが設定されている場合 …OnCustomDrawItemが発生。以下に続く。
2. TreeView1.Canvas.FontにTreeView1.Fontの内容がコピー(VCL風に言うと、Assign)される。
3. イベントハンドラであるTreeView1CustomDrawItemが呼ばれる。
4. このタイミングでTreeView1.Canvas.Font.Colorを任意の色にすると、そのアイテムは任意の色で描画される。


Harry  2013-08-09 07:41:41  No: 45000

>OnClick内で書きたい場合はどうすれば良いでしょう...

OnClick内で「仕込み」をしておけばいいんです。

以下、サンプルコードです。
Button1を押すと、すべてのノードのフォント色を赤に変えます。(トグル動作)
Button2を押すと、フォント色が緑色のノードが移動して行きます。

そういえば、Delphiのバージョン、環境等を書いてくださいね。たまに動作が異なりますので。

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    TreeView1: TTreeView;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure TreeView1CustomDrawItem(Sender: TCustomTreeView;
      Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
  private
    { Private 宣言 }
    FAlternativeColor: Boolean;
    FSpecialIndex: Integer;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
const
  TestItems: array [0..3] of record
    Item: String;
    Child: array [0..2] of String;
  end =(
    (Item:'あいうえお'; Child:('あ', 'い', 'う')), (Item:'かきくけこ'; Child:('か', 'き', 'く')),
    (Item:'さしすせそ'; Child:('さ', 'し', 'す')), (Item:'たちつてと'; Child:('た', 'ち', 'つ'))
  );
var
  I, J: Integer;
  ANode: TTreeNode;
begin
  for I:=Low(TestItems) to High(TestItems) do begin
    ANode:=TreeView1.Items.Add(nil, TestItems[I].Item);
    for J:=Low(TestItems[I].Child) to High(TestItems[I].Child) do begin
      TreeView1.Items.AddChild(ANode, TestItems[I].Child[J]);
    end;
  end;
  TreeView1.FullExpand;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FAlternativeColor:=not FAlternativeColor;
  TreeView1.Invalidate; // 再描画を指示。Repaint/Update メソッドもヘルプで参照してください。
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Inc(FSpecialIndex);
  if FSpecialIndex=TreeView1.Items.Count then FSpecialIndex:=0;
  TreeView1.Invalidate;
end;

procedure TForm1.TreeView1CustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  // すべてのノードのテキストカラーをclRedにする
  if FAlternativeColor then Sender.Canvas.Font.Color:=clRed;

  // 特定ノードのテキストカラーをclLimeにする
  if Node.AbsoluteIndex=FSpecialIndex then Sender.Canvas.Font.Color:=clLime;
end;


Harry  2013-08-09 07:47:26  No: 45001

…で、自分が環境を書き忘れてる、の法則でした。(Delphi6 Personal、Windows XP)


tt  2013-08-09 18:06:58  No: 45002

Syakeさん  Harryさん
ご回答ありがとうございました。サンプルコードとリンクを参考にさせて頂いて、
今回やりたかったことが実現出来ました!1日中悩んでいたので本当に助かりました。
ご丁寧なご説明、どうもありがとうございました。


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

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






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