0

I'll try to keep it short.

What I need is a ListView that:

  • Has an exact number of elements that I will provide.
  • Is Scrollable
  • The ScrollBar is disabled
  • The ListView scrolls when Scroll Wheel is used on Children Elements
  • Scrolls the list element by element, but I don't think it's a problem because the ListView has a property that tells it to show only full elements, not partial.

What I tried:

  • A ListView with UniformGrid as its ItemsTemplate but a UniformGrid lets you define how many rows does the actual Grid have, not how many elements does the List show. Besides I've had problems with scrolling, but maybe I did something wrong.

Any ideas ? Thanks!


Edit:

I think I've found a way for Children Elements to bubble up the Scroll Event.

Child elements of scrollviewer preventing scrolling with mouse wheel?

But That would force me to use that behaviour on EVERY little control in the list. Isn't there another way ?

Edit2:

Mark Feldman has solved most of my problems. One still continues:

  • Has an exact number of elements that I will provide. I need my ListView to show let's say 6 elements. So in the end the height of the ListView has to be exactly 6*ChildElement. I'm guessing I have to calculate manually. And I imagine I could do that in XAML or in codeBehind. I really want to do it in XAML, I imagine I'd have to bind to element in config that says how many elements are supposed to be visible, and multiply it by ActualHeight of ItemTemplate. Is it doable ?
Community
  • 1
  • 1
michal.ciurus
  • 3,616
  • 17
  • 32
  • You said that "Scrolls the list element by element, but I don't think it's a problem because the ListView has a property that tells it to show only full elements, not partial." Which property on the ListView control allows you to specify that behavior? – Caleb Bell Nov 01 '17 at 16:34

2 Answers2

3

Just disable the vertical scrollbar visibility, everything else should work as you describe.

<ListBox ScrollViewer.VerticalScrollBarVisibility="Disabled">
Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • Ok, I needed to set `height` of my `ListView` for it to scroll properly, and not stretch across all elements. What about the other issue: - I want my list to show a given number of elements. For example if I want to show 6 elements, my `ListView` height has to be 6 * `ChildElement Height`. How Do i do that ? – michal.ciurus Dec 05 '13 at 10:29
  • You haven't really given us enough info to answer this fully but my first response would be to just override your ListBoxItem style and set the height to 1/6th of whatever the height of your ListBox is. If your ListBox can be resized then bind your ListBoxItem height to your ListBox ActualHeight and run it through a converter to divide by 6. – Mark Feldman Dec 05 '13 at 11:50
0

I encountered a need to come up with a solution to your second question. The solution is very susceptible to layout changes and not recommended, but it's the only thing that seems to get the job done:

private void List_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
        var list = sender as ListViewBase;   // works with ListView or GridView
        if (list == null || list.Items == null)
        {
            return;
        }

        var nMax = 6; // set this to your desired number of items
        var n = list.Items.Count;
        var gen = list.ItemContainerGenerator;
        if (gen != null)
        {
            var height = 0.0;
            DependencyObject o;
            for (var i = 0;
                 i < nMax
                 && i < n
                 && (o = gen.ContainerFromIndex(i)) != null;
                 i++)
            {
                // calculate running height
                var h = o.GetValue(ActualHeightProperty) as double? ?? 0;
                height += h+2;
            }

            // set height, unsub and resub to listeners otherwise endless layout cycle
            if (height > 0)
            {
                list.SizeChanged -= List_OnSizeChanged;
                list.Height = height;
                list.SizeChanged += List_OnSizeChanged;
            }
        }
    }

Subscribe to your list's SizeChanged event with the above event handler and change nMax to your desired number of items.

If your list items are being virtualized, and ItemsContainerGenerator is null, you may have to force realization by changing the ItemsPanel to a concrete element like StackPanel:

   <ListView>
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
    </ListView>

In my case I had a further need to allow a dynamic number of items such that they fill the container completely (rather than a fixed number of items), this solution is modified from the above, but even more susceptible to layout changes and will probably require more careful integration:

 private void List_OnSizeChanged(object sender, SizeChangedEventArgs e)
 {
        var list = sender as ListViewBase;
        if (list == null || list.Items == null)
        {
            return;
        }

        var n = list.Items.Count;
        var containingFixedGrid = FindVisualParent<Grid>(list);  // this is an attempt to get the first container that is bounded by the actual height of the UI, in my case and the general case it is probably going to be a grid, but you may need to update this to whatever element makes sense; you can also use a named element
        if (containingFixedGrid == null || containingFixedGrid.ActualHeight > this.ActualHeight)
        {
            return;
        }

        var gen = list.ItemContainerGenerator;
        if (gen != null)
        {
            var adjustOffset = 2;  // small adjustment needed for alignment, adjust for your needs
            var height = 0.0;
            DependencyObject o;
            for (var i = 0; i < n && (o = gen.ContainerFromIndex(i)) != null; i++)
            {
                var item = o as FrameworkElement;
                if (item != null)
                {
                    // calculate the offset of this item to its containing grid
                    var t = item.TransformToVisual(containingFixedGrid) as MatrixTransform;
                    if (t != null && t.Matrix.OffsetY + item.ActualHeight < containingFixedGrid.ActualHeight)
                    {
                        height += item.ActualHeight + adjustOffset;
                    }
                }
            }

            // set height, unsub and resub to listeners otherwise endless layout cycle
            if (height > 0)
            {
                list.SizeChanged -= List_OnSizeChanged;
                list.Height = height;
                list.SizeChanged += List_OnSizeChanged;
            }
        }
    }

    public static T FindVisualParent<T>(DependencyObject element) where T : DependencyObject
    {
        var parent = element; 
        while (parent != null)
        {
            T type = parent as T; if (type != null)
            {
                return type;
            }
            parent = VisualTreeHelper.GetParent(parent) as UIElement;
        } 
        return null;
    }

Subscribe the desired lists to the handler and now the number of items in each list should fill the list entirely without any of the items being clipped. Adjust the adjustOffset to account for any small discrepancies.

apenn
  • 65
  • 5
  • Not really the only way. The best way is to add an attached behavoiour on ScrollViewer and react to `PreviewMouseWheel`. 1. Set it as handled so the ScrollViewer doesn't do the scrolling logic. 2. Use `ScrollToVerticalOffset` to scroll "manually" Done! A couple lines of code and it's pretty universal. Of course You don't have to use Attached Behaviours and Attached Properties and you can do it in the code behind. – michal.ciurus Jan 16 '14 at 07:48
  • if the question was "how do I scroll entire items at a time", that might be a valid answer (even then, there are better built-in ways, such as http://stackoverflow.com/a/12007763/2891965). However, the solution I give above addresses the problem of displaying an exact number of complete items in a list with no partial or clipped items. Unless you have an example, I fail to see how controlling the scrolling accomplishes controlling the dimensions of the list so it fits items exactly. – apenn Jan 20 '14 at 22:12