F#でWPF --- 可変個のコントロール --- 独自の条件で生成するコントロールを変更

下記記事にて、型でコントロールを分岐する実装をしました。
F#でWPF --- 可変個のコントロール --- 型で生成するコントロールを変更 - 何でもプログラミング

今回はユーザーが独自に定義した条件で分岐するよう実装します。

作成するアプリケーション

F#の判別共用体はXAMLのx:Typeで認識されないため、型による分岐ができません。

そこで今回は、判別共用体をバインドして図形を描画するアプリケーションを作成してみます。

type Shape =
    | Circle of Size : double
    | Square of Size : double

window.DataContext <- { Shapes = 
    [ Square(10.0); Circle(20.0); Square(30.0) ] }

f:id:any-programming:20170224222408p:plain

DataTemplateSelector

独自の条件で分岐するにはDataTemplateSelectorを利用します。

SelectTemplateを実装し、ItemTemplateSelectorに設定します。

type MyDataTemplateSelector() =
    inherit DataTemplateSelector()
    override this.SelectTemplate(item, container) = // DataTemplate
<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemTemplateSelector>
        <local:MyDataTemplateSelector />
    </ItemsControl.ItemTemplateSelector>
    <ItemsControl.Resources>
        <DataTemplate x:Key="template1">
            <!-- Control -->
        </DataTemplate>
        <DataTemplate x:Key="template2">
            <!-- Control -->
        </DataTemplate>
    </ItemsControl.Resources>
</ItemsControl>


アプリケーションコード

XAML

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DataTemplateSelectors;assembly=ShapeDataTemplateSelector"
        Title="MainWindow" Height="100" Width="140">
    <Grid>
        <ItemsControl ItemsSource="{Binding Shapes}">
            <ItemsControl.ItemTemplateSelector>
                <local:ShapeDataTemplateSelector />
            </ItemsControl.ItemTemplateSelector>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.Resources>
                <DataTemplate x:Key="circleTemplate">
                    <Ellipse Width="{Binding Size}" Height="{Binding Size}" Fill="Orange" Margin="10" />
                </DataTemplate>
                <DataTemplate x:Key="squareTemplate">
                    <Rectangle Width="{Binding Size}" Height="{Binding Size}" Fill="Orange" Margin="10" />
                </DataTemplate>
            </ItemsControl.Resources>
        </ItemsControl>
    </Grid>
</Window>

DataTemplateSelectors.fs

namespace DataTemplateSelectors

open System.Windows
open System.Windows.Controls

type Shape =
    | Circle of Size : double
    | Square of Size : double

type ShapeDataTemplateSelector() =
    inherit DataTemplateSelector()
    override this.SelectTemplate(item, container) = 
        let templateName = 
            match item with
            | :? Shape as shape -> 
                match shape with
                | Circle _ -> "circleTemplate"
                | Square _ -> "squareTemplate" 
            | _ -> ""
        (container :?> FrameworkElement).FindResource(templateName) :?> DataTemplate

Program.fs

open System
open System.Windows
open DataTemplateSelectors

type ViewModel = { Shapes : Shape list }

[<STAThread>]
[<EntryPoint>]
let main argv = 
    let window = Application.LoadComponent(Uri("MainWindow.xaml", UriKind.Relative)) :?> Window
    window.DataContext <- { Shapes = 
        [ Square(10.0)
          Circle(20.0)
          Square(30.0) ] }
    Application().Run(window) |> ignore
    0


判別共用体のプロパティ

今回はCircleやRectangleのSizeをバインドして利用しました。

これは判別共用体のパラメータがプロパティとして現状解釈されているためであり、今後仕様が変わってバインディングに利用できなくなるかもしれません。