【WPF】循環スクロールをUserControlで実装してみた

# はじめに
WPFでUWPのTimePickerを作ってみようとしたが、無限に循環するスクロールが標準コントロールになかったので、UserControlで作成してみることにした。


# 実装
```xml:CirculationScroll.xaml
<UserControl>
    <UserControl.Resources>
        <Storyboard x:Key="storyboardSlideDown" Completed="SlideDown_Completed">
            <DoubleAnimation
                Storyboard.TargetName="scrollItems"
                Storyboard.TargetProperty="(Canvas.Top)"
                From="-20"
                To="0"
                Duration="0:0:0.4" />
        </Storyboard>
        <Storyboard x:Key="storyboardSlideUp" Completed="SlideUp_Completed">
            <DoubleAnimation
                Storyboard.TargetName="scrollItems"
                Storyboard.TargetProperty="(Canvas.Top)"
                From="-20"
                To="-40"
                Duration="0:0:0.4" />
        </Storyboard>
        <Storyboard x:Key="storyboardSlideReset">
            <DoubleAnimation
                Storyboard.TargetName="scrollItems"
                Storyboard.TargetProperty="(Canvas.Top)"
                To="-20"
                Duration="0:0:0" />
        </Storyboard>
    </UserControl.Resources>
    <Grid ClipToBounds="True">
        <Canvas x:Name="slideCanvas" ClipToBounds="True">
            <ItemsControl
                    x:Name="scrollItems"
                    Canvas.Top="-20"
                    ItemsSource="{Binding ScrollList, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:CirculationScroll}}, Mode=TwoWay}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock
                                Height="20"
                                Text="{Binding Text, Mode=TwoWay}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Canvas>
    </Grid>
</UserControl>

```

UserControl.Resourcesでスクロールっぽく見せるためのStoryBoardを用意。表示するテキストはItemsControlにTextBlockを用意する。

```CS:CirculationScroll.xaml.cs
    public partial class CirculationScroll : UserControl
    {
        public CirculationScroll()
        {
            InitializeComponent();

            ScrollList = new ObservableCollection<ScrollText>(); 
        }

        public readonly static DependencyProperty ScrollListProperty = DependencyProperty.Register("ScrollList", typeof(ObservableCollection<ScrollText>), typeof(CirculationScroll), new FrameworkPropertyMetadata(default(ObservableCollection<ScrollText>), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public ObservableCollection<ScrollText> ScrollList
        {
            get { return (ObservableCollection<ScrollText>)GetValue(ScrollListProperty); }
            set { SetValue(ScrollListProperty, value); }
        }

        private void SlideDown_Completed(object sender, EventArgs e)
        {
            var reset = FindResource("storyboardSlideReset") as Storyboard;
            reset.Begin();

            ScrollList.Move(ScrollList.Count - 1, 0);
        }

        private void SlideUp_Completed(object sender, EventArgs e)
        {
            var reset = FindResource("storyboardSlideReset") as Storyboard;
            reset.Begin();

            ScrollList.Move(0, ScrollList.Count - 1);
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            if (e.Delta > 0)
            {
                var slideDown = FindResource("storyboardSlideDown") as Storyboard;
                slideDown.Begin();
            }
            else
            {
                var SlideUp = FindResource("storyboardSlideUp") as Storyboard;
                SlideUp.Begin();
            }

            base.OnMouseWheel(e);
        }
    }

    public class ScrollText
    {
        public ScrollText(string text)
        {
            Text = text;
        }
        public string Text { get; set; }
    }

```
依存関係プロパティにObservableCollectionを公開し、ユーザー側で表示文字列のListを設定することで、スクロールっぽくアニメーションできることを確認した。連続的にスクロールした場合にカクつくので改良が必要と思う。