ListViewで特定の値に一致する場合に太字するには?

解決


arigayas  URL  2013-07-16 23:26:42  No: 44861

環境は Delphi XE + Windows 7 です。

ListView [ViewStyle=vsReport]にした状態で読み込んだXMLファイルの特定の属性の値に一致する場合に
特定のColumnのコメントの文字を目立つようにしたいんですが、どうすれば良いんでしょうか?
もしくは特定のColumnのみ目立つようにするのが無理なら該当する行が目立つように出来れば嬉しいです。

XMLファイルは以下のようなファイルです。
<?xml version='1.0' encoding='UTF-8'?>
<root>
<Data no="1" user_id="RCtVZoj6GmAFDN7nkCmiPOk4Q6w" premium="1" date="1373951872">こんにちは</Data>
<Data no="2" user_id="-U-a1fFoCb9Sd5GqOSNbXs8__Vk" premium="2" date="1373951873">よろしくお願いします。</Data>
<Data no="3" user_id="1CIKJhglBzBkAqi9HUKmY6awOFg" date="1373951874">わーい</Data>
</root>

ListView のColumnのタイトル?は、 [no | user_id | コメント | date] という並びです。
それぞれの値を変数に代入してListViewに表示するのは出来てます。

premiumの値が1の場合は太字、2の場合では赤文字にしたいです。
ググって試しても上手く太字にならないので質問させていただきました。


DEKO  2013-07-17 01:24:35  No: 44862

> ググって試しても上手く太字にならないので質問させていただきました。

その "試してダメだったコード" を晒して頂けると話は早いかと思います。


DEKO  2013-07-17 01:26:42  No: 44863

> その "試してダメだったコード" を晒して頂けると話は早いかと思います。

参考にしたサイトの URL も貼って頂けるとなお Good です。


arigayas  URL  2013-07-17 03:40:00  No: 44864

DEKOさん、返信していただきありがとうございます。

<Data>1つ分をListViewに追加するソースコードは
  LineStrings.Add(UserId);
  LineStrings.Add(Comment);
  LineStrings.Add(UnixTimeToDatetimeStr(StrToInt64(dateTime)));

  if Premium ='1' then
    ListView1.Canvas.Font.Style := [fsBold];

  AddListItem(CommentNo,LineStrings); ←自作関数にしてます。
こんな感じです。


  2013-07-17 04:06:31  No: 44865

http://owlsperspective.blogspot.jp/2008/07/tlistviewoncustomdrawitem.html

これではどうですか。


Harry  2013-07-17 05:23:07  No: 44866

arigayasさん、データをListViewに書き込むときにFontの属性を決定させることは出来ませんよ。
各Itemが描画される直前にCanvasのFontのプロパティに手を加える、というようにしないと。

もう少し具体的にお手伝いしてみますと…このような仕掛けはいかがですか?
http://i.imgur.com/xNHWx87.png

上も下もまったく同じデータが入ったListViewです。
SubItems[3] として、Premium のデータを入れてあります。
arigayasさんのコードなら、LineStrings.Add(Premium); を追加でしょうかね?

上と下の違いは、下のListViewには5番目のカラムを作っていないため、Premium のデータが
非表示な状態になっている、ということです。(非表示でもデータの入出力は普通に出来ます。)

この状態で以下のOnCustomDrawItemイベントハンドラを入れればOkです。

procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
var
  TL: TListView;
  PL: Integer;
begin
  if Item.SubItems.Count<4 then Exit; // SubItems[3]、つまり Premium が無いなら処理しない

  TL:=Sender as TListView;
  case StrToIntDef(Item.SubItems[3], 9999) of // 数字以外なら9999にしてcase文をスルーさせる
    1: TL.Canvas.Font.Style:=TL.Font.Style+[fsBold];
    2: TL.Canvas.Font.Color:=clRed;
  end;
end;

非表示のデータを作ってそれを利用する方法のほか、TListItem.Dataに整数値を入れておく
方法もあると思います。
  入れる   ListItem.Data:=Pointer(StrToInt(Premium));
  取り出す PL:=Integer(ListItem.Data);
みたいな感じで。


Harry  2013-07-17 05:36:03  No: 44867

あっ、var の PL: Integer; は消し忘れです。不要ですのでよろしく。テヘペロ


DEKO  2013-07-17 08:29:46  No: 44868

Harry さんに殆ど書かれてしまいましたが...

元のコードでは TListItem に Premium の値が保持されていないようですので、

・Premium カラムを作る
・非表示な Premium カラムを作る
・TListItem.Data に整数値を入れる (解放の必要はない)
・TListItem.Data に構造体或いはクラスへのポインタを入れる (解放の必要がある)

いずれかの方法で Premium の値を保持する必要があります。

今回、Premium は整数値のようですから、
Harry さんの仰っていた "TListItem.Data に整数値を入れる方法" がスマートでいいかと思います。
(String <-> Integer 変換も不要ですし)

# その他の(XML の)属性も非表示で保持する必要があるのならば、
# TListItem.Data に構造体或いはクラスをぶら下げる構造の方がいいと思います。

リストアイテムが描画される時に OnCustomDrawItem イベントが発生しますので、
そのイベントハンドラ内で Premium の値を判断し、
フォントの色やスタイルを変更してあげれば目的の事が達成できます。

逆に言えば、OnCustomDrawItem イベントが発生した時に TListItem から
Premium の値を判断できないのであれば "うまくいかない" 事になります。


arigayas  2013-07-17 23:43:04  No: 44869

あ さん、Harry さん、DEKO さん、回答していだきありがとうございます。

Harry さんのコードを参考にして「LineStrings.Add(Premium);」を追加でほぼ思い通りになりました。
赤字には1行全部がなるんですが
なぜか「no と user_id 」の二つだけが太字になるのが謎です・・・。

DEKO さん、解説していだきありがとうございます。だいぶ解りました!


Harry  2013-07-18 08:58:57  No: 44870

>なぜか「no と user_id 」の二つだけが太字になるのが謎です・・・。
これって一行全部太字になるはずが、no と user_id だけが太字、コメント と date が
標準のまま、ということですよね?

通常、一行全部を同じフォント属性にするならこれで良いと思うのですが…。
うーん、テーマとかメイリオとかClearTypeとかチェックしましたが、こちらでは再現しない
のですよー。 (Delphi 6 Personl/XE4、WindowsXP)

一応、OnCustomDrawSubItemも加えたらどうなるか、やってみて下さい。
オブジェクトインスペクタへのイベントハンドラの登録をお忘れなく。
※数字かどうかのチェック方法を、あえて変えてみました。

procedure SetLVItemFont(ACustomListView: TCustomListView; AListItem: TListItem);
var
  LV: TListView;
  PL: Integer;
begin
  if AListItem.SubItems.Count<4 then Exit;                 // SubItems[3](Premium)が無いなら処理しない
  if not TryStrToInt(AListItem.SubItems[3], PL) then Exit; // 数字以外なら処理しない

  LV:=ACustomListView as TListView;
  case PL of
    1: LV.Canvas.Font.Style:=LV.Font.Style+[fsBold]; // LV.Canvas.Font.Style+[fsBold] だと変わるか??
    2: LV.Canvas.Font.Color:=clRed;
  end;
end;

procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  SetLVItemFont(Sender, Item);
end;

procedure TForm1.ListView1CustomDrawSubItem(Sender: TCustomListView;
  Item: TListItem; SubItem: Integer; State: TCustomDrawState;
  var DefaultDraw: Boolean);
begin
  SetLVItemFont(Sender, Item);
end;

TListItem.Dataを使う方法も簡単ですから、ぜひトライしてください。
そうすれば、冒頭にあるチェックのための2行も不要になります。


arigayas  URL  2013-07-18 22:20:24  No: 44871

Harry さん、回答していだきありがとうございます。

>>なぜか「no と user_id 」の二つだけが太字になるのが謎です・・・。
>これって一行全部太字になるはずが、no と user_id だけが太字、コメント と date が
>標準のまま、ということですよね?
そういうことです。

>procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
>  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
>begin
>  SetLVItemFont(Sender, Item);
>end;

>procedure TForm1.ListView1CustomDrawSubItem(Sender: TCustomListView;
>  Item: TListItem; SubItem: Integer; State: TCustomDrawState;
>  var DefaultDraw: Boolean);
>begin
>  SetLVItemFont(Sender, Item);
>end;
それぞれコメントアウトにして片方だけを有効にして実行してみました。

ListView1CustomDrawItemだけを有効にして実行すると、
「no と user_id」だけが太字、「コメント と date」が標準のままになり、

ListView1CustomDrawSubItemだけを有効にして実行すると、
「no」以外の「user_id、コメント、date」が太字になりました。

この二つを上手くやると「user_id」だけを太字に出来そうと思えたんですが
(特定の列だけを太字にするのは)無理でしょうか?


arigayas  URL  2013-07-18 22:21:59  No: 44872

書き忘れましたが、ListView1CustomDrawItemとListView1CustomDrawSubItemを有効にしたら、1行全部太字になりました。


DEKO  2013-07-18 22:49:01  No: 44873

> premiumの値が1の場合は太字、2の場合では赤文字にしたいです。
ではなく、

"Premium の値が 1 の場合は user_id 列を太字、2 の場合には赤文字にしたい"

なのであれば、
OnCustomDrawItem イベントハンドラは不要で、OnCustomDrawSubItem だけで可能です。
SubItem が 0 (=user_id) の時だけ処理すればお望みの動作になると思います。

procedure TForm1.ListView1CustomDrawSubItem(Sender: TCustomListView;
  Item: TListItem; SubItem: Integer; State: TCustomDrawState;
  var DefaultDraw: Boolean);
begin
  if SubItem = 0 then
    SetLVItemFont(Sender, Item);
end;


Harry  2013-07-19 01:05:41  No: 44874

DEKOさん、ListViewの魔手に捕まってますよ!
SubItemパラメータは、SubItems[n] のIndexと一致しないです。
1, 2, 3,… と変化するので、Columnsと一致してます。
従って、カラム1のuser_idをターゲットにする時は、こうなります。>arigayasさん
  if SubItem = 1 then 〜

arigayasさん
>ListView1CustomDrawItemだけを有効にして実行すると、
>「no と user_id」だけが太字、「コメント と date」が標準のままになり、
いやー本当に不思議です。何か仕様が変わったのかなー?
もしよろしれば、参考のためキャプチャ画像をPleaseです。

>ListView1CustomDrawSubItemだけを有効にして実行すると、
>「no」以外の「user_id、コメント、date」が太字になりました。
うん、これは普通ですね。


DEKO  2013-07-19 01:09:18  No: 44875

> DEKOさん、ListViewの魔手に捕まってますよ!
> SubItemパラメータは、SubItems[n] のIndexと一致しないです。

アウチ。サブアイテムのインデックスは 1 からでしたか。


arigayas  URL  2013-07-19 11:12:07  No: 44876

Harry さん、DEKO さん、回答していだきありがとうございました。
おかげさまで「user_id」だけを太字に出来ました!!

> >ListView1CustomDrawItemだけを有効にして実行すると、
> >「no と user_id」だけが太字、「コメント と date」が標準のままになり、
> いやー本当に不思議です。何か仕様が変わったのかなー?
> もしよろしれば、参考のためキャプチャ画像をPleaseです。
「no と user_id」だけが太字の時のスクリーンショットです。> http://twitpic.com/d3caub/full


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

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






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