listviewc#-3.0listviewitemvirtualmode

how to set the top item property of listviewitem when it is in virtual mode?


public new int VirtualListSize
    {
        get { return base.VirtualListSize; }
        set
        {
            // If the new size is smaller than the Index of TopItem, we need to make
            // sure the new TopItem is set to something smaller.
            if (VirtualMode &&
                View == View.Details &&
                TopItem != null &&
                value > 0 &&
                TopItem.Index > value - 1)
            {
                TopItem = Items[value - 1];
            }

            base.VirtualListSize = value;
        }
    }

I am trying to set the topitem property of listview, however in virtual mode items are disabled. so any code trying to access it in virtual mode throws an invalidoperation exception. Exception doesn't occur when i try to debug line by line. If i comment the line TopItem=Items[value-1] it doesnt throws any exception.

System.InvalidOperationException: When in VirtualMode the ListView RetrieveVirtualListItem event needs a list view SubItem for each ListView column. at System.Windows.Forms.ListView.WmReflectNotify(Message& m) at System.Windows.Forms.ListView.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) Please suggests.


Solution

  • You don't need to change the TopItem when the virtual list size becomes smaller. The .NET ListView already does that. According to dotPeek disassembler:

    public int VirtualListSize { 
        ... 
        set {
            ...
            bool keepTopItem = this.IsHandleCreated && VirtualMode && this.View == View.Details && !this.DesignMode; 
            int topIndex = -1;
            if (keepTopItem) { 
                topIndex = unchecked( (int) (long)SendMessage(NativeMethods.LVM_GETTOPINDEX, 0, 0)); 
            }
    
            virtualListSize = value;
    
            if (IsHandleCreated && VirtualMode && !DesignMode)
                SendMessage(NativeMethods.LVM_SETITEMCOUNT, virtualListSize, 0); 
    
            if (keepTopItem) { 
                topIndex = Math.Min(topIndex, this.VirtualListSize - 1); 
                // After setting the virtual list size ComCtl makes the first item the top item.
                // So we set the top item only if it wasn't the first item to begin with. 
                if (topIndex > 0) {
                    ListViewItem lvItem = this.Items[topIndex];
                    this.TopItem = lvItem;
                } 
            }
        } 
    }
    

    This trouble with this is that, in my experience, setting TopItem on a virtual listview is an error prone activity. In my code I have this block:

    // Damn this is a pain! There are cases where this can also throw exceptions!
    try {
        this.listview.TopItem = lvi;
    }
    catch (Exception) {
        // Ignore any failures
    }
    

    Since setting TopItem sometimes throws exceptions, this means that sometimes setting VirtualListSize similarly throws exceptions.

    Other points

    You can access the Items collection via an index even when in virtual mode. So, this is fine (assuming the list isn't empty):

    this.listview1.TopItem = this.listview1.Items[this.listview1.Items.Count - 1];
    

    You can't iterate the Items collection in virtual mode. This will throw an exception:

    foreach (ListViewItem item in this.listview1.Items) { ... }