WPF listboxとsliderを連携させたい(スクロールさせたい)

WpfSliderScroll.jpg
WpfSliderScroll.jpg

WPF Listboxを利用してボタンを配置します。
画面からはみ出る部分をSliderを利用してスクロールさせます。
標準のスクロールバーだとデザイン的にそっけないので、、、。

Listboxと、Sliderを連携させるためにUIAutomationを使います。


様々な形で提供されているアプリケーションの UI を外部から包括的に操作できる事を目的として作られた API 群で、UI 情報を収集するためのツリー構造や、状態の変化を検知するためのイベントを通じて、様々な UI 情報にアクセスしそれを操作することができる。

UIAutomationを使うことでスクロールさせることもできます。UIAutomationを使うために、下記の2つを参照に追加します。

UIAutomationProvider
UIAutomationTypes

ListboxにGridを配置し、その中にButtonを配置する
c#コードからボタン名をバインディングさせる
UIAutomationを利用して、Sliderの位置からListboxのスクロール表示位置を割り当てる

Xaml

<Window x:Class="SliderThumb.MainWindow"
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"
xmlns:local="clr-namespace:SliderThumb"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Slider x:Name="slider" HorizontalAlignment="Left" Margin="229,10,0,0" VerticalAlignment="Top" Height="299" Width="31" ValueChanged="slider_ValueChanged" Orientation="Vertical" IsDirectionReversed="True"/>
<TextBox x:Name="SliderValue" HorizontalAlignment="Left" Height="23" Margin="287,23,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="201"/>
<Button x:Name="button" Content="上げる" HorizontalAlignment="Left" Margin="287,81,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
<Button x:Name="button_Copy" Content="下げる" HorizontalAlignment="Left" Margin="413,81,0,0" VerticalAlignment="Top" Width="75" Click="button_Copy_Click"/>
<ListBox x:Name="l_listbox" HorizontalAlignment="Left" Height="289" Margin="0,10,0,0" VerticalAlignment="Top" Width="220" BorderThickness="0">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="210" Height="60" >
<Button x:Name="B_test" Content="{Binding ButtonContent}" Height="49.627" Margin="1,-5,0,0" FontSize="29.333" FontWeight="Bold" Width="189.682" Foreground="#FFFFFFFF" HorizontalAlignment="Left">
<Button.Effect>
<DropShadowEffect/>
</Button.Effect>
<Button.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF46AF0E" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Button.Background>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

</Grid>
</Window>

C#コード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;

namespace SliderThumb
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            List<TestItem> items = new List<TestItem>();


            for (int i = 0; i < 30; i++)
            {
                items.Add(new TestItem(i + ":Test"));
            }
            l_listbox.ItemsSource = items;

            slider.Maximum = 30;
        }

        private void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            SliderValue.Text = slider.Value.ToString();
            l_listbox.SelectedIndex = (int)slider.Value;

            double scrollpoint = slider.Value / 30 * 100;

            // ListBoxからAutomationPeerを取得
            var peer = ItemsControlAutomationPeer.CreatePeerForElement(this.l_listbox);
            // GetPatternでIScrollProviderを取得
            var scrollProvider = peer.GetPattern(PatternInterface.Scroll) as IScrollProvider;
            // パーセントで位置を指定してスクロール
            scrollProvider.SetScrollPercent(
                // 水平スクロールは今の位置
                scrollProvider.HorizontalScrollPercent,
                // スクロール位置を割合で指定する
                scrollpoint);
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            slider.Value = slider.Value + 1;
        }

        private void button_Copy_Click(object sender, RoutedEventArgs e)
        {
            slider.Value = slider.Value - 1;
        }

        public class TestItem
        {
            public String ButtonContent { get; set; }

            public TestItem(String button_name)
            {
                this.ButtonContent = button_name;

            }

        }
    }
}

 

AutomaitionPeer

Screenshot of blogs.msdn.microsoft.com

ボタンやテキストボックスといったコントロール(型)を UI オートメーション用に公開してくれるもので、言ってみれば、各 UI を AutomationPeer を介して操作すると思っていただければいいかと思います。アプリケーションの公開されている UI 情報にアクセスするためのインターフェイスというかリモコンというかそんな感じのイメージですか。

IScrollProvider

子オブジェクトのコレクションのスクロール可能なコンテナーの役割を果たすコントロールへの、UI オートメーション クライアントのアクセスをサポートするメソッドとプロパティを公開します。このコントロールの子は、IScrollItemProvider を実装する必要があります。

※peer: (年齢・地位・能力などが)同等の者; 同僚,同輩,仲間)

コードではAutomaitionPeerを利用して、listboxのUI情報にアクセスするためのインターフェースをUIオートメーション用に取得し、その情報のなあから、スクロールを提供する子オブジェクトを取得する。取得した子オブジェクトのメソッドを利用してスクロール一を割り当てる。

// ListBoxからAutomationPeerを取得
var peer = ItemsControlAutomationPeer.CreatePeerForElement(this.l_listbox);
// GetPatternでIScrollProviderを取得
var scrollProvider = peer.GetPattern(PatternInterface.Scroll) as IScrollProvider;
// パーセントで位置を指定してスクロール
scrollProvider.SetScrollPercent(
    // 水平スクロールは今の位置
    scrollProvider.HorizontalScrollPercent,
    // スクロール位置を割合で指定する
    scrollpoint);