11

这个问题可能被认为是在 listview 中使用 ownerdraw 和 virtualmode进行闪烁的后续问题。

我有一个ListView控件Virtual mode,我尝试执行自定义绘图。项目渲染是通过以下方法覆盖完成的:

protected override void OnDrawItem(DrawListViewItemEventArgs eventArgs) 

如引用问题中所述,自定义绘图引入了鼠标悬停事件的闪烁。调试器告诉我这是由于触发了过多的自定义绘制事件而发生的。


现在 - 引用问题的公认答案告诉我们:

这是 .NET 的 ListView 中的一个错误,您无法通过双缓冲来解决它。

  • 那么,这些信息的可靠性如何?这真的是一个错误吗?或者,也许我们只是试图切断部分消息并希望它不会改变可见的行为?

  • 如果我有我的所有者为ListViewin绘制例程,Virtual Mode,我可以抑制这些Custom Draw事件并且只执行我的绘制,WM_PAINT或者在某些情况下这可能是不正确的,这是真的吗?

  • System.Windows.Forms控件能够在WM_PAINT不改变其初始行为的情况下完成所有绘画的先决条件是什么?

4

3 回答 3

10

至少对于OnDrawItem的双缓冲,有bug是不对的,但是有点傻:有一个protected属性可以设置,但是需要重写ListView。我创建了这种类:

public class MyListView : ListView
{
    public MyListView()
        : base()
    {
        DoubleBuffered = true;
    }
}

然后在我的 MyForm.Designer.cs 文件中,我使用以下行更改 ListView 的实例化:

private ListView myListView;

this.myListView = new MyListView();

OnDrawItem 会像一个魅力一样工作!

于 2012-05-08T15:44:20.493 回答
4

喜欢这里的这个答案,虽然不确定,但是,

我认为ListView.BeginUpdate()ListView.EndUpdate()会解决问题。

有关此的 MSDN 主题

也许这样:

protected override void OnDrawItem(DrawListViewItemEventArgs eventArgs)
{
    ListView.BeginUpdate();
    //Do Works Here
    ListView.EndUpdate();
}

更新

另一种选择可能是使用新线程BackgroundWorker来更新 ListView...我在我的应用程序中与BeginUpdate()/一起实现EndUpDate()了它,发现它比仅BeginUpdate()/ EndUpDate()..

更新

我在 SO找到了另一个有效的解决方案,一个由以下人员提供的 Helper 类Brian Gillespie

public enum ListViewExtendedStyles
{
    /// <summary>
    /// LVS_EX_GRIDLINES
    /// </summary>
    GridLines = 0x00000001,
    /// <summary>
    /// LVS_EX_SUBITEMIMAGES
    /// </summary>
    SubItemImages = 0x00000002,
    /// <summary>
    /// LVS_EX_CHECKBOXES
    /// </summary>
    CheckBoxes = 0x00000004,
    /// <summary>
    /// LVS_EX_TRACKSELECT
    /// </summary>
    TrackSelect = 0x00000008,
    /// <summary>
    /// LVS_EX_HEADERDRAGDROP
    /// </summary>
    HeaderDragDrop = 0x00000010,
    /// <summary>
    /// LVS_EX_FULLROWSELECT
    /// </summary>
    FullRowSelect = 0x00000020,
    /// <summary>
    /// LVS_EX_ONECLICKACTIVATE
    /// </summary>
    OneClickActivate = 0x00000040,
    /// <summary>
    /// LVS_EX_TWOCLICKACTIVATE
    /// </summary>
    TwoClickActivate = 0x00000080,
    /// <summary>
    /// LVS_EX_FLATSB
    /// </summary>
    FlatsB = 0x00000100,
    /// <summary>
    /// LVS_EX_REGIONAL
    /// </summary>
    Regional = 0x00000200,
    /// <summary>
    /// LVS_EX_INFOTIP
    /// </summary>
    InfoTip = 0x00000400,
    /// <summary>
    /// LVS_EX_UNDERLINEHOT
    /// </summary>
    UnderlineHot = 0x00000800,
    /// <summary>
    /// LVS_EX_UNDERLINECOLD
    /// </summary>
    UnderlineCold = 0x00001000,
    /// <summary>
    /// LVS_EX_MULTIWORKAREAS
    /// </summary>
    MultilWorkAreas = 0x00002000,
    /// <summary>
    /// LVS_EX_LABELTIP
    /// </summary>
    LabelTip = 0x00004000,
    /// <summary>
    /// LVS_EX_BORDERSELECT
    /// </summary>
    BorderSelect = 0x00008000,
    /// <summary>
    /// LVS_EX_DOUBLEBUFFER
    /// </summary>
    DoubleBuffer = 0x00010000,
    /// <summary>
    /// LVS_EX_HIDELABELS
    /// </summary>
    HideLabels = 0x00020000,
    /// <summary>
    /// LVS_EX_SINGLEROW
    /// </summary>
    SingleRow = 0x00040000,
    /// <summary>
    /// LVS_EX_SNAPTOGRID
    /// </summary>
    SnapToGrid = 0x00080000,
    /// <summary>
    /// LVS_EX_SIMPLESELECT
    /// </summary>
    SimpleSelect = 0x00100000
}

public enum ListViewMessages
{
    First = 0x1000,
    SetExtendedStyle = (First + 54),
    GetExtendedStyle = (First + 55),
}

/// <summary>
/// Contains helper methods to change extended styles on ListView, including enabling double buffering.
/// Based on Giovanni Montrone's article on <see cref="http://www.codeproject.com/KB/list/listviewxp.aspx"/>
/// </summary>
public class ListViewHelper
{
    private ListViewHelper()
    {
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr handle, int messg, int wparam, int lparam);

    public static void SetExtendedStyle(Control control, ListViewExtendedStyles exStyle)
    {
        ListViewExtendedStyles styles;
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        styles |= exStyle;
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }

    public static void EnableDoubleBuffer(Control control)
    {
        ListViewExtendedStyles styles;
        // read current style
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        // enable double buffer and border select
        styles |= ListViewExtendedStyles.DoubleBuffer | ListViewExtendedStyles.BorderSelect;
        // write new style
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }
    public static void DisableDoubleBuffer(Control control)
    {
        ListViewExtendedStyles styles;
        // read current style
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        // disable double buffer and border select
        styles -= styles & ListViewExtendedStyles.DoubleBuffer;
        styles -= styles & ListViewExtendedStyles.BorderSelect;
        // write new style
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }
}
于 2012-05-13T05:46:37.297 回答
4

我在任何自定义呈现事件处理程序(DrawItem、DrawSubItem)中都看到了 ListView 控件的闪烁问题。我尝试了 BeginUpdate()/EndUpdate() 和双缓冲但没有成功。我认为 .NET 会为自定义绘制列右侧的所有列触发额外的 WM_PAINT 。

但是,我发现此解决方法适用于单个自定义呈现的列 ListView。它工作得很好!

  1. 在列标题编辑器中,将自定义绘制的列设置为最后一列
  2. 更改所需位置的“DisplayIndex”

这应该解决鼠标悬停或运行时渲染中的闪烁问题。

于 2012-05-14T18:01:58.150 回答