F#でWPF --- 可変個のコントロール

F#でWPFプロジェクトを作成する方法は下記記事を参照してください。
F#でWPF --- ウィンドウ表示 - 何でもプログラミング

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

ボタンを押す度、ランダムな位置に円シェイプを追加するアプリケーションを作成します。

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

コード(F#側)

コード内で利用しているDataContextは下記記事を参照してください
F#でWPF --- Elm Architectureを利用したMVVM - 何でもプログラミング

open System
open System.Windows

type Msg = AddCircle

type Model = { Circles : Point list }

let initialModel = { Circles = [] }

let random = Random()

let updateModel model msg =
    match msg with
    | AddCircle ->
        let p = Point(random.Next(100) |> double, random.Next(100) |> double)
        { model with Circles = p :: model.Circles }

[<STAThread>]
[<EntryPoint>]
let main argv = 
    let window = Application.LoadComponent(Uri("MainWindow.xaml", UriKind.Relative)) :?> Window
    window.DataContext <- DataContext(initialModel, updateModel, id)
    Application().Run(window) |> ignore
    0


ItemsControl

可変個のコントロールを表示する際に、ItemsControlが利用できます。

基本的な使い方は下記のようなフォーマットになります。

<ItemsControl ItemsSource="{Binding Data}" >
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <!-- Controlを配置するPanel -->
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <!-- Control -->
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>


可変個の円

Canvas上に可変個のEllipseを配置するにはこのようなコードになります。

Ellipseは全てItemContainerで包まれているため、Canvas.Leftを設定する場合はItemContainerStyleで設定する必要があります。

<ItemsControl ItemsSource="{Binding Circles}" >
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Ellipse Width="10" Height="10" Fill="Orange" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Canvas.Left" Value="{Binding X}" />
            <Setter Property="Canvas.Top"  Value="{Binding Y}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>


ItemContainerStyleを利用しない場合

EllipseをCanvasで包みます。

<ItemsControl ItemsSource="{Binding Circles}" >
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Canvas>
                <Ellipse Canvas.Left="{Binding X}" Canvas.Right="{Binding Y}" Width="10" Height="10" Fill="Orange" />
            </Canvas>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>


Xaml全体

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        Title="MainWindow" Height="180" Width="140">
    <Grid>
        <ItemsControl ItemsSource="{Binding Circles}" Width="100" Height="100" Margin="10,10,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Ellipse Width="10" Height="10" Fill="Orange" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Left" Value="{Binding X}" />
                    <Setter Property="Canvas.Top"  Value="{Binding Y}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
        <Button Content="追加" Command="{Binding AddCircle}" HorizontalAlignment="Left" Margin="10,115,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window>