UnChecked/ checked ListViewItem

66 views Asked by At

I have a ListViewItem that contains many items by default all the item checked.

I want to write a function that will get list of items that need to be checked.

public void NewCheckSecurities(SecurityList oSecurities)
{
    // error checking 
    if (oSecurities == null || oSecurities.Count == 0)
    {
        return;
    }

    // check sent securities 
    ListView.ListViewItemCollection oItems = m_lsvSecurities.Items;
    if (oItems != null)
    {
        int i = 1;
        foreach (ListViewItem oItem in oItems)
        {
            bool bFind = oSecurities.FindSecurity((oItem.Tag as SecurityItem).Security.Symbol) != null;
            if(bFind)
            {
                oItem.Checked = true;
            }
            else
            {
                oItem.Checked = false;
            }
        }
    }
}

oSecurities got 5 securities that I want to checked.

oItems size - 2800 items

In order to check the problem, I had to split the function into 2 parts (find, checked).

The first part is a loop that runs through all the items and just makes bFind to true or false.

When I add the second part with

            if(bFind)
            {
                oItem.Checked = true;
            }
            else
            {
                oItem.Checked = false;
            }

its work but too slow.

when the code doing oItem.Checked to true or false foreach oItem in oItems is took long time.

there is a way to do this faster?

2

There are 2 answers

1
Peter B On

The issue is most likely caused by the component redrawing itself after every update.

This can often be sped up by telling the Form to pause visual updates while you are making changes, as follows:

this.SuspendLayout();

// Do your updates here

this.ResumeLayout();

It should run in a place where this equals the Form. Or if you prefer to run it somewhere else then you'll need to pass the Form as a parameter to that method and call Suspend...+Resume... there.

1
IV. On

For controls like ListView and DataGridView that host a large number of items, one way to improve performance is to use VirtualMode=true to decouple changes to the underlying list items from the UI draw. Corollary to this is that if you want to write a function that will get list of items that need to be checked this no longer needs to touch the UI at all and should be fast.

public partial class MainForm : Form
{
    public MainForm() => InitializeComponent();
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        for (int i = 1; i <= 3000; i++)
        {
            SecuritiesList.Add(new Security { Text = $"Security {i}", IsChecked = false });
        }
        m_lsvSecurities.View = View.Details;
        m_lsvSecurities.HeaderStyle = ColumnHeaderStyle.None;
        m_lsvSecurities.Columns.Add("", 80);
        m_lsvSecurities.Columns.Add("Security", 300);
        m_lsvSecurities.CheckBoxes = true;
        m_lsvSecurities.VirtualMode = true;
        m_lsvSecurities.RetrieveVirtualItem += (sender, e) =>
        {
            if (e.ItemIndex >= 0 && e.ItemIndex < SecuritiesList.Count)
            {
                var security = SecuritiesList[e.ItemIndex];
                ListViewItem item = new ListViewItem(text: security.IsChecked ? "\u2612" : "\u2610");
                item.SubItems.Add(security.Text);
                e.Item = item;
            }
            else
            {
                e.Item = new ListViewItem();
            }
        };
        m_lsvSecurities.MouseClick += (sender, e) =>
        {
            var hti = m_lsvSecurities.HitTest(e.Location);
            if (hti.Location == ListViewHitTestLocations.Label)
            {
                var security = SecuritiesList[hti.Item.Index];
                security.IsChecked = !security.IsChecked;
                m_lsvSecurities.Invalidate(hti.Item.Bounds);
            }
        };
        m_lsvSecurities.VirtualListSize = SecuritiesList.Count;
        _ = benchmark();
    }
    private List<Security> SecuritiesList { get; } = new List<Security>();
}

After generating a list of 3000 securities and showing the list, after 2 seconds the benchmark() method is called to measure how long it takes to get a list of items that need to be checked, which in this example is "every fourth item".

conditional list selection

    /// <summary>
    ///  Show the dialog, wait 2 seconds, then 
    ///  run conditional set check for 3000 items.
    /// </summary>
    async Task benchmark()
    {
        await Task.Delay(TimeSpan.FromSeconds(2));
        Text = DateTime.Now.ToString(@"hh\:mm\:ss\.ffff tt");
        screenshot("screenshot.1.png");
        var stopwatch = Stopwatch.StartNew();

        // Example of a function that will get list
        // of items that need to be checked.
        setListOfItemsConditionalChecked();

        m_lsvSecurities.Refresh();
        stopwatch.Stop();
        Text = DateTime.Now.ToString(@"hh\:mm\:ss\.ffff tt");
        await Task.Delay(TimeSpan.FromSeconds(0.5));
        MessageBox.Show($"{stopwatch.ElapsedMilliseconds} ms");
    }

    private void setListOfItemsConditionalChecked()
    {
        for (int i = 0; i < SecuritiesList.Count; i++) 
        {
            var mod = i + 1;
            SecuritiesList[i].IsChecked = (mod % 4 == 0);
        }
    }

Class for list items

{
    public string Text { get; set; }
    public bool IsChecked { get; set; }
}