2009-08-04 8 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

Stan, 내가 집에 갈 때 나는 이것을 시험 할 것이다. 귀하의 코드 예제를 주셔서 감사합니다. –

+0

방금 ​​시도했지만 ProcessDrawItem이 호출되지 않습니다. 나는 브레이크 포인트를 두었다. 그게 문제가된다면 win7을 사용합니까? –

+0

win7에서 같은 방식으로 작동하는지 확실하지 않습니다. 이유는 알 수 없습니다. 또한이 바보 같은 건 알지만 클래스의 디자이너 부분에서 ListView에서 CustomListView로 형식을 변경 했습니까? 생성자가 코드에서 CustomListView로 변경해야하는 툴바에서 listview를 추가 한 경우 생성자가 ListView를 만드는 InitializeComponent()를 호출한다는 것을 기억하십시오. –

1

내 첫 번째 생각은 ListView 컨트롤을 하위 클래스로 설정하고 OwnerDraw를 true로 설정 한 다음 모든 그리기를 직접 수행하는 것이지만 너무 작은 변경으로 인해 잔인한 것처럼 보입니다.

웹의 방황에서 나는이 상황이 매우 유사하여 도움이 될 수있는이 article을 발견했으며 모든 것을 직접 그리지 않도록합니다.

+0

감사합니다, 그것을 줄 것이다 시도.놀랍게도 이것이 작은 일을 어떻게 처리했는지입니다. 필자는 Winforms가 이런 유형의 사용자 지정에 대해서는 매우 유연하지 않다고 생각합니다. –

3

.NET 목록보기가 훨씬 더 직접적으로 다른 답변이 제안하는 것보다 그리기 소유자를 지원합니다. 하위 클래스조차도 필요하지 않습니다. 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

관련 문제