How to make scrollable, resizable listview?

141 views Asked by At

Originally I wanted to make listview dependant on window height parameter, but to be honest I find this solution impractical, believing it can be done better.

So, here is an object of my interest:

<StackPanel>
            <TextBlock Text="{Binding LessonTitle}" Foreground="Black" HorizontalAlignment="Center" Margin="0,10,0,0" FontSize="36" Background="DimGray"></TextBlock>
            <Grid Height="Auto" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Hidden">
                <ListView x:Name="LV2" Background="DimGray"
                          BorderBrush="Transparent"
                          ItemsSource="{Binding Lessons}"
                       ScrollViewer.VerticalScrollBarVisibility="Hidden" 
                      ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                      SizeChanged="LV2_SizeChanged">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Height="1000"
                                        ScrollViewer.CanContentScroll="True" 
                                        ScrollViewer.VerticalScrollBarVisibility="Hidden">
                                    <TextBlock Width="Auto" FontSize="24" Foreground="Black" TextWrapping="Wrap">
                                        <Run Text="{Binding LessonText}"/>
                                    </TextBlock>
                                    <Image Source="{Binding LessonImage}"/>
                                </StackPanel>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
            </Grid>
        </StackPanel>

My listview and remnants of several attempts to make it scrollable (also also resizable with the window), and by scrollable I mean to make an entire list scroll smoothly, instead of by each element, but I have no clue how to achieve it.

I've already tried changing stackpanels to dockpanels/grids and vice versa, different scrollviwer placements, but none of them work. At best what I could've achieved so far was either scrolling listview's first item or scrolling item by item.

UPDATE:

So far I seem to have achieved the desired result:

<StackPanel>
            <TextBlock Text="{Binding LessonTitle}" Foreground="Black" HorizontalAlignment="Center" Margin="0,10,0,0" FontSize="36" Background="DimGray"></TextBlock>
            
            <Grid Height="Auto">
                <ScrollViewer x:Name="SV" VerticalScrollBarVisibility="Visible" SizeChanged="SV_SizeChanged">
                <ListView x:Name="LV2" Background="DimGray"
                          BorderBrush="Transparent"
                          ItemsSource="{Binding Lessons}"
                          Width="Auto" Height="Auto"
                          ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <Grid Height="Auto">
                                <TextBlock Width="Auto" FontSize="24" Foreground="Black" TextWrapping="Wrap">
                                        <Run Text="{Binding LessonText}"/>
                                </TextBlock>
                                <Image Source="{Binding LessonImage}"/>
                            </Grid>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
                </ScrollViewer>
            </Grid>
            
        </StackPanel>

This is how I did it in XAML, I've put an entire listview into a scrollviewer and made the size dependant on the window size throught this function:

private void SV_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      SV.Height = HeightModel.Instance.Height - 100;
      SV.Width = HeightModel.Instance.Width - 120;
    }

I also added width and height ot the listview.

However the size updates only upon changing the view and not in real time. So now I need help to make it actually responsive, either by making this function work in real time or purely throught XAML.

The other problem is that for some reason I cannot scroll the listview with mouse scholl, but only with mouse click, and I am completely confused why it happened to work like this.

1

There are 1 answers

8
isplayDayameNay On
<StackPanel>
            <TextBlock Text="{Binding LessonTitle}" Foreground="Black" HorizontalAlignment="Center" Margin="0,10,0,0" FontSize="36" Background="DimGray"></TextBlock>
            <Grid Height="Auto" >
                <ListBox x:Name="LV2" Width="400" Height="400" Background="DimGray"
                          BorderBrush="Transparent"
                          ItemsSource="{Binding Lessons}"
                      SizeChanged="LV2_SizeChanged">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Height="1000">
                                    <TextBlock FontSize="24" Foreground="Black" TextWrapping="Wrap">
                                        <Run Text="{Binding LessonText}"/>
                                    </TextBlock>
                                    <Image Source="{Binding LessonImage}"/>
                                </StackPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
            </Grid>
        </StackPanel>

note the repeat buttons present on the box and not the view

ListBox has these properties by default. just define the size of the visible area and everything extending beyond that comes scrollable. You may want to separate the items to their own item slots instead of a stackpanel that extends far beyond the dimensions of its contents as well. Here is an example:

<StackPanel>
    <TextBlock Text="{Binding LessonTitle}" Foreground="Black" HorizontalAlignment="Center" Margin="0,10,0,0" FontSize="36" Background="DimGray"></TextBlock>
    <Grid Height="504" >
        <ListBox x:Name="LV2" Height="400" Background="DimGray" BorderBrush="Transparent" ItemsSource="{Binding Lessons}" Panel.ZIndex="1" VerticalContentAlignment="Stretch" ScrollViewer.CanContentScroll="True">
                <TextBlock FontSize="24" Foreground="Black" TextWrapping="Wrap" Text="{Binding LessonText}" />
                <Image Source="/Image2.png" Width="297" Height="498"/>
        </ListBox>
    </Grid>
</StackPanel>

The result:

Note that if Lessons is a class and has properties in it then the box will automatically add them when defined as the ItemssSource. Also Some controls won't allow you to manipulate the items beyond the scope of its privileges if it is inheriting from an itemssource, instead manipulate the source itself to change the items.

If you need to populate the list with a larger quantity of items the easiest way would be in the .cs / vb codebehind. The C# would be something like:

var lessons = //the source of the items.
foreach(var item in lessons){ LV2.Items.Add((new Grid/Canvas/Panel{ Child/Children.Add/Content = item }));

If you require multiple types of parent controls add an if else loop to check the type and a foreach loop to handle the respective type

XAML

<ListBox x:Name="listBox"/>

Codebehind

        foreach (PropertyInfo propertyInfo in typeof(SomeTypeClass).GetProperties())
        {
            StackPanel panel = new StackPanel();
            panel.Orientation = System.Windows.Controls.Orientation.Horizontal;
            panel.VerticalAlignment = VerticalAlignment.Stretch;
            panel.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;

            var lbl = new TextBlock();
            lbl.VerticalAlignment = VerticalAlignment.Stretch;
            lbl.Text = propertyInfo.Name;
            lbl.ToolTip = "ToolTipStuff";
            lbl.Margin = mrgn;
            panel.Children.Add(lbl);
            var spcr = new Rectangle();
            spcr.Width = 25;
            spcr.Height = 15;
            panel.Children.Add(spcr);
    if(prop.GetType() == typeof(string)
    {
            var txtBx = new System.Windows.Controls.TextBox();
            panel.Children.Add(txtBx);
            txtBx.Text = "TextToTheRightOfPropertyName";
            txtBx.ToolTip = "DifferentToolTipStuff";

            var spcr2 = new Rectangle();
            spcr2.Width = 5;
            spcr2.Height = 5;
      // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
            panel.Children.Add(spcr2);
            var mtxtBx = new TextBox();
            mtxtBx.Height = 15;
  // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
            panel.Children.Add(mtxtBx);
    listBox.Items.Add(panel);
    }
    else if (prop.GetType() == typeof(AnotherTypeofVar)
    {
    //add the respective type of element to panel :
    }
                // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
           
    
        }

alternate codebehind

listbox.ItemsSource = //The class or database containing the data