UWP ScrollViewer 意外行为

UWP ScrollViewer unexpected behavior

在为 UWP 平台开发应用程序时,我遇到了一个意外行为案例。当我在一页上显示太多内容时,我利用了 ScrollViewer 控件,它应该可以解决这个问题。

该控件允许滚动内容,但它处理输入的方式不自然。当控件被 Clicked 时,ScrollViewer 中没有可聚焦的控件被单击,Focus传递给其内容中的第一个可聚焦控件。这实际上意味着 ScrollViewer 滚动回顶部,这不是我不希望的行为。

我希望 ScrollViewer 保持当前位置,就像网页一样。如果单击空白位置,网页不会滚动回第一个可聚焦控件。另一个类似的例子是 GridStackpanel 在单击时不会将焦点传递给其中一个子项。

下面是演示上述行为的一些 xaml 代码,只需创建一个新的 UWP 项目 并将其放在生成的 [ 的网格中=50=]:

<ScrollViewer Height="400"> <!-- Unexpected and unnatrual behavior -->
    <StackPanel Width="250" Background="DarkGray" Padding="10"> <!-- With Content Height > ScrollViewer Height -->
        <TextBlock Text="Unnatural scroll behavior" FontWeight="Bold"/>
        <TextBox Text="Some focusable control"/>
        <TextBlock Height="800" Text="On focus => pass focus to first available => scroll to the top" TextWrapping="Wrap"/>
        <TextBox Text="Some focusable control"/>
    </StackPanel>
</ScrollViewer>

我确实想出了一个解决方案,但是这似乎是解决当前问题的正确方法,这似乎很奇怪:

<ScrollViewer Height="400">
    <!-- Corrected behaviour, but extra ContentControl. Easily forgotten -->
    <ContentControl>
        <StackPanel Width="250" Background="Gray" Padding="10">
            <!-- With Content Height > ScrollViewer Height -->
            <TextBlock Text="Corrected scroll behavior" FontWeight="Bold"/>
            <TextBox Text="Some focusable control"/>
            <TextBlock Height="800" Text="On focus => no scroll" TextWrapping="Wrap"/>
            <TextBox Text="Some focusable control"/>
        </StackPanel>
    </ContentControl>
</ScrollViewer>

这行得通,但是总是记得添加 ContentControl 很烦人,这就是为什么我会 post 一个以更好的方式实现我的解决方案的答案.

问题仍然存在:有没有更好的方法来解决这个问题?

我的解决方案通过创建处理 ContentControl.

的自定义 UserControl 以对开发人员更友好的方式实施

Scroller.xaml:

<UserControl
    x:Class="Controls.Scroller"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <ScrollViewer Name="ScrollerControl" Height="{x:Bind Height, Mode=TwoWay}" Width="{x:Bind Width, Mode=TwoWay}">
        <ContentControl Content="{x:Bind ScrollContent}"/>
    </ScrollViewer>

</UserControl>

Scroller.xaml.cs:

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;

namespace Controls
{
    [ContentProperty(Name = "ScrollContent")]
    public sealed partial class Scroller : UserControl
    {
        public static readonly DependencyProperty ScrollContentProperty = DependencyProperty.Register("ScrollContent", typeof(object), typeof(object), new PropertyMetadata(new Grid()));

        public ScrollViewer ScrollViewer { get { return ScrollerControl; } }
        public object ScrollContent { get { return GetValue(ScrollContentProperty); } set { SetValue(ScrollContentProperty, value); } }

        public Scroller()
        {
            DataContext = this;
            this.InitializeComponent();
        }
    }
}

用法:

首先将以下行添加到主页的 xaml:

xmlns:cont="using:Controls"

然后按如下方式使用控件:

<cont:Scroller Height="400" x:Name="ScrollControl">
    <!-- Place any content in here -->
    <!-- The content below is just for example -->
    <StackPanel Width="250" Background="DarkGray" Padding="10">
        <TextBlock Text="Corrected scroll behaviour" FontWeight="Bold"/>
        <TextBox Text="Some focusable control"/>
        <TextBlock Height="800" Text="On focus => no scroll" TextWrapping="Wrap"/>
        <TextBox Text="Some focusable control"/>
    </StackPanel>
</cont:Scroller>

注意在代码中必须使用x:Name来引用控件。此外,如果您想访问 ScrollViewer,请使用

ScrollControl.ScrollViewer