C# ListView в VirtualMode мерцает, когда выбранный элемент не виден

Я знаю, что это звучит сложно воспроизвести, но, пожалуйста, следуйте за мной:

У вас есть ListView с VirtualMode = true.

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

Вы увидите, что на долю секунды он ведет себя ненормально, и увидите какое-то мерцание. Если вы обострите ситуацию и попытаетесь добавить очень много элементов очень быстро (я добавляю около 20 раз в секунду), вы увидите, что маленькая проблема становится очень большой. Это комбинация мерцающих и недействительных предметов.

Я исследовал проблему, и кажется, что ListView генерирует много RetrieveVirtualItem события для выбранного элемента (даже если он явно не виден).

Кажется, что когда я добавляю новый элемент (увеличиваю VirtualListSize), ListView сначала пытается сфокусироваться на выбранном элементе, а затем возвращается к предыдущему местоположению.

Кто-нибудь испытывал такую ​​же проблему?

2 ответа

Вот производный класс с обходом этой проблемы.

использование SetVirtualListSize() метод вместо обычного VirtualListSize,

public class FlickerFreeListView : ListView
{
    #region Static Functionality

    private static FieldInfo _internalVirtualListSizeField;

    static FlickerFreeListView()
    {
        _internalVirtualListSizeField = typeof(ListView).GetField("virtualListSize", System.Reflection.BindingFlags.NonPublic | BindingFlags.Instance);

        if (_internalVirtualListSizeField == null)
        {
            string msg = "Private field virtualListSize in type System.Windows.Forms.ListView is not found. Workaround is incompatible with installed .NET Framework version, running without workaround.";
            Trace.WriteLine(msg);
        }
    }

    #endregion


    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SendMessage(HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam);

    internal IntPtr SendMessage(int msg, IntPtr wparam, IntPtr lparam)
    {
        return SendMessage(new HandleRef(this, this.Handle), msg, wparam, lparam);
    }

    public void SetVirtualListSize(int size)
    {
        // if workaround incompatible with current framework version (usually MONO)
        if (_internalVirtualListSizeField == null)
        {
            VirtualListSize = size;
        }
        else
        {
            if (size < 0)
            {
                throw new ArgumentException("ListViewVirtualListSizeInvalidArgument");
            }

            _internalVirtualListSizeField.SetValue(this, size);
            if ((base.IsHandleCreated && this.VirtualMode) && !base.DesignMode)
            {
                SendMessage(0x102f, new IntPtr(size), new IntPtr(2));
            }
        }
    }
}

Различные элементы управления имеют защищенное свойство DoubleBuffered. Вы можете попробовать получить свой собственный DBListView из ListView, и в его конструкторе установить его свойство DoubleBuffered в значение true.

Другие вопросы по тегам