Мерцание в виде списка с ownerdraw и virtualmode - PullRequest
2 голосов
/ 02 июня 2009

Я использую элемент управления списком со следующими параметрами:

        this.listView1.BackColor = System.Drawing.Color.Gainsboro;
        this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
        this.columnHeader1,
        this.columnHeader2});
        this.listView1.FullRowSelect = true;
        this.listView1.HideSelection = false;
        this.listView1.Location = new System.Drawing.Point(67, 192);
        this.listView1.Name = "listView1";
        this.listView1.Size = new System.Drawing.Size(438, 236);
        this.listView1.TabIndex = 0;
        this.listView1.UseCompatibleStateImageBehavior = false;
        this.listView1.View = System.Windows.Forms.View.Details;
        this.listView1.DrawColumnHeader += new System.Windows.Forms.DrawListViewColumnHeaderEventHandler(this.listView1_DrawColumnHeader);
        this.listView1.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.listView1_RetrieveVirtualItem);
        this.listView1.DrawSubItem += new System.Windows.Forms.DrawListViewSubItemEventHandler(this.listView1_DrawSubItem);

Две строки снабжены произвольным текстом. Владение розыгрышем простое:

    private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
    {
        if (e.ColumnIndex == 0)
        {
            e.DrawBackground();
            e.DrawText();                
        }
        else
            e.DrawDefault = true;
        //Console.WriteLine("{0}\t\tBounds:{1}\tItem:{2}\tSubitem:{3}", (i++).ToString(), e.Bounds.ToString(), e.Item, e.SubItem);
    }

проблема в том, что при наведении курсора мыши на содержимое списка, у меня мерцает первый столбец. Отладка показывает, что DrawSubItem вызывается постоянно, когда на нем находится мышь.

Это ошибка? Как избежать этого поведения?

Ответы [ 3 ]

5 голосов
/ 02 июня 2009

Это ошибка в ListView .NET, и вы не можете обойти ее путем двойной буферизации.

В виртуальных списках базовый элемент управления генерирует множество пользовательских событий рисования, когда указатель мыши находится над столбцом 0. Эти пользовательские события рисования вызывают мерцание, даже если вы включаете DoubleBuffering, поскольку они отправляются за пределы обычного сообщения WmPaint.

Кажется, я тоже помню, что это происходит только в XP. Vista исправила это (но представила другие).

Вы можете посмотреть на код в ObjectListView , чтобы увидеть, как он решил эту проблему.

Если вы хотите решить это самостоятельно, вам нужно углубиться во внутреннюю структуру элемента управления ListView:

  1. переопределить WndProc
  2. перехватить сообщение WmPaint и установить флаг, который является истинным во время сообщения
  3. перехватывает сообщение WmCustomDraw и игнорирует все сообщения, которые происходят вне события WmPaint.

Примерно так:

protected override void WndProc(ref Message m) {
    switch (m.Msg) {
        case 0x0F: // WM_PAINT
            this.isInWmPaintMsg = true;
            base.WndProc(ref m);
            this.isInWmPaintMsg = false;
            break;
        case 0x204E: // WM_REFLECT_NOTIFY
            NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR));
            if (nmhdr.code == -12) { // NM_CUSTOMDRAW
                if (this.isInWmPaintMsg)
                    base.WndProc(ref m);
            } else
                base.WndProc(ref m);
            break;
        default:
            base.WndProc(ref m);
            break;
    }
}
4 голосов
/ 05 ноября 2009

Я получаю кучу

'System.Drawing.NativeMethods' is inaccessible due to its protection level

и

The type name 'NMHDR' does not exist in the type 'System.Drawing.NativeMethods' 

ошибки. Я где-то читал, что должен включить user32.dll, но не могу понять, как это сделать в этом случае.

Редактировать: ОК, я отправил еще до того, как начал думать. Теперь я создал свой собственный элемент управления ListView и скопировал структуру из кода objectListView. Кажется, сейчас работает. Вот мой код:

public class Listview : ListView
{
    private bool isInWmPaintMsg=false;

    [StructLayout(LayoutKind.Sequential)]
    public struct NMHDR
    {
        public IntPtr hwndFrom;
        public IntPtr idFrom;
        public int code;
    }

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case 0x0F: // WM_PAINT
                this.isInWmPaintMsg = true;
                base.WndProc(ref m);
                this.isInWmPaintMsg = false;
                break;
            case 0x204E: // WM_REFLECT_NOTIFY
                NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
                if (nmhdr.code == -12)
                { // NM_CUSTOMDRAW
                    if (this.isInWmPaintMsg)
                        base.WndProc(ref m);
                }
                else
                    base.WndProc(ref m);
                break;
            default:
                base.WndProc(ref m);
                break;
        }
    }

}
3 голосов
/ 02 июня 2009

Я не могу предложить решение ListView, вызывающее события пользовательского рисования слишком часто, но, возможно, вы можете просто замаскировать проблему с двойной буферизацией:

Stackoverflow: как удвоить буферизацию элементов управления .NET на форме?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...