2009-08-04 14 views
0

listview detault選択ペイントをオーバーライドすることはできますか?エクスプローラのウィンドウのように、項目の上に半透明の青色に見えるものがオーバーレイされています。Winforms ListView選択図面?

選択を示すために選択範囲を囲むようにアウトラインを描きたいと思います。

これを行う方法はありますか?例が分かります。

答えて

1

ここでは簡単な例を示します。

最初のヘルパー構造体と列挙型。

[StructLayout(LayoutKind.Sequential)] 
    public struct DRAWITEMSTRUCT 
    { 
     public int CtlType; 
     public int CtlID; 
     public int itemID; 
     public int itemAction; 
     public int itemState; 
     public IntPtr hwndItem; 
     public IntPtr hDC; 
     public RECT rcItem; 
     public IntPtr itemData; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public struct RECT 
    { 
     public int left; 
     public int top; 
     public int right; 
     public int bottom; 
     public int Width 
     { 
      get { return right - left; } 
     } 
     public int Height 
     { 
      get { return bottom - top; } 
     } 
    } 

    public enum ListViewDefaults 
    { 
     LVS_OWNERDRAWFIXED = 0x0400 
    } 

    public enum WMDefaults 
    { 
     WM_DRAWITEM = 0x002B, 
     WM_REFLECT = 0x2000 
    } 

は、今、カスタムリストビューを作成し、最も重要なpart..the描画のための今すぐ

public class CustomListView : ListView 
    { 
     protected override CreateParams CreateParams 
     { 
      get 
      { 
       CreateParams cp = base.CreateParams; 
       //add OwnerDraw style...i took this idea from Reflecting against ListView 
       // bit OR is very important, otherwise you'll get an exception 
       cp.Style |= (int)ListViewDefaults.LVS_OWNERDRAWFIXED; 

       return cp; 
      } 
     } 

     protected override void WndProc(ref Message m) 
     { 

      base.WndProc(ref m); 

      //if we are drawing an item then call our custom Draw. 
      if (m.Msg == (int)(WMDefaults.WM_REFLECT | WMDefaults.WM_DRAWITEM)) 
        ProcessDrawItem(ref m); 
     } 

CreateParamsをのWndProcをオーバーライドします。 私はかなり素人ですが、これは何をすべきかのアイデアを得るはずです。

private void ProcessDrawItem(ref Message m) 
     { 
      DRAWITEMSTRUCT dis = (DRAWITEMSTRUCT)Marshal.PtrToStructure(m.LParam, typeof(DRAWITEMSTRUCT)); 
      Graphics g = Graphics.FromHdc(dis.hDC); 
      ListViewItem i = this.Items[dis.itemID]; 

      Rectangle rcItem = new Rectangle(dis.rcItem.left, dis.rcItem.top, this.ClientSize.Width, dis.rcItem.Height); 
      //we have our rectangle. 
      //draw whatever you want 
      if (dis.itemState == 17) 
      { 
       //item is selected 
       g.FillRectangle(new SolidBrush(Color.Red), rcItem); 
       g.DrawString(i.Text, new Font("Arial", 8), new SolidBrush(Color.Black), new PointF(rcItem.X, rcItem.Y+1)); 
      } 
      else 
      { 
       //regular item 
       g.FillRectangle(new SolidBrush(Color.White), rcItem); 
       g.DrawString(i.Text, new Font("Arial", 8), new SolidBrush(Color.Black), new PointF(rcItem.X, rcItem.Y+1)); 
      } 

      //we have handled the message 
      m.Result = (IntPtr)1; 
     } 

これが結果です。

alt text http://i27.tinypic.com/adf9dx.jpg

+0

ありがとうスタン、私は家に帰るときにこれを試してみます。あなたのコード例をありがとう。 –

+0

私はそれを試しましたが、ProcessDrawItemは決して呼び出されません。私はブレークポイントを置いた、それはヒットしなかった。それが問題になるなら、私はwin7を使用しますか? –

+0

win7で同じように動作するかどうかは分かりませんが、なぜそうは見えません。また、私はこれが愚かだと知っていますが、クラスのデザイナーの部分でListViewからCustomListViewに型を変更しましたか?コンストラクタは、コード内のCustomListViewに変更する必要があるツールバーからリストビューを追加した場合、ListViewを作成するInitializeComponent()を呼び出します。 –

1

私の最初の考えは、ListViewコントロールをサブクラス化し、OwnerDrawをtrueに設定し、すべての図面を自分で実行することですが、そのような小さな変更に対しては過剰なもののようです。

しかし、私のウェブの散歩では、あなたの状況に非常に似ていて、自分ですべてを描くことを避けることができるので、これは役に立ちましたarticleです。

+0

おかげで、それを与えるだろう試してみる。これは驚くべきことです。 Winformsは、私が推測するようなカスタマイズのような柔軟性はないようです。 –

3

.NET ListViewコントロールは、はるかに直接的に他の答えが提案よりも、描画所有者をサポートしています。サブクラス化する必要はありません。 OwnerDrawをtrueに設定し、DrawSubItemイベントを待機し、そのイベントで好きなものを描画できます。

いつものように、ObjectListViewはこのプロセスを簡単にします。これを行う方法を厳密に文書化しているthis pageがあります。 alt text http://objectlistview.sourceforge.net/cs/_images/ownerdrawn-example1.png

ただし、セル自体の境界の外に何かを描画したい場合は、これらの方法はどれも効果がありません。したがって、前後の行をオーバーラップさせた行全体を選択するアウトラインを描くことを希望する場合は、オーナー描画によってこれを行うことはできません。各セルは個別に描画され、既に存在していたものをすべて消去して画面の一部を「所有」します。

あなたが求めているようなことをするには、カスタムドローのポストペイント段階を傍受する必要があります(所有者ドローではありません。)Michael Dunn wrote a great introduction CodeProjectのカスタム図面へ)。あなたは何が必要です読むことができますhere

は私がそれを言うことを憎むが、最も簡単な答えは、ObjectListViewを使用する装飾を作成し、それをインストールするには:

public void InitializeSelectionOverlay() 
{ 
    this.olv1.HighlightForegroundColor = Color.Black; 
    this.olv1.HighlightBackgroundColor = Color.White; 
    this.olv1.AddDecoration(new SelectedRowDecoration()); 
} 

public class SelectedRowDecoration : IOverlay 
{ 
    public void Draw(ObjectListView olv, Graphics g, Rectangle r) { 
     if (olv.SelectedIndices.Count != 1) 
      return; 

     Rectangle rowBounds = olv.GetItem(olv.SelectedIndices[0]).Bounds; 
     rowBounds.Inflate(0, 2); 
     GraphicsPath path = this.GetRoundedRect(rowBounds, 15); 
     g.DrawPath(new Pen(Color.Red, 2.0f), path); 
    } 

    private GraphicsPath GetRoundedRect(RectangleF rect, float diameter) { 
     GraphicsPath path = new GraphicsPath(); 

     RectangleF arc = new RectangleF(rect.X, rect.Y, diameter, diameter); 
     path.AddArc(arc, 180, 90); 
     arc.X = rect.Right - diameter; 
     path.AddArc(arc, 270, 90); 
     arc.Y = rect.Bottom - diameter; 
     path.AddArc(arc, 0, 90); 
     arc.X = rect.Left; 
     path.AddArc(arc, 90, 90); 
     path.CloseFigure(); 

     return path; 
    } 
} 

これは、このようなもの与える:alt text http://i29.tinypic.com/97v1id.png

+0

ありがとうございます、あなたのコントロールはクールに見えます。それは商業ですか?ニンピッキングではなく、ただ疑問に思う。 –

+0

オープンソースで、GPLで公開されています。企業がGPLを気に入らなければ、数百ドルで商用ライセンスを購入することができます(中小企業や個人の場合は少なくなります)。 – Grammarian

+0

また、実際には.NETのListViewだけですが、プレーンなバニラバージョンを使用することの苦労を取り除く巧妙なヘルパー関数がたくさんあります。 – Grammarian