简体   繁体   中英

ScrollViewer ChangeView on SizeChanged

I have DataTemplate which I use in HubSection:

<DataTemplate x:Name="dataTemplate2">
    <Grid x:Name="greedy">
        <ScrollViewer x:Name="scroller" SizeChanged="ScrollViewer_SizeChanged" Height="{Binding Height,ElementName=greedy}" >
            <ItemsControl x:Name="itemsControl" 
                ItemsSource="{Binding}" 
                ItemTemplate="{StaticResource dataTemplateDetails}">
                <ItemsControl.ItemContainerTransitions>
                    <TransitionCollection>
                        <ReorderThemeTransition />
                        <NavigationThemeTransition />
                    </TransitionCollection>
                </ItemsControl.ItemContainerTransitions>
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</DataTemplate>

In my ItemsControl I have items which can be expandable. What I want to achieve is that when the item will expand to see more details about that item. I want Scrollviewer to scroll down (for amount of changed size of ScrollViewer ).

Code behind for SizeChanged event:

private void ScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
     ScrollViewer myScroll = (ScrollViewer)sender;
     myScroll.ChangeView(null, myScroll.ScrollableHeight, null, false);
}

What I have done now is not working as my expectation. I scroll down to the end right now. But the thing is it scrolls only when the items extend the size of available view (ScrollBar is shown). Then if I expand another item it doesn't work. If I hide details about item (ScrollBar hides too) and expand it again it will work again. It's like the SizeChanged event occures only when ScrollViewer is going into action but have infinite height which doesn't change.

I've tried Grid with row set to "*", it changes nothing. Now I try to set height by binding it to height of ItemsControl - still the same behaviour.

Could you help me with the solution, show the path of thinking or enlighten me with some workaround?

EDIT:

I prepared some code to work with to see what happens exactly.

1) Create New Project -> Store Apps (c#) -> Windows Phone 8.1 (Blank App) and name it "scroll"

2) Paste this code into MainPage.xaml

<Page
x:Class="scroll.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:scroll"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="DarkOliveGreen">

<Page.Resources>
    <DataTemplate x:Name="dataTemplateDetails">
        <Grid Name="grido" Grid.Row="1" Margin="10,10,10,10">
            <Grid.RowDefinitions>
                <RowDefinition Height="50" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="50" />
                <RowDefinition Height="50" />
            </Grid.RowDefinitions>
            <Border Grid.Row="0" Background="Black" CornerRadius="10" Opacity="0.4" />
            <Border Grid.Row="1" Background="Black" CornerRadius="10" Opacity="0.3" />
            <Border Grid.Row="2" Background="Black" CornerRadius="10" Opacity="0.2" />
            <Border Grid.Row="3" Background="Black" CornerRadius="10" Opacity="0.1" />
            <TextBlock Grid.Row="0" Text="{Binding Name}" Style="{StaticResource BaseTextBlockStyle}" HorizontalAlignment="Center"/>
            <Grid Grid.Row="1" HorizontalAlignment="Center">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Margin="5,0,5,0" Text="Description:" Style="{StaticResource BaseTextBlockStyle}"/>
                <TextBlock Grid.Column="1" Text="{Binding Description}" Style="{StaticResource BaseTextBlockStyle}"/>
            </Grid>
            <TextBlock Grid.Row="2" Text="Next row" Style="{StaticResource BaseTextBlockStyle}" HorizontalAlignment="Center"/>
            <TextBlock Grid.Row="3" Text="Next row" Style="{StaticResource BaseTextBlockStyle}" HorizontalAlignment="Center"/>
        </Grid>
    </DataTemplate>
    <DataTemplate x:Name="dataTemplate2">
        <Grid x:Name="greedy" >
            <ScrollViewer x:Name="scroller" SizeChanged="ScrollViewer_SizeChanged" VerticalAlignment="Top">
                <ItemsControl x:Name="itemsControl" 
                    ItemsSource="{Binding}" 
                    ItemTemplate="{StaticResource dataTemplateDetails}">
                    <ItemsControl.ItemContainerTransitions>
                        <TransitionCollection>
                            <ReorderThemeTransition />
                            <NavigationThemeTransition />
                        </TransitionCollection>
                    </ItemsControl.ItemContainerTransitions>
                </ItemsControl>
            </ScrollViewer>
        </Grid>
    </DataTemplate>
</Page.Resources>

<Page.BottomAppBar>
    <CommandBar Background="Black" Opacity="0.6" x:Name="myCommandBar">
        <AppBarButton Icon="Add" Label="Add" x:Name="AddItem" Click="Add_Click"/>
        <AppBarButton Icon="Delete" Label="Delete" x:Name="RemoveItem" Click="Delete_Click"/>
    </CommandBar>
</Page.BottomAppBar>
<Grid>
    <Hub x:Name="myHub" Header="Test">
        <HubSection x:Uid="myDetailsHubsection" x:Name="myDetailsHubsection" Header="Details" DataContext="{Binding Items}" ContentTemplate="{StaticResource dataTemplate2}" />
    </Hub>
</Grid>

3) Paste this code into MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace scroll
{
public sealed partial class MainPage : Page
{
    public static Details dataContextItems;

    public MainPage()
    {
        this.InitializeComponent();

        this.NavigationCacheMode = NavigationCacheMode.Required;

        dataContextItems = new Details();
    }

    public class TestItem
    {
        public string Name { get; set; }
        public string Description { get; set; }

        public TestItem(string n, string d)
        {
            Name = n;
            Description = d;
        }
    }

    public class Details : INotifyPropertyChanged
    {
        private ObservableCollection<TestItem> _items;
        public ObservableCollection<TestItem> Items
        {
            set
            {
                _items = value;
                NotifyPropertyChanged("Items");
            }
            get
            {
                return _items;
            }
        }

        public Details()
        {
            _items = new ObservableCollection<TestItem>();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        this.DataContext = dataContextItems;
    }

    private void Add_Click(object sender, RoutedEventArgs e)
    {
        TestItem iAmAnItem = new TestItem("Name of an item", "Long and detailed description of an item");
        dataContextItems.Items.Add(iAmAnItem);
    }

    private void Delete_Click(object sender, RoutedEventArgs e)
    {
        if (dataContextItems.Items.Count > 0)
            dataContextItems.Items.RemoveAt(0);
    }

    private void ScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        ScrollViewer myScroll = (ScrollViewer)sender;
        myScroll.ChangeView(null, myScroll.ScrollableHeight, null, false);
    }
}
}

4) Run the app

5) When you add first two items you can't scroll them but when you add more items you can see that it scrolls down as soon as items "need more space" to be shown and scrollbar occures. But with adding more items it doesn't work. If you delete items and add "third" item again it will scroll down.

I want it to scroll down everytime size of scrollviewer changes (in this case when new item occures but keep in mind it should work when item "extends" in my original solution and there can be few extended items simultaneously).

I've been tinkering with the solution and I finally found a way to do that. I think the problem is that I didn't understand how ScrollViewer does work. I took the scrolling height as UIElement height hoping for SizeChanged to be fired what isn't truth. ScrollViewer wasn't changing its size because it just took the whole space it could and then just displayed how much content it is in it (It's like ScrollViewer has almost always infinite height unless it's less than actual available view space). With adding first two items SizeChanged event was firing with third one too and then nothing happend. It proves that.

I needed SizeChanged to be fired everytime the size of ScrollViewer (or in this case the Grid ) was changing. Solution is very simple but still it needs understanding of how ScrollViewer works - and now it seems so obvious that it will never be greater than available space.

Changes made to make it work:

<DataTemplate x:Name="dataTemplate2">
<ScrollViewer x:Name="scroller" VerticalAlignment="Top" HorizontalAlignment="Stretch" IsEnabled="True" >
    <Grid VerticalAlignment="Top" x:Name="greedo" SizeChanged="greedo_SizeChanged">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <ItemsControl x:Name="itemsControl" 
                    ItemsSource="{Binding}" 
                    ItemTemplate="{StaticResource dataTemplateDetails}">
                <ItemsControl.ItemContainerTransitions>
                    <TransitionCollection>
                        <ReorderThemeTransition />
                        <NavigationThemeTransition />
                    </TransitionCollection>
                </ItemsControl.ItemContainerTransitions>
            </ItemsControl>
        </Grid>
    </ScrollViewer>
</DataTemplate>

and code behind:

private void greedo_SizeChanged(object sender, SizeChangedEventArgs e)
{
        Grid takingScroll = (Grid)sender;
        ScrollViewer myScroll = (ScrollViewer)takingScroll.Parent;
        myScroll.ChangeView(null, myScroll.ScrollableHeight, null, false);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM