F#でWPF --- テキストボックスCommand

今回はstringを送るCommandをTextBoxに追加します。
TextBoxにはCommandプロパティが用意されていないため、Behaviorを作成します。

Behaviorの基本は下記記事を参照してください。
F#でWPF --- チェックボックスCommand --- Behavior利用 - 何でもプログラミング

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

キーボードのフォーカスが外れた時に、テキストボックスの文字列がテキストブロックに反映されるアプリケーションです。
f:id:any-programming:20170203075044p:plain

TextBoxBehavior

LostKeyboradFocusのイベントでCommandを実行するようにします。

ついでにReturnでキーボードのフォーカスが外れるようにします。

BehaviorBaseは下記記事を参照してください。
F#でWPF --- チェックボックスCommand --- Behavior利用 - 何でもプログラミング

type TextBoxBehavior() =
    inherit BehaviorBase<TextBox>()
    static member val CommandProperty = DependencyProperty.Register("Command", typeof<ICommand>, typeof<TextBoxBehavior>)
    member this.Command with get()           = this.GetValue(TextBoxBehavior.CommandProperty) :?> ICommand
                        and  set(x:ICommand) = this.SetValue(TextBoxBehavior.CommandProperty, x)

    override this.OnAttached control =
        [ control.LostKeyboardFocus.Subscribe(fun _ -> if this.Command <> null then 
                                                          this.Command.Execute(control.Text))
          // Returnでキーボードフォーカスを外す
          control.KeyDown.Subscribe(fun x -> if x.Key = Key.Return then
                                                Keyboard.ClearFocus()) ]


アプリケーションコード

アプリケーション名はBehaviors.exeでTextBoxBehaviorはnamespace Behaviorsに定義されています。

TextBoxのTextはデフォルトでTwoWayなので、setterにアクセスしないようOneWayにします。

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

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

Xaml

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
        xmlns:local="clr-namespace:Behaviors;assembly=Behaviors"
        Title="MainWindow" Height="100" Width="200">
    <Grid>
        <TextBox Text="{Binding Text, Mode=OneWay}" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
            <i:Interaction.Behaviors>
                <local:TextBoxBehavior Command="{Binding SetText}" />
            </i:Interaction.Behaviors>
        </TextBox>
        <TextBlock Text="{Binding Text}" HorizontalAlignment="Left" Margin="13,37,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
    </Grid>
</Window>

F#

type Model = { Text : string }

type Msg = SetText of string

let updateModel model msg =
    match msg with
    | SetText x -> 
        { model with Text = x }

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


EventTriggerとInvokeCommandActionで実装

System.Windows.InteractivityのEventTriggerとInvokeCommandActionを利用してLostKeycoardFocus時にCommandを送ることができます。

またRelativeSourceのFindAncestorモードを利用することにより、TextBoxのTextプロパティをCommandParameterにバインドすることが可能です。

この例ではReturn入力時にキーボードフォーカスが外れることはありません。

<TextBox Text="{Binding Text, Mode=OneWay}" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="LostKeyboardFocus">
            <i:InvokeCommandAction Command="{Binding SetText}" CommandParameter="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}}}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>