Неожиданное поведение UWP ScrollViewer

avatar
John
7 апреля 2018 в 23:40
605
1
1

При разработке приложения для платформы UWP я столкнулся с непредвиденным поведением. Когда у меня было слишком много контента для представления на одной странице, я воспользовался элементом управления ScrollViewer, который должен решить эту проблему.

Элемент управления позволяет прокручивать содержимое, но способ обработки ввода неестественен. Когда элемент управления получает Clicked, так что ни один фокусируемый элемент управления в ScrollViewer не щелкается, Focus передается первому фокусируемому элементу управления в его содержимом. Фактически это означает, что ScrollViewer прокручивается обратно наверх, чего я не ожидал.

Я бы хотел, чтобы ScrollViewer сохранял свою текущую позицию, как это делает веб-страница. Веб-страница не прокручивается назад к первому фокусируемому элементу управления, если щелкнуть пустое место. Другим подобным примером является то, что Grid или Stackpanel не передает фокус одному из своих дочерних элементов при нажатии.

Ниже приведен некоторый код xaml, демонстрирующий указанное поведение, просто создайте новый проект UWP и поместите его в сетку сгенерированного MainPage.xaml:<2924908353220>

<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, поэтому я опубликую ответ, который лучше реализует мое решение.

Вопрос остается: есть ли лучший способ решить эту проблему?

Источник

Ответы (1)

avatar
John
7 апреля 2018 в 23:51
0

Мое решение реализовано более удобным для разработчиков способом путем создания пользовательского UserControl, который обрабатывает ContentControl.

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
John
5 мая 2018 в 20:10
0

@CoCaIceDew Сначала я оставил это без проверки, так как хотел подождать около недели и посмотреть, есть ли у кого-нибудь лучшие решения представленной проблемы. Однако я забыл принять свое решение, так что спасибо за напоминание!