WPF: ListView inside of Scrollviewer; Layout question

Jan picture Jan · Nov 11, 2010 · Viewed 8.4k times · Source

i do have a question about layouting with a listview inside of a scrollviewer. Once a listview is inside a scrollviewer it does use it's maximum size and doesn't scroll itself cause the scrollviewer offers unlimited amount of space to the controls inside of it. The problem is, that controls that are below a long list are only visible if the user scrolls down and I want to make the listview only use the space that is necessary and use a scrollbar itself. Pictures do tell more information than words (links for pictures also do tell much as my reputation isn't at 10 yet.. Edit2: well i can use only one link so i copied all pictures onto one). If the lists isn't long everything is okay:

Picture 1 : http://i.stack.imgur.com/7dDEC.jpg

Now if the list is longer the controls below move down into the invisible land:

Picture 2 : see link from picture 1

What I do want to happen now is this:

Picture 3 : see link from picture 1

That itself isn't really a problem cause we could put everything in a dockpanal and do dock the controls below to Dock.Below and Top to Top and let the listview fill the center with "lastchildfill". Now for the real problem. What if the window gets smaller? Then at first the listview disappears and then also everything else without having a scrollbar to scroll to the controls on the bottom.

Picture 4 : see link from picture 1

The ideal solution i am searching for is to have scrollbars on the window (or a root scrollviewer) which would enable us to scroll to every section of the window like this and just have the outer scrollbars to be visible once everything is a minimum size.

Picture 5 : see link from picture 1

ANY IDEAS? too many pictures? here's a little bit of xaml for everyone to try making it work (it's just a fast example windows...)

<Window x:Class="WpfTest1.ScrollTestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ScrollTestWindow" Height="400" Width="700">
    <ScrollViewer >
        <DockPanel LastChildFill="True" ScrollViewer.CanContentScroll="True" ScrollViewer.HorizontalScrollBarVisibility="Visible">
            <Grid DockPanel.Dock="Top">
                <TextBlock Text="Example controls above listview" Background="LightGray" FontSize="30"></TextBlock>
            </Grid>
            <Grid DockPanel.Dock="Bottom">
                <TextBlock Text="Example controls below listview" Background="LightGray" FontSize="30"></TextBlock>
            </Grid>
            <ListView FontSize="30">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Width="190" Header="Date" />
                        <GridViewColumn Width="200" Header="Day Of Week"  DisplayMemberBinding="{Binding DayOfWeek}" />
                        <GridViewColumn Width="120" Header="Year" DisplayMemberBinding="{Binding Year}" />
                    </GridView>
                </ListView.View>
                <sys:DateTime>1/1/1</sys:DateTime>
                <sys:DateTime>1/1/1</sys:DateTime>
                <sys:DateTime>1/1/1</sys:DateTime>
                <sys:DateTime>1/1/1</sys:DateTime>
                <sys:DateTime>1/1/1</sys:DateTime>
                <sys:DateTime>1/1/1</sys:DateTime>
                <sys:DateTime>1/1/1</sys:DateTime>
                <sys:DateTime>1/1/1</sys:DateTime>
            </ListView>
        </DockPanel>

    </ScrollViewer>

Answer

Markus picture Markus · Oct 24, 2012

Ok. So I had the same problem, but have now managed to solve it!

My project looks a little different than yours, but I think that it should work for you to. The solution that I'm presenting also has some limitation to it. For example, it will only work if you only add one ListView! In your example you only have one, so that won't be a problem there. But for anyone that might want more ListViews, you'll have to add more functionality for deciding the sizes of the Views, and where to put them.

You will also have to have a MinHeight set for the ListView.

My solution is to create you own panel class that extends a StackPanel, override the MeasureOverride and the ArrangeOverride functions. And add the ListView into the created Panel

CustomPanel:

public class ScrollablePanel : StackPanel
{
    protected override Size MeasureOverride(Size constraint)
    {
        Size tmpSize = base.MeasureOverride(constraint);
        tmpSize.Height = (double)(this.Children[0] as UIElement).GetValue(MinHeightProperty);
        return tmpSize;
    }

    protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
    {
        Size tmpSize = new Size(0, 0);

        //Width stays the same
        tmpSize.Width = finalSize.Width;
        
        //Height is changed
        tmpSize.Height = finalSize.Height;

        //This works only for one child!
        this.Children[0].SetCurrentValue(HeightProperty, tmpSize.Height);
        this.Children[0].Arrange(new Rect(new Point(0, 0), tmpSize));
        
        return tmpSize;
    }
}

The XAML

<Window x:Class="WpfTest1.ScrollTestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfTest1"
Title="ScrollTestWindow" Height="400" Width="700">
    <ScrollViewer >
        <DockPanel LastChildFill="True" ScrollViewer.CanContentScroll="True" ScrollViewer.HorizontalScrollBarVisibility="Visible">
            <Grid DockPanel.Dock="Top">
                <TextBlock Text="Example controls above listview" Background="LightGray" FontSize="30"></TextBlock>
            </Grid>
            <Grid DockPanel.Dock="Bottom">
                <TextBlock Text="Example controls below listview" Background="LightGray" FontSize="30"></TextBlock>
            </Grid>
            <local:ScrollablePanel>
                <ListView FontSize="30" MinHeight="80">
                    <ListView.View>
                        <GridView>
                            <GridViewColumn Width="190" Header="Date" />
                            <GridViewColumn Width="200" Header="Day Of Week"  DisplayMemberBinding="{Binding DayOfWeek}" />
                            <GridViewColumn Width="120" Header="Year" DisplayMemberBinding="{Binding Year}" />
                        </GridView>
                    </ListView.View>
                    <sys:DateTime>1/1/1</sys:DateTime>
                    <sys:DateTime>1/1/1</sys:DateTime>
                    <sys:DateTime>1/1/1</sys:DateTime>
                    <sys:DateTime>1/1/1</sys:DateTime>
                    <sys:DateTime>1/1/1</sys:DateTime>
                    <sys:DateTime>1/1/1</sys:DateTime>
                    <sys:DateTime>1/1/1</sys:DateTime>
                    <sys:DateTime>1/1/1</sys:DateTime>
                </ListView>
            </local:ScrollablePanel>
        </DockPanel>

    </ScrollViewer>
</Window>

It was a long time ago this question was asked, but I hope that this answer will help at least someone!

I also like to thank @sisyphe for all the help needed to solve this problem :)