2014年7月10日木曜日

ボタン用 ViewModel の作成

今回も Livet を使った MVVM ネタです。

ViewModel を書いていると同じことをたくさん書くので疲れます。
で、今回は ViewModelCommand でボタン用の ViewModel を作成してみました。

using System;
using Livet;
using Livet.Commands;

/// <summary>
/// <see cref="System.Windows.Controls.Button"/> の ViewModel の機能を提供します。
/// </summary>
public class ButtonViewModel : ViewModel
{
    #region Private fields

    /// <summary>
    /// コンテンツ。
    /// </summary>
    private object content;

    /// <summary>
    /// コマンド。
    /// </summary>
    private ViewModelCommand command;

    /// <summary>
    /// コマンドが実行可能かどうかの値。
    /// </summary>
    private bool canExecute;

    /// <summary>
    /// コマンドが実行可能かどうかが変化したことを自動通知するかどうかの値。
    /// </summary>
    private bool autoRaiseCanExecuteChanged;

    /// <summary>
    /// コマンドが実行するメソッド。
    /// </summary>
    private Action execute;

    #endregion // Private fields

    #region Constructors

    /// <summary>
    /// <see cref="ButtonViewModel"/> クラスの新しいインスタンスを初期化します。
    /// </summary>
    /// <param name="execute">コマンドが実行するメソッド。</param>
    /// <exception cref="ArgumentNullException"><c>execute</c> が null です。</exception>
    public ButtonViewModel(Action execute)
        : this(execute, true, true)
    {
    }

    /// <summary>
    /// <see cref="ButtonViewModel"/> クラスの新しいインスタンスを初期化します。
    /// </summary>
    /// <param name="execute">コマンドが実行するメソッド。</param>
    /// <param name="canExecute">コマンドが実行可能かどうかの値。</param>
    /// <exception cref="ArgumentNullException"><c>execute</c> が null です。</exception>
    public ButtonViewModel(Action execute, bool canExecute)
        : this(execute, canExecute, true)
    {
    }

    /// <summary>
    /// <see cref="ButtonViewModel"/> クラスの新しいインスタンスを初期化します。
    /// </summary>
    /// <param name="execute">コマンドが実行するメソッド。</param>
    /// <param name="canExecute">コマンドが実行可能かどうかの値。</param>
    /// <param name="autoRaiseCanExecuteChanged">コマンドが実行可能かどうかが変化したことを自動通知するかどうかの値。</param>
    /// <exception cref="ArgumentNullException"><c>execute</c> が null です。</exception>
    public ButtonViewModel(Action execute, bool canExecute, bool autoRaiseCanExecuteChanged)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        this.execute = execute;
        this.canExecute = canExecute;
        this.autoRaiseCanExecuteChanged = autoRaiseCanExecuteChanged;
    }

    #endregion // Constructors

    #region Properties

    /// <summary>
    /// コンテンツを取得または設定します。
    /// </summary>
    public object Content
    {
        get { return this.content; }
        set
        {
            if (value != this.content)
            {
                this.content = value;
                this.RaisePropertyChanged(() => this.Content);
            }
        }
    }

    /// <summary>
    /// コマンドを取得します。
    /// </summary>
    public ViewModelCommand Command
    {
        get
        {
            return this.command = this.command ??
                new ViewModelCommand(this.execute, () => this.CanExecute);
        }
    }

    /// <summary>
    /// コマンドが実行可能かどうかの値を取得または設定します。
    /// </summary>
    public bool CanExecute
    {
        get { return this.canExecute; }
        set
        {
            if (value != this.canExecute)
            {
                this.canExecute = value;
                this.RaisePropertyChanged(() => this.CanExecute);

                if (this.AutoRaiseCanExecuteChanged)
                {
                    this.RaiseCanExecuteChanged();
                }
            }
        }
    }

    /// <summary>
    /// コマンドが実行可能かどうかが変化したことを自動通知するかどうかの値を取得または設定します。
    /// </summary>
    public bool AutoRaiseCanExecuteChanged
    {
        get { return this.autoRaiseCanExecuteChanged; }
        set
        {
            if (value != this.autoRaiseCanExecuteChanged)
            {
                this.autoRaiseCanExecuteChanged= value;
                this.RaisePropertyChanged(() => this.AutoRaiseCanExecuteChanged);
            }
        }
    }

    #endregion // Properties

    #region Public methods

    /// <summary>
    /// コマンドを実行します。
    /// </summary>
    public void Execute()
    {
        if (this.command != null)
        {
            this.Command.Execute();
        }
    }

    /// <summary>
    /// コマンドが実行可能かどうかが変化したことを通知します。
    /// </summary>
    public void RaiseCanExecuteChanged()
    {
        if (this.command != null)
        {
            this.command.RaiseCanExecuteChanged();
        }
    }

    #endregion // Public methods
}

最低限必要なプロパティを実装しました。
おまけで、RaiseCanExecuteChanged メソッドの実行を制御するプロパティを用意。

使う側の MainWindowViewModel.cs はこんな感じで実装します。

public MainWindowViewModel()
{
    this.CopyButton = new ButtonViewModel(this.CopyAction)
    {
        Content = "コピー",
    };
}

public ButtonViewModel CopyButton { get; private set; }

private void CopyAction()
{
}

最後は、xaml です。

<Button Command="{Binding Path=CopyButton.Command}"
        Width="75"
        Height="24"
        Margin="3"
        HorizontalAlignment="Right"
        Content="{Binding Path=CopyButton.Content" />

バインドする名前が長くなってしまうのが難点だけど、ViewModel がスッキリできました。


頑張りすぎず脱力系でいこうと思います。
以上。

2014年7月3日木曜日

Levet で MVVM の基本

お久しぶりです。
1ヶ月以上の開いてしまいました。

今回は、前回の Livet を使った簡単な MVVM のサンプルを作りたいと思います。

機能は TextBox に入力した文字列を別の TextBox にコピーするだけ。
とても、シンプルです。

画面はこんな感じ。(UI のセンスがありません)



では、はじめます。

Visual Studio Express 2013 を立ち上げたら、「Livet WPF4.5 MVVM アプリケーション」を選んでプロジェクトを作成します。

MainWindowVIewModel.cs を開いてたら、TextBox 用のプロパティを2つ追加します。


private string inputText;

public string InputText
{
    get { return this.inputText; }
    set
    {
        if (value != this.inputText)
        {
            this.inputText = value;
            this.RaisePropertyChanged(() => this.InputText);
        }
    }
}

private string outputText;

public string OutputText
{
    get { return this.outputText; }
    private set
    {
        if (value != this.outputText)
        {
            this.outputText = value;
            this.RaisePropertyChanged(() => this.outputText);
        }
    }
}

次にコピーの処理を追加します。

private void CopyAction()
{
    this.OutputText = this.InputText;
}

最後にボタン用のコマンドを追加します。

private ViewModelCommand copyCommand;

public ViewModelCommand CopyCommand
{
    get
    {
        return this.copyCommand = this.copyCommand ??
            new ViewModelCommand(this.CopyAction);
    }
}

MainWindow.xaml を開いてコントロールを追加します。

<Window x:Class="Kaznagamine.LivetSample.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
        xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
        xmlns:v="clr-namespace:Kaznagamine.LivetSample.Views"
        xmlns:vm="clr-namespace:Kaznagamine.LivetSample.ViewModels"
        Title="MainWindow" Height="300" Width="400">
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <StackPanel Orientation="Horizontal">
            <TextBox Text="{Binding Path=InputText}"
                     Width="120"
                     Height="23"
                     Margin="3"
                     TextAlignment="Right" />
            <Button Command="{Binding Path=CopyCommand}"
                    Width="75"
                    Height="24"
                    Margin="3"
                    HorizontalAlignment="Right"
                    Content="コピー" />
            <TextBox Text="{Binding Path=OutputText}"
                     Width="120"
                     Height="23"
                     Margin="3"
                     TextAlignment="Right"
                     IsReadOnly="True"/>
        </StackPanel>
    </Grid>
</Window>

ビルドして実行したら、左の TextBox に文字列を入力してボタンを押します。
右の TextBox に同じ文字列が表示されたら完成です。


頑張りすぎず脱力系でいこうと思います。
以上。