WPF の Command に挑戦

はじめに

先日 DataBinding を使ってウィンドウにデータを表示したので、今回は Command を使ってボタンを押した時のアクションを追加してみます。

RoutedCommand を使う

public partial class EditDialog : Window
{
    // ファイル選択ダイアログを表示するコマンド
    public static readonly ICommand ReferenceCommand
        = new RoutedCommand("ReferenceCommand", typeof(Button));

    public EditDialog()
    {
        InitializeComponent();

        // パスは適当
        var item = new Item()
        {
            Title = "Firefox",
            FileName = @"C:\Program Files\Mozilla Firefox\Firefox.exe",
            WorkingDirectory = @"C:\Program Files\Mozilla Firefox\",
            Arguments = @"C:\Sample.html"
        };

        // データクラスをコントロールにバインドする
        titleTextBox.DataContext = item;
        fileNameTextBox.DataContext = item;
        workingDirTextBox.DataContext = item;
        parameterTextBox.DataContext = item;

        // ReferenceCommand とイベントハンドラ結びつける CommandBinding を作成
        var referenceCommandBinding = new CommandBinding(ReferenceCommand,
            referenceCommandBinding_Executed,
            referenceCommandBinding_CanExecute);

        // CommandBindig の登録
        referenceButton.CommandBindings.Add(referenceCommandBinding);
    }

    // コマンド実行する
    private void referenceCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
        bool? result = dialog.ShowDialog(this);
        if (result.HasValue && result.Value)
        {
            Item item = (Item)fileNameTextBox.DataContext;
            item.FileName = dialog.FileName;
        }
    }

    // コマンドを実行できるかどうか判断する
    private void referenceCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = fileNameTextBox.DataContext is Item;
    }
}

XAML を修正

修正箇所にはコメントを入れています。

<!-- Window タグ内で名前空間を指定しています-->
<Window x:Class="WpfSample.EditDialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:WpfSample="clr-namespace:WpfSample"
    Title="EditDialog" Height="400" Width="400" Background="#303030">
    <Window.Resources>
        <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
            <Setter Property="Margin" Value="2"/>
            <Setter Property="Height" Value="28"/>
            <Setter Property="Foreground" Value="#F0F0F0"/>
            <Setter Property="Background" Value="#0593E2"/>
        </Style>
        <Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
            <Setter Property="Background" Value="#444444"/>
            <Setter Property="Foreground" Value="#F0F0F0"/>
            <Setter Property="Margin" Value="2"/>
            <Setter Property="Height" Value="28"/>
        </Style>
        <Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}">
            <Setter Property="Margin" Value="2"/>
            <Setter Property="Height" Value="28"/>
            <Setter Property="Background" Value="#444444"/>
            <Setter Property="Foreground" Value="#F0F0F0"/>
        </Style>
        <Style x:Key="{x:Type Label}" TargetType="{x:Type Label}">
            <Setter Property="Foreground" Value="#F0F0F0"/>
        </Style>
    </Window.Resources>
    <DockPanel LastChildFill="True">
        <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" FlowDirection="RightToLeft">
            <Button Width="100" Content="キャンセル"/>
            <Button Width="100" Content="OK"/>
        </StackPanel>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="150"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="50"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Label Grid.Column="0" Grid.Row="0" Content="名前"/>
            <Label Grid.Column="0" Grid.Row="1" Content="ファイル名"/>
            <Label Grid.Column="0" Grid.Row="2" Content="パラメータ"/>
            <Label Grid.Column="0" Grid.Row="3" Content="作業フォルダ"/>
            <Label Grid.Column="0" Grid.Row="4" Content="実行時の大きさ"/>
            <Label Grid.Column="0" Grid.Row="5" Content="ツールチップ"/>
            <Label Grid.Column="0" Grid.Row="6" Content="ショートカットキー"/>
            <Label Grid.Column="0" Grid.Row="7" Content="ホットキー"/>
            <TextBox Name="titleTextBox" Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="2" Text="{Binding Title}"/>
            <TextBox Name="fileNameTextBox" Grid.Column="1" Grid.Row="1" Text="{Binding FileName}"/>
            <TextBox Name="parameterTextBox" Grid.Column="1" Grid.Row="2" Grid.ColumnSpan="2" Text="{Binding Arguments}"/>
            <TextBox Name="workingDirTextBox" Grid.Column="1" Grid.Row="3" Grid.ColumnSpan="2" Text="{Binding WorkingDirectory}"/>
            <ComboBox Name="windowSizeComboBox" Grid.Column="1" Grid.Row="4" Grid.ColumnSpan="2" SelectedIndex="0" >
                <ComboBoxItem Content="通常のウィンドウ"/>
                <ComboBoxItem Content="最大化"/>
                <ComboBoxItem Content="最小化"/>
            </ComboBox>
            <TextBox Name="toolTipTextBox" Grid.Column="1" Grid.Row="5" Grid.ColumnSpan="2"/>
            <TextBox Name="shortcutKeyTextBox" Grid.Column="1" Grid.Row="6"/>
            <TextBox Name="hotKeyTextBox" Grid.Column="1" Grid.Row="7" />

            <!-- 参照ボタンを押したときに実行する Command を指定-->
            <Button Name="referenceButton" Grid.Column="2" Grid.Row="1" Content="参照" Command="{x:Static WpfSample:EditDialog.ReferenceCommand}"/>

            <Button Grid.Column="2" Grid.Row="6" Content="クリア"/>
            <Button Grid.Column="2" Grid.Row="7" Content="クリア"/>
        </Grid>
    </DockPanel>
</Window>

名前空間の指定と、ボタンを押したときに実行する Command の指定を行っています。

最後に

OpenFileDialog を表示するだけのアクションを実現するだけでも、Command を使うと結構面倒だなぁ。プログラムの規模が小さいから便利さを感じないのかも?
あと CommandBinding は XAML で記述できるけれど、エントリが長くなるので次回に持ち越します。今日はここまで。