13

アイテムの描画を微調整したいListViewがあります (たとえば、リスト ビューのアイテムで特定の文字列を強調表示する) が、アイテムの表示方法を根本的に変更したくありません。

OwnerDrawを true に設定し、ハイライト効果を描画する方法について頭を悩ませることができますが、デフォルトの実装を延期して残りのリスト ビュー アイテムを描画しようとすると、問題が発生し、全体が残ってしまいます。実際に私が完全に間違っていることを示すグラフィカルな問題の負荷。

DrawItemとイベントの「デフォルト」ハンドラーが何をするかを確認できる場所はありDrawSubItemますか?

参考までに、私が現在行っていることを示すスニペットを次に示します。

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();
}
4

5 に答える 5

16

今は完全な回答を書き上げる時間がないので、代わりにいくつかの簡単なメモを書き留めて、後で戻ってきます.

LarsTech が言ったように、所有者がListViewコントロールを描画するのは面倒です。.NetListViewクラスは、基礎となるWin32 リスト ビュー コントロールのラッパーであり、「所有者描画」機能はNM_CUSTOMDRAW通知コードによって提供されます。そのため、「デフォルトの .Net 実装」はありません。デフォルトでは、基になる Win32 コントロールが使用されます。

生活をさらに困難にするために、いくつかの追加の考慮事項があります。

  • LarsTech が指摘したように、最初のサブアイテムは実際には親アイテム自体を表しているため、両方でレンダリングを処理すると、最初のセルの内容を 2 回描画する可能性がありますDrawItemDrawSubItem
  • 基になるリスト ビュー コントロール (このページのメモに記載)にバグがあり、DrawItem対応するイベントなしでイベントが発生することをDrawSubItem意味します。つまり、イベントで背景を描画し、DrawItemイベントでテキストを描画すると、DrawSubItemアイテムマウスオーバーするとテキストが消えます。
  • 一部のレンダリングは、デフォルトではダブルバッファリングされていないようです
  • ItemStateまた、列のサイズを変更した直後など、プロパティが常に正しいとは限らないことにも気付きました。したがって、私はそれに頼らないことが最善であることがわかりました。
  • また、テキストが複数の行に分割されていないことを確認する必要があります。そうしないと、下の行の上部の数ピクセルがセルの下部に表示されます。
  • また、最初のセルをレンダリングする際には、ネイティブ リスト ビューが使用する追加のパディングを考慮する必要があります。
  • イベントが最初に発生するため、ハンドラーDrawItemで描画するものDrawItem(選択効果など) は、DrawSubItemハンドラーで行うこと (特定のセルに異なる背景色を設定するなど) によってオーバーレイされる可能性があります。

全体として、所有者の描画の処理はかなり複雑な作業です。イベント内ですべての描画を処理するのが最善であり、クラスDrawSubItemを使用して独自のダブル バッファリングを実行するのが最善であることがわかりました。BufferedGraphics

また、 ObjectListViewのソース コードを見ると非常に便利であることがわかりました。

最後に、これはすべて、リスト ビューの詳細モード (私が使用している唯一のモード) を処理するためのものです。他のモードも機能させたい場合は、考慮すべき追加事項があると思います。

機会があれば、実際に動作するサンプル コードを投稿してみます。

于 2012-02-01T13:41:00.893 に答える
5

これが完全に役立つかどうかはわかりませんが、いくつかのメモを追加します。

覚えておくべきことの 1 つは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 コントロールを描画する所有者は、傷ついた世界では歓迎されます。Visual Styles で描画する必要はなくなりました。これも自分で行う必要があります。うーん。

于 2012-01-30T17:14:20.087 に答える
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;
    }

    }
}
于 2013-11-05T09:49:16.447 に答える
0

ComponentOwl は最近、Better ListView Express と呼ばれるフリーウェア コンポーネントをリリースしました。

これは ListView とまったく同じように見え、動作しますが、はるかに強力な所有者描画機能を備えています。すべての要素を正確に描画し、一部の描画をオフにすることもできます (選択をオンにするなど)。

于 2012-02-01T14:52:54.307 に答える