John DMC
John DMC

Reputation: 95

WPF ScrollViewer gives my listview (in a grid row) the whole screen instead of a portion

The main grid on my usercontrol has 3 rows. The top row is a data-bound listvew that takes about 60% of the whole window (there's more data rows than can be displayed and the listview automatically displays a scroll bar - This is good). Second row is a gridsplitter and the 3rd takes up the rest of the display.

This 3rd row has a grid wrapped in a border and also contains a textbox that wraps & can grow larger. When it does, it sometimes pushes the buttons at the very bottom off the screen, so I thought that if I wrapped a ScrollViewer around the main grid that I'd keep my 3 rows on the screen in the same proportion with the existing listview scrollbar left intact and then just get an additional scrollbar on the right that would let me scroll down to see the buttons if the 3rd row grew too tall (like you do on this browser page with scroll bars for the code & the page scroller too.

What happens instead, is that the first row with the listview has expanded vertically to take the whole screen and I can't see rows 2 or 3 at all until I've scrolled to the end of all the items in the listview. I've tried various combinations of hardcoding row heights (bad, I know) 'Auto' & '*' to now avail.

Is there a way to accomplish what I'm trying? I didn't think I'd have to (and down't want to) re-engineer the whole screen for this.

Thanks, I'm new to WPF & it's fun but very frustrating at times!

I'm posting some XAML, below, but I'm not sure it will help.

 <ScrollViewer>
    <Grid Name="grdEvents" HorizontalAlignment="Center" >
    <Grid.RowDefinitions>
        <RowDefinition Height="60*" />
        <RowDefinition Height="10" />
        <RowDefinition Height="30*"/>
    </Grid.RowDefinitions>
    <ListView SelectionChanged="lvActivities_SelectionChanged" MouseDoubleClick="ListView_MouseDoubleClick" Grid.Row="0" Name="lvActivities" HorizontalAlignment="Stretch" LVLO:ListViewLayoutManager.Enabled="True" >
        <!--ItemContainerStyle="{StaticResource SelectedItem}"   MouseEnter="lvActivities_MouseEnter"  -->

        <ListView.ItemContainerStyle>
            <Style TargetType="ListBoxItem">

...

   </ListView>


    <GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch">  </GridSplitter>

    <Border Grid.Row="2" CornerRadius="20" 
            BorderBrush="Gray"
            Background="White"
            BorderThickness="2"
            Padding="8">

        <Grid Name="wpCurrentRow" DataContext="{Binding ElementName=lvActivities, Path=SelectedItem}" Grid.Row="2" Background="{StaticResource ResourceKey=MyBackGroundBrush}">

Upvotes: 1

Views: 906

Answers (2)

Mike Strobel
Mike Strobel

Reputation: 25623

I don't think you can accomplish what you want with relative row sizes. What you can do, however, is manually set a proportional height to the top row any time the ScrollViewer's size changes. But since you also have a splitter, you will want to stop doing this once the user adjusts the height manually. Take a look at this:

XAML:

<Window x:Class="StackOverflow.Wpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid>
    <ScrollViewer x:Name="_scrollViewer">
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition x:Name="_mainRow" />
          <RowDefinition Height="Auto" />
          <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <ListView />

        <GridSplitter x:Name="_splitter"
                      Grid.Row="1"
                      Height="5"
                      HorizontalAlignment="Stretch"
                      ResizeDirection="Rows"
                      ResizeBehavior="PreviousAndNext"
                      MouseDoubleClick="OnSplitterMouseDoubleClick" />

        <Grid Grid.Row="2" />
      </Grid>
    </ScrollViewer>
  </Grid>
</Window>

Code behind:

public partial class MainWindow
{
    private bool _shouldUpdateGridLayout;

    public MainWindow()
    {
        InitializeComponent();
        EnsureGridLayoutUpdates();
    }

    private void EnsureGridLayoutUpdates()
    {
        if (_shouldUpdateGridLayout)
            return;

        _scrollViewer.SizeChanged += OnScrollViewerSizeChanged;
        _splitter.DragCompleted += OnSplitterDragCompleted;
        _shouldUpdateGridLayout = true;
    }

    private void CancelGridLayoutUpdates()
    {
        if (!_shouldUpdateGridLayout)
            return;

        _scrollViewer.SizeChanged -= OnScrollViewerSizeChanged;
        _splitter.DragCompleted -= OnSplitterDragCompleted;
        _shouldUpdateGridLayout = false;
    }

    private void UpdateGridLayout()
    {
        _mainRow.Height = new GridLength(2 * _scrollViewer.ActualHeight / 3);
    }

    private void OnScrollViewerSizeChanged(object s, SizeChangedEventArgs e)
    {
        UpdateGridLayout();
    }

    private void OnSplitterDragCompleted(object s, DragCompletedEventArgs e)
    {
        CancelGridLayoutUpdates();
    }

    private void OnSplitterMouseDoubleClick(object s, MouseButtonEventArgs e)
    {
        EnsureGridLayoutUpdates();
        UpdateGridLayout();

        // Handle the event to prevent DragCompleted from being raised
        // in response to the double-click.
        e.Handled = true;
    }
}

Note that I chose to restore both the default size and automatic size management when the user double-clicks the splitter. It's not the prettiest solution, but it beats using fixed heights. Feel free to encapsulate the behavior in custom panel/control.

Upvotes: 1

Taugenichts
Taugenichts

Reputation: 1365

Instead of using proportions, use hardcoded heights on the rows. Change the 60* to 60 for the first row.

Also just for the sake of experimentation you could try this:

<Grid.RowDefinitions>
    <RowDefinition Height="2*"/>
    <RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox Name="ListBox1" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Grid.Row="0" Background="Blue"/>
<ScrollViewer Grid.Row="1">
    <StackPanel >   
        <Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Click="Button_Click" />
        <TextBox>Hello </TextBox>
    </StackPanel>
</ScrollViewer>

It adds a scroll viewer just to the second row and you then use a stack panel to store the rest of the elements. It makes it look a bit better imo too.

The elements were just added for example; replace them with your own.

Upvotes: 0

Related Questions