2012-01-30 74 views
8

我有一個ListView我希望調整項目的繪圖(例如突出顯示列表視圖中的某些字符串),但我不想從根本上改變項目的顯示方式。ListView的默認實現OwnerDraw

我已經將OwnerDraw設置爲true,並且可以讓我的頭瞭解如何繪製突出顯示效果,但每當我試圖按照默認實現繪製列表視圖項目的其餘部分時,事情就會出錯,而我留下了一大堆圖形問題,表明實際上我完全出錯了。

有什麼地方可以看到DrawItemDrawSubItem事件的「默認」處理程序是否可以幫助我更好地理解並更輕鬆地調整我的代碼?

僅供參考這裏的一個片段顯示的是我在做什麼:

public MyListView() 
{ 
    this.OwnerDraw = true; 
    this.DoubleBuffered = true; 
    this.DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(MyListView_DrawColumnHeader); 
    this.DrawItem += new DrawListViewItemEventHandler(MyListView_DrawItem); 
    this.DrawSubItem += new DrawListViewSubItemEventHandler(MyListView_DrawSubItem); 
} 

private void MyListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) 
{ 
    // Not interested in changing the way columns are drawn - this works fine 
    e.DrawDefault = true; 
} 

private void MyListView_DrawItem(object sender, DrawListViewItemEventArgs e) 
{ 
    e.DrawBackground(); 
    e.DrawFocusRectangle(); 
} 

private void MyListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) 
{ 
    string searchTerm = "Term"; 
    int index = e.SubItem.Text.IndexOf(searchTerm); 
    if (index >= 0) 
    { 
     string sBefore = e.SubItem.Text.Substring(0, index); 

     Size bounds = new Size(e.Bounds.Width, e.Bounds.Height); 
     Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds); 
     Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds); 

     Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height); 
     e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect); 
    } 

    e.DrawText(); 
} 
+0

我想你需要添加關於什麼錯誤的詳細信息。我測試了你的代碼,它是......好的。它用黃色框突出了「Term」一詞。也許顯示繪圖結果的圖像。 – LarsTech 2012-01-30 16:01:17

+0

@LarsTech我遇到了很多問題,從選擇效果不再可見(可以在上面的示例中看到)到項目文本被「雙重渲染」,並且當我將鼠標懸停在項目上時也消失。我不希望有人能夠神奇地修復所有這些問題(這些問題也會過於具體以至於不能幫助未來的用戶),相反我希望看到一個能夠儘可能接近地呈現所有內容的示例實現。標準實施 – Justin 2012-01-30 16:22:08

回答

10

我現在還沒有時間寫出完整的答案,所以我會寫下一些快速記錄,稍後再回來。

正如LarsTech說,使用繪製ListView控制是一種痛苦 - 。淨ListView類是底層Win32 List View Control周圍的包裝和「所有者繪製」的能力是由NM_CUSTOMDRAW notification code提供。因此沒有「默認的.Net實現」 - 默認是使用底層的Win32控件。

爲了讓生活更加困難也有一些額外的因素使:

  • 作爲LarsTech指出,其實第一個子項表示父項目本身,所以如果你在兩個把手渲染DrawItemDrawSubItem你可能會畫第一個單元格的內容兩次。如果在DrawItem事件中繪製背景,然後繪製文本在DrawSubItem事件中,當您將鼠標懸停時,項目文本將消失。
  • 某些渲染似乎並不是默認的雙緩衝
  • 我還注意到ItemState屬性並不總是正確的,例如在調整列大小之後。因此,我發現最好不要依賴它。
  • 您還需要確保您的文本不會分割成多行,否則您會在單元格底部看到較低行的前幾個像素。
  • 在渲染第一個單元格時需要特別考慮本地列表視圖使用的額外填充。
  • 因爲DrawItem事件首先發生,所以您在DrawItem處理程序中繪製的任何內容(例如選擇效果)都可能被您在DrawSubItem處理程序中執行的操作(例如使某些單元具有不同的背景色)覆蓋。

總而言之處理所有者繪製是一個相當複雜的事 - 我發現它最好地處理所有DrawSubItem事件中繪製,其也最好使用BufferedGraphics類執行自己的雙緩衝。

我還發現在ObjectListView的源代碼非常方便。

最後,所有這些只是爲了處理列表視圖(我使用的唯一模式)的細節模式,如果你想其他模式也可以工作,那麼我相信有額外的東西需要考慮。

當我有機會時,我會嘗試發佈我的工作示例代碼。

3

我不知道這是否會完全幫助你,但我會添加一些注意事項:

有一點要記住的是,DrawSubItem也會繪製第一項,這可能是你從double-rendered看起。

有些事情嘗試(沒有考慮速度):

private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) { 
    e.DrawBackground(); 
    if ((e.State & ListViewItemStates.Selected) == ListViewItemStates.Selected) { 
    Rectangle r = new Rectangle(e.Bounds.Left + 4, e.Bounds.Top, TextRenderer.MeasureText(e.Item.Text, e.Item.Font).Width, e.Bounds.Height); 
    e.Graphics.FillRectangle(SystemBrushes.Highlight, r); 
    e.Item.ForeColor = SystemColors.HighlightText; 
    } else { 
    e.Item.ForeColor = SystemColors.WindowText; 
    } 
    e.DrawText(); 
    e.DrawFocusRectangle(); 
} 

爲了您的日常DrawSubItem,確保你沒有在第一列拉和我增加了DrawBackground()程序。我在高亮矩形中添加了一些裁剪,因此它不會在列參數之外繪製。

private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) { 
    if (e.ColumnIndex > 0) { 
    e.DrawBackground(); 

    string searchTerm = "Term"; 
    int index = e.SubItem.Text.IndexOf(searchTerm); 

    if (index >= 0) { 
     string sBefore = e.SubItem.Text.Substring(0, index); 

     Size bounds = new Size(e.Bounds.Width, e.Bounds.Height); 
     Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds); 
     Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds); 

     Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height); 

     e.Graphics.SetClip(e.Bounds); 
     e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect); 
     e.Graphics.ResetClip(); 
    } 

    e.DrawText(); 
    } 
} 

一般來說,所有者繪製ListView控件在受到傷害的世界中是歡迎的。你不再使用視覺樣式,你也必須自己去做。啊。

2

選擇的項目背面顏色已更改。在Windows中默認爲藍色。此代碼將有助於ü在任何顏色:

private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) 
    { 
     e.DrawBackground(); 
     if (e.Item.Selected) 
     { 
      e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds); 
     } 
     e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds); 

    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     for (int ix = 0; ix < listView1.Items.Count; ++ix) 
     { 
      var item = listView1.Items[ix]; 
      item.BackColor = (ix % 2 == 0) ? Color.Gray : Color.LightGray; 

     } 

    } 

    private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) 
    { 
     e.DrawDefault = true; 
    } 

    } 
} 
0
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) 
    { 
     e.DrawBackground(); 
     if (e.Item.Selected) 
     { 
      e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds); 
     } 
     e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds); 

    }