Calculating number of visible items in ListBox

别等时光非礼了梦想. 提交于 2019-12-04 17:56:16
elbweb

After trying to figure out something similar, I thought I would share my result here (as it seems easier than the other responses):

Simple visibility test I got from here.

private static bool IsUserVisible(FrameworkElement element, FrameworkElement container)
{
    if (!element.IsVisible)
        return false;

    Rect bounds =
        element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
    var rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
    return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
}

Afterwards you can loop through the listboxitems and use that test to determine which are visible. The count of this list would give you the number of visible items in the listbox.

private List<object> GetVisibleItemsFromListbox(ListBox listBox, FrameworkElement parentToTestVisibility)
{
    var items = new List<object>();

    foreach (var item in PhotosListBox.Items)
    {
        if (IsUserVisible((ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item), parentToTestVisibility))
        {
            items.Add(item);
        }
        else if (items.Any())
        {
            break;
        }
    }

    return items;
}

This would then contain the list of items currently shown in the listbox (including those hidden by scrolling or something similar).

Will you probably could figure out the sizes of all the books before adding them to the list box (possibly by parsing and populating the template XAML in code behind then asking the outer control for its size) this would be a lot of work for not much gain. Can't you just simply select a number of books which will be enough to fill the list box, but not so many to slow the rendering down and turn the scroll bar off? This could still be linked to the size of the list box so that as it grew more items were added, but the performance cost for adding a few extra items should be less that the cost of calculating all the sizes again.

Quick example: Let's say that the size of a book with one author is 150 pixels. You could take the size of the listbox and divide it by 125 to get a rough estimate of the number of items which would be in-the-ballpark, but not costly to calculate. What you want to avoid is too few items since that will leave empty space.


Edited in light of your comment.

In that case you could add the items to the list box (using the method above to get a close guess), then calculate which the last completely visible item is and then remove the extra items.

This extension method will get the last element that is completely shown in a listbox or null if no items are visible:

public static class ListBoxExtensions
{
    public static FrameworkElement GetLastItem(this ListBox listBox)
    {
        double height = listBox.ActualHeight;

        double currentHeight = 0;
        FrameworkElement previous = null;

        foreach (FrameworkElement item in listBox.Items)
        {
            currentHeight += item.ActualHeight;
            if (currentHeight > height)
            {
                return previous;
            }

            previous = item;
        }

        return previous;
    }
}

If the list is made larger then you can add enough items to the collection that you fill in the gap and re-run the process.

"How can I calculate how many items to display so the scrollbar is not shown? "

Short answer: (listBox1.Height/ listBox1.ItemHeight)

This gives you the number of displayed/available lines, so you can read only this number of lines and will fill all the listbox.

Now, the demo:

        int listbox_total_visible_lines = 2;
        listBox1.Height = (listbox_total_visible_lines + 1) * listBox1.ItemHeight;
        listBox1.Items.Add("um");
        listBox1.Items.Add("dois");
        listBox1.Items.Add("tres");
        listBox1.Items.Add("quatro");
        listBox1.Items.Add("cinco");
        listBox1.SelectedIndex = listBox1.Items.Count - 1;
        this.Text = (listBox1.Height/ listBox1.ItemHeight).ToString();

This example let's you choose the number of items that are going to be visible, so it's the number os lines that are actualy available to show. Therefore, you add only the "listbox_total_visible_items" items and the listbox will be full and not show the scrollbars.

Code explain:

  1. listbox_total_visible_items contains the number of lines to be shown

  2. setup listbox with the proper size, 2 lines only

3-7. add some lines

  1. go to the last line, just for fun

  2. show on the form text bar, the number of the listbox lines, based on the listbox height divided by the size of each item.

That's it.

Kornelije Petak

The solution can implicitly be found here: Solution

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!