ListBoxをカスタマイズして都道府県の地図を選択するUIを作成する

以前のこちらの投稿で、ListBoxのControlTemplateを変更して各リストアイテムを楕円形にするようなものを紹介しましたが、今回はこれを応用して都道府県の地図を選択するようなUIを作成していこうと思います。


完成後のスクリーンショットは下記のようなものになります(各県のスケールが合っていないのは許してください)。



これを作成する際のポイントとなる部分を説明していきます。


XAMLは下記のようになります(ポイントとなる部分以外は省略してあります)。
XAML

<Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   x:Class="Window1"
   Title="東北地方" Height="700" Width="330" FontSize="24">
    <Window.Resources>
        <XmlDataProvider x:Key="PrefecturesDS" Source="Data\tohoku.xml"/>
    </Window.Resources>
    <ListBox HorizontalContentAlignment="Center" ItemsPanel="{DynamicResource CanvasItemsPanel}">
        <ListBox.Resources>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
                <Setter Property="Width" Value="200"/>
                <Setter Property="Height" Value="200"/>
                <Setter Property="Background" Value="{Binding XPath=Color}"/>
                <Setter Property="Canvas.Top" Value="{Binding XPath=TopPosition}"/>
                <Setter Property="Canvas.Left" Value="{Binding XPath=LeftPosition}"/>
                <Setter Property="Content" Value="{Binding XPath=Name}"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <Grid>
                                <Path Data="{Binding XPath=Geometry}" Fill="{TemplateBinding Background}"/>
                                <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            </Grid>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsSelected" Value="True">
                                    <Setter Property="Background" Value="{StaticResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            <ItemsPanelTemplate x:Key="CanvasItemsPanel">
                <Canvas IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ListBox.Resources>
        <ListBox.ItemsSource>
            <Binding Source="{StaticResource PrefecturesDS}" XPath="/Prefectures/Prefecture"/>
        </ListBox.ItemsSource>
    </ListBox>
</Window>


データソースとなるXMLファイルですが、これには下記のようにName、Color、TopPosition、LeftPosition、Geometryの5つのデータが含まれています。
データ(XML)ファイル
<?xml version="1.0" encoding="utf-8"?>
<Prefectures>
  <Prefecture>
    <Name>青森</Name>
    <Color>Goldenrod</Color>
    <LeftPosition>65</LeftPosition>
    <TopPosition>2</TopPosition>
    <Geometry>F1 M 32.5379,148.80〜途中省略〜 32.5379,148.803 Z </Geometry>
  </Prefecture>
  <Prefecture>
    <Name>秋田</Name>
    <Color>DarkKhaki</Color>
…以下省略


Nameは県の名前、Colorは色、TopPositionとLeftPositionは配置する位置、Geometryは県の形のジオメトリデータとなっています。


GeometryのデータをControlTemplate内のPathのDataプロパティとバインドすることで、各リストアイテムがそれぞれの県の形に描画されます。


また、ItemsPanelTemplateを既定のVirtualizingStackPanelからCanvasへと変更し、添付プロパティ(attached property)であるTopとLeftをListBoxItemのStyle内でそれぞれTopPosition、LeftPositionとバインドしています。これにより各県がXMLデータで指定されている位置に配置されるようになります。


WPFのリストボックスは、各リストアイテムのデータの持ち方、形状、位置など様々な要素を非常に柔軟にカスタマイズすることができるようになっています。何かを選択するというようなUIのほとんどは、ListBoxを使って実現することができると思います。