やる気駆動型エンジニアの備忘録

WPF(XAML+C#)の話題を中心に.NET/Android/CI やたまに趣味に関するブログです

WPFでPathListBoxを使う

WPFのコントロールにPathListBoxというものがあります。
これは、通常のItemsControlとは少し異なる動作をします。
百聞は一見にしかず、まずは以下のイメージを御覧ください。

f:id:iyemon018:20170212225314g:plain

単純な動作ではありますが他のList系コントロールとは明らかに動作が異なります。
このような一風変わったPathListBoxですが、かなり簡単に実装することができます。

PathListBoxのプロパティ

PathListBoxには以下のプロパティがあります。

  • StartItemIndex
  • WrapItems
  • LayoutPaths

StartItemIndex

ItemsSourceに設定したコレクション要素の開始インデックスのプロパティです。
例えば"StartItemIndex=2"に設定すると以下の様になります。

f:id:iyemon018:20170212231943p:plain

WrapItems

このプロパティをtrueにすると、表示範囲外にあるコレクション要素をラップします。
falseにすると、表示範囲外のコレクション要素はラップされずに見切れてしまいます。

f:id:iyemon018:20170215152657j:plain

LayoutPaths

ItemsSourceにバインドしたコレクションを配置するためのレイアウト要素を設定することができます。
上記のサンプルでは、赤いPathを作成し、そのPathをLayoutPathsに設定しています。

以下、サンプルのソースです。

事前準備

PathListBoxを使用するには、以下のアセンブリを参照する必要があります。
Blendを使用すればドラッグアンドドロップで自動的に参照が追加されます。

PathListBoxを参照するには、予め以下の名前空間を追加しておきましょう。

xmlns:ec="http://schemas.microsoft.com/expression/2010/controls"
<Window x:Class="PathListBoxSample.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:ec="http://schemas.microsoft.com/expression/2010/controls"
        xmlns:local="clr-namespace:PathListBoxSample"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="240"
        Height="240"
        mc:Ignorable="d">
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>
    <Grid>
        <ec:PathListBox ItemsSource="{Binding Values,
                                              Mode=OneWay}"
                        StartItemIndex="2"
                        WrapItems="True">
            <ec:PathListBox.LayoutPaths>
                <ec:LayoutPath SourceElement="{Binding ElementName=path}" />
            </ec:PathListBox.LayoutPaths>
        </ec:PathListBox>
        <Border>
            <Path x:Name="path"
                  Margin="47.958,56.296,51.484,48.649"
                  Data="M167.9651,297.05052 C167.1821,300.96551 298.95333,358.22029 298.95333,358.22029 L336.53545,481.93313 501.22995,268.87785 584.51969,339.3325"
                  Stretch="Fill"
                  Stroke="#FFE40000" />
        </Border>
        <Button Width="32"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Command="{Binding AddCommand,
                                  Mode=OneWay}"
                Content="+" />
        <Button Width="32"
                Margin="36,0,0,0"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Command="{Binding SubCommand,
                                  Mode=OneWay}"
                Content="-" />
    </Grid>
</Window>
  • MainViewModel.cs
namespace PathListBoxSample
{
    using System.Collections.ObjectModel;
    using System.Linq;
    using Microsoft.Practices.Prism.Commands;
    using Microsoft.Practices.Prism.Mvvm;

    public class MainViewModel : BindableBase
    {
        public ObservableCollection<int> Values { get; private set; }

        public DelegateCommand AddCommand { get; private set; }

        public DelegateCommand SubCommand { get; private set; }

        public MainViewModel()
        {
            Values = new ObservableCollection<int>(Enumerable.Range(1, 5));
            AddCommand = new DelegateCommand(() => Values.Add(Values.Max() + 1));
            SubCommand = new DelegateCommand(() =>
                                             {
                                                 if (Values.Any()) Values.Remove(Values.Max());
                                             });
        }
    }
}

このようにPathListBoxはWPFの中でもかなり特殊なコントロールです。
以下のページにもう少し詳しい使い方が記述されています。

パスに沿ったアイテムのレイアウト

今回作成したサンプルはこちら↓↓↓↓↓
github.com