2012-08-12 15 views
9

Office XPスタイルのフォーカス/選択リストボックスアイテムを表示する最もシンプルでクリーンな方法は何ですか?選択矩形のようなOffice XPテーマのリストボックスを作成するには?

はアイデアがより明確に示すために、このサンプル画像を参照してください:

enter image description here

私はlbOwnerDrawFixedまたはlbOwnerDrawVariableのいずれかにリストボックスのスタイルを設定し、OnDrawItemイベントを変更する必要があると思いますか?

これは私が立ち往生だところ、私は、そこに書くことがどのようなコードは本当にわからない、これまで私が試している:

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
    Rect: TRect; State: TOwnerDrawState); 
begin 
    with (Control as TListBox).Canvas do 
    begin 
    if odSelected in State then 
    begin 
     Brush.Color := $00FCDDC0; 
     Pen.Color := $00FF9933; 
     FillRect(Rect); 
    end; 

    TextOut(Rect.Left, Rect.Top, TListBox(Control).Items[Index]); 
    end; 
end; 

私は働かないことが知られての必要があり、私はファンキーのすべての種類を取得起こって物事:

enter image description here

を私が間違って何をやっているが、もっと重要なのはどのような私はそれを動作させるために変更する必要がありますか?

ありがとうございました。

+0

私は、リストボックスをテーマにした選択のような種類があるとは思いません。リストボックスには['LBCP_ITEM'](http://msdn.microsoft.com/en-us/library/windows/desktop/bb(v = vs.85).aspx)の部分がありますが、非所有者の描画リストボックス - 退屈。だから多分テーマ選択部分を借りることができます。 ['this post'](http://stackoverflow.com/a/10936108/960757)に記載されているAndrewのようなツリービューから。リストボックスの状態のコードを変更するだけで済みます。 – TLama

+0

これは私が心配していたものです.TreeviewやListviewとは異なり、リストボックスはテーマ選択をペイントします。 –

+3

ハードコーディングの色は、ユーザーが完全に異なる配色になっていることを忘れないでください。 –

答えて

13

異なる状態のアイテムをペイントするのを忘れました。アイテムが現在どの状態にあるのか、それを描いているのかを判断する必要があります。

あなたの写真には何がありますか?連続選択 -

enter image description here

ボーナス:ItemHeightセット16へと

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
    Rect: TRect; State: TOwnerDrawState); 
var 
    Offset: Integer; 
begin 
    with (Control as TListBox) do 
    begin 
    Canvas.Font.Color := Font.Color; 
    if (odSelected in State) then 
    begin 
     Canvas.Pen.Color := $00FF9932; 
     Canvas.Brush.Color := $00FDDDC0; 
    end 
    else 
    begin 
     Canvas.Pen.Color := Color; 
     Canvas.Brush.Color := Color; 
    end; 
    Canvas.Rectangle(Rect); 
    Canvas.Brush.Style := bsClear; 
    Offset := (Rect.Bottom - Rect.Top - Canvas.TextHeight(Items[Index])) div 2; 
    Canvas.TextOut(Rect.Left + Offset + 2, Rect.Top + Offset, Items[Index]); 
    end; 
end; 

そして結果:あなたは複数選択を有効にして複数の項目を選択している場合は、これはうまく見えていません:

ここでは、連続選択を実装するトリッキーな解決法があります。前と同じように項目を描画し、前と次の項目の選択状態に応じて、項目のボーダーの上端と下端を色の線でオーバードローすることが原則です。それ以外の場合は、アイテムの選択で自然に再描画されるネイバーアイテムが呼び出されないため、現在のアイテムの外部にレンダリングする必要があります。したがって、水平線は、現在の項目範囲の上1ピクセルおよび下1ピクセルで塗りつぶされます(これらの線の色は、相対選択状態にも依存します)。

ここで非常に奇妙なのは、アイテムオブジェクトを使用して各アイテムの選択状態を保存することです。私はそれをしました。なぜなら、ドラッグの項目を選択すると、マウスボタンを離すまでSelectedプロパティは実際の状態に戻りません。幸いにも、OnDrawItemイベントはもちろん、実際の状態で発生します。回避策として、私はOnDrawItemイベントからのこれらの状態の保存を使用しました。

重要:

お知らせ、私は、実際の選択状態を保存するので、注意してください、とあなたが何か他のもののための項目オブジェクトを使用している場合は、この実際に格納する項目オブジェクトを使用していること州ブール値の配列に変換します。

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
    Rect: TRect; State: TOwnerDrawState); 
const 
    SelBackColor = $00FDDDC0; 
    SelBorderColor = $00FF9932; 
var 
    Offset: Integer; 
    ItemSelected: Boolean; 
begin 
    with (Control as TListBox) do 
    begin 
    Items.Objects[Index] := TObject((odSelected in State));  

    if (odSelected in State) then 
    begin 
     Canvas.Pen.Color := SelBorderColor; 
     Canvas.Brush.Color := SelBackColor; 
     Canvas.Rectangle(Rect); 
    end 
    else 
    begin 
     Canvas.Pen.Color := Color; 
     Canvas.Brush.Color := Color; 
     Canvas.Rectangle(Rect); 
    end; 

    if MultiSelect then 
    begin 
     if (Index > 0) then 
     begin 
     ItemSelected := Boolean(ListBox1.Items.Objects[Index - 1]); 
     if ItemSelected then 
     begin 
      if (odSelected in State) then 
      begin 
      Canvas.Pen.Color := SelBackColor; 
      Canvas.MoveTo(Rect.Left + 1, Rect.Top); 
      Canvas.LineTo(Rect.Right - 1, Rect.Top); 
      end 
      else 
      Canvas.Pen.Color := SelBorderColor; 
     end 
     else 
      Canvas.Pen.Color := Color; 
     Canvas.MoveTo(Rect.Left + 1, Rect.Top - 1); 
     Canvas.LineTo(Rect.Right - 1, Rect.Top - 1); 
     end; 

     if (Index < Items.Count - 1) then 
     begin 
     ItemSelected := Boolean(ListBox1.Items.Objects[Index + 1]); 
     if ItemSelected then 
     begin 
      if (odSelected in State) then 
      begin 
      Canvas.Pen.Color := SelBackColor; 
      Canvas.MoveTo(Rect.Left + 1, Rect.Bottom - 1); 
      Canvas.LineTo(Rect.Right - 1, Rect.Bottom - 1); 
      end 
      else 
      Canvas.Pen.Color := SelBorderColor; 
     end 
     else 
      Canvas.Pen.Color := Color; 
     Canvas.MoveTo(Rect.Left + 1, Rect.Bottom); 
     Canvas.LineTo(Rect.Right - 1, Rect.Bottom); 
     end; 
    end; 

    Offset := (Rect.Bottom - Rect.Top - Canvas.TextHeight(Items[Index])) div 2; 
    Canvas.Brush.Style := bsClear; 
    Canvas.Font.Color := Font.Color; 
    Canvas.TextOut(Rect.Left + Offset + 2, Rect.Top + Offset, Items[Index]); 
    end; 
end; 

そして結果:

enter image description here

+2

ええ、私は状態が選択されているかどうかをチェックすることを忘れていましたが、それでも私は仕事の答えから遠く離れていました。また、私は複数の選択を検討することを忘れて、効果は悪くない:)とにかくいつものように良い答えTLama;) –

+2

あなたは大歓迎です!マルチセレクトについては、アイテムの周りに境界線があると、より複雑になります。この場合、前の項目(存在する場合)と次の項目(存在する場合)が選択されているかどうかをさらに確認する必要があります。その場合は、現在選択されている項目の上または下の境界線を非表示にして、連続選択['like this](http://i.imgur.com/hG7zG.png)を取得する必要があります。 – TLama

+1

優秀な解決策、私はあなたの2番目のスクリーンショットに示されているようにマルチセレクトを動作させようとしましたが、私はあなたのアップデートされたバージョンに比べてどこにもいませんでした。可能であれば、私はあなたの答えを2回受け入れるでしょう:) –

2

関数に渡されるState変数の値を調べる必要があります。これは、アイテムが選択されているかどうかを示し、ブラシとペンを適切に設定できます。

関連する問題