c# - SizeChanged 上的 ScrollViewer ChangeView

标签 c# wpf xaml windows-phone-8.1

我有在 HubSection 中使用的 DataTemplate:

<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>

在我的 ItemsControl 中,我有可以扩展的项目。我想要实现的是当该项目将展开以查看有关该项目的更多详细信息时。我希望 Scrollviewer 向下滚动(对于 ScrollViewer 的更改大小)。

SizeChanged 事件背后的代码:

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

我现在所做的并没有达到我的预期。我现在向下滚动到最后。但问题是它仅在项目扩展可用 View 大小时才滚动(显示 ScrollBar)。然后,如果我展开另一个项目,它就不起作用。如果我隐藏有关项目的详细信息(ScrollBar 也隐藏)并再次展开它,它将再次工作。 这就像 SizeChanged 事件仅在 ScrollViewer 开始运行但无限高度不变时发生。

我试过将行设置为“*”的网格,它没有任何改变。现在我尝试通过将高度绑定(bind)到 ItemsControl 的高度来设置高度 - 仍然是相同的行为。

您能帮我解决问题,展示思路或启发我一些解决方法吗?

编辑:

我准备了一些代码来查看到底发生了什么。

1) 创建新项目 -> Store Apps (c#) -> Windows Phone 8.1 (Blank App) 并将其命名为“scroll”

2) 将此代码粘贴到 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) 将此代码粘贴到 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) 运行应用

5) 当您添加前两个项目时,您无法滚动它们,但是当您添加更多项目时,您可以看到它会在项目“需要更多空间”显示并且出现滚动条时立即向下滚动。但是随着添加更多项目它不起作用。如果您删除项目并再次添加“第三个”项目,它将向下滚动。

我希望它在每次滚动查看器的大小发生变化时向下滚动(在这种情况下,当出现新项目时,但请记住,当项目在我的原始解决方案中“扩展”并且同时可以有很少的扩展项目时,它应该可以工作)。

最佳答案

我一直在修改解决方案,我终于找到了一种方法来做到这一点。 我认为问题是我不明白 ScrollViewer 是如何工作的。我将滚动高度作为 UIElement 高度,希望 SizeChanged 被触发,但事实并非如此。 ScrollViewer 没有改变它的大小,因为它只是占用了它可能占用的整个空间,然后只显示其中的内容量(就像 ScrollViewer 几乎总是无限高除非它小于实际可用的 View 空间)。添加前两项 SizeChanged 事件也触发了第三项,然后什么也没发生。这证明了这一点。

每次 ScrollViewer(或在本例中为 Grid)的大小发生变化时,我都需要触发 SizeChanged。解决方案非常简单,但仍然需要了解 ScrollViewer 的工作原理 - 现在看起来很明显,它永远不会超过可用空间。

为使其工作而进行的更改:

<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>

和代码隐藏:

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

关于c# - SizeChanged 上的 ScrollViewer ChangeView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30732803/

相关文章:

c# - 如何在不存在的文件夹中自动创建文件?

c# - 使用带有绑定(bind)的自定义附加属性

WPF + PRISM - 我应该将 WCF 客户端注入(inject) View-Model 吗?

wpf - 如何让 ScrollViewer 在 StackPanel 中工作?

c# - 包装类的代码生成器

c# - 返回多个枚举值的最佳方法是什么? (Java 和 C#)

c# - 命名空间还是程序集?

c# - 屏幕坐标到 sharpdx 设备上下文坐标

c# - 如何有效地响应 Windows RT 中的触摸事件

c# - 将 XAML 事件订阅到不同 C# 文件中的事件处理程序