basyura's blog

あしたになったらほんきだす。

xaml - ScrollViewer と ScrollBar を同期

諸事情による調査。非表示にした ScrollViewer の ScrollBar と、外側に配置して表示している ScrollBar のスクロール位置を同期する。

f:id:basyura:20180624165529p:plain:w300

<UserControl x:Class="VirtualScrollSample.MainPage"
    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">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="20" />
        </Grid.ColumnDefinitions>
        <ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Hidden" Name="Container">
            <StackPanel>
                <Grid Background="Orange" Height="100" />
                <Grid Background="Blue" Height="100" />
                <Grid Background="Yellow" Height="100" />
                <Grid Background="Green" Height="100" />
                <Grid Background="AliceBlue" Height="100" />
                <Grid Background="Coral" Height="100" />
                <Grid Background="Beige" Height="100" />
                <Grid Background="Black" Height="100" />
                <Grid Background="BlanchedAlmond" Height="100" />
                <Grid Background="Red" Height="100" />
                <Grid Background="Blue" Height="100" />
                <Grid Background="Yellow" Height="100" />
                <Grid Background="Green" Height="100" />
                <Grid Background="AliceBlue" Height="100" />
                <Grid Background="Coral" Height="100" />
                <Grid Background="Beige" Height="100" />
                <Grid Background="Black" Height="100" />
                <Grid Background="BlanchedAlmond" Height="100" />
            </StackPanel>
        </ScrollViewer>
        <ScrollBar  Grid.Column="1" Grid.Row="1" Name="ContainerScrollBar" Scroll="ContainerScrollBar_Scroll"/>
    </Grid>
</UserControl>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;

namespace VirtualScrollSample
{
    public partial class MainPage : UserControl
    {
        /// <summary>
        /// 
        /// </summary>
        public MainPage()
        {
            InitializeComponent();

            SetBinding(OffsetProperty, new Binding("VerticalOffset")
            {
                Source = Container,
                Mode = BindingMode.OneWay,
            });
        }
        /// <summary>
        /// ScrollViewer のスクロール位置を ScrollBar に反映する
        /// </summary>
        public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register(
            "Offset", typeof(double), typeof(MainPage), new PropertyMetadata(0d, (d, e) =>
            {
                MainPage page = d as MainPage;
                double offset = (double)e.NewValue / page.Container.ScrollableHeight;
                page.ContainerScrollBar.Value = offset;

            }));
        /// <summary>
        /// ScrollViewer のスクロール位置
        /// </summary>
        private double Offset 
        {
            get { return (double)this.GetValue(OffsetProperty); }
            set { this.SetValue(OffsetProperty, value); }
        }
        /// <summary>
        /// ScrollBar のスクロール位置を ScrollViewer に反映する
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ContainerScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            double offset = Container.ScrollableHeight * e.NewValue;
            Container.ScrollToVerticalOffset(offset);
        }
    }
}