読者です 読者をやめる 読者になる 読者になる

しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Xamarin で Command ・・・微妙;

Xamarin 版 SimpleTimer も完成間近

タイマー開始・一時停止・停止の処理を ViewModel の Command で追加してみました

    #region Privates

    /// <summary>
    /// 元の値
    /// </summary>
    private TimeSpan originValue;

    /// <summary>
    /// 元の最大値
    /// </summary>
    private double originMaxSeconds;

    /// <summary>
    /// タイマー開始時経過ミリ秒
    /// </summary>
    private int startTime;

    /// <summary>
    /// 排他制御オブジェクト
    /// </summary>
    private static readonly object LockObject = new object();

    #endregion //Privates

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public TopPageViewModel()
    {
        this.IsTimerStarted = false;
        this.IsEnableStop = false;

        this.timerValue = TimeSpan.FromMinutes(5);
        this.MaxSeconds = 3 * 60 * 60;
        this.originMaxSeconds = this.MaxSeconds;
            
        this.stopCommand = new Command(this.StopTimer);
    }

    /// <summary>
    /// タイマー開始フラグ の変更後の処理
    /// </summary>
    private void OnIsTimerStartedChanged()
    {
        this.IsEnableStart = !this.isTimerStarted || this.isTimerPaused;
        this.IsEnablePause = this.isTimerStarted && !this.isTimerPaused;
        this.IsEnableStop = this.isTimerStarted;
    }

    /// <summary>
    /// タイマー一時停止フラグ の変更後の処理
    /// </summary>
    private void OnIsTimerPausedChanged()
    {
        this.IsEnableStart = !this.isTimerStarted || this.isTimerPaused;
        this.IsEnablePause = this.isTimerStarted && !this.isTimerPaused;
    }
        
    #region TimerValue:タイマー残り秒数 プロパティ

    /// <summary>
    /// タイマー残り秒数
    /// </summary>
    private TimeSpan timerValue;

    /// <summary>
    /// タイマー残り秒数 の取得および設定
    /// </summary>
    public TimeSpan TimerValue
    {
        get { return this.timerValue; }
        set { this.SetProperty<TimeSpan>(ref this.timerValue, value); }
    }

    #endregion //TimerValue:タイマー残り秒数 プロパティ

    #region MaxSeconds:タイマー設定秒数 プロパティ
    /// <summary>
    /// タイマー設定秒数
    /// </summary>
    private double maxSeconds;

    /// <summary>
    /// タイマー設定秒数 の取得および設定
    /// </summary>
    public double MaxSeconds
    {
        get { return this.maxSeconds; }
        set { this.SetProperty<double>(ref this.maxSeconds, value); }
    }
    #endregion //MaxSeconds:タイマー設定秒数 プロパティ

    #region IsTimerPaused:タイマー一時停止フラグ プロパティ
    /// <summary>
    /// タイマー一時停止フラグ
    /// </summary>
    private bool isTimerPaused;

    /// <summary>
    /// タイマー一時停止フラグ の取得および設定
    /// </summary>
    public bool IsTimerPaused
    {
        get { return this.isTimerPaused; }
        set
        {
            if (this.SetProperty<bool>(ref this.isTimerPaused, value))
            {
                this.OnIsTimerPausedChanged();
            }
        }
    }
    #endregion //IsTimerPaused:タイマー一時停止フラグ プロパティ

    #region タイマー停止コマンド

    /// <summary>
    /// タイマー停止コマンド
    /// </summary>
    private ICommand stopCommand;

    /// <summary>
    /// タイマー停止コマンド の取得
    /// </summary>
    public ICommand StopCommand
    {
        get { return this.stopCommand; }
    }

    /// <summary>
    /// タイマー停止
    /// </summary>
    public void StopTimer()
    {
        Monitor.Enter(LockObject);
        try
        {
            this.IsTimerStarted = false;

            this.TimerValue = this.originValue;
            this.MaxSeconds = this.originMaxSeconds;
        }
        finally
        {
            Monitor.Exit(LockObject);
        }
    }

    #endregion //タイマー一時停止コマンド

    #region IsEnableStop:タイマー停止可能フラグ プロパティ
    /// <summary>
    /// タイマー停止可能フラグ
    /// </summary>
    private bool isEnableStop;

    /// <summary>
    /// タイマー停止可能フラグ の取得および設定
    /// </summary>
    public bool IsEnableStop
    {
        get { return this.isEnableStop; }
        set { this.SetProperty<bool>(ref this.isEnableStop, value); }
    }
    #endregion //IsEnableStop:タイマー停止可能フラグ プロパティ

停止の Command のみですが、Command の中に CanStop みたいなコマンド実行可否を返すメソッドを設定せず、IsEnableStop というプロパティで実装しています

Xamarin.Forms でも Command(Action execute, Func canExecute) といったコンストラクタが用意されているのになんでこのようにしているかというと

/// <summary>
/// Element プロパティ変更イベントハンドラ
/// </summary>
/// <param name="sender">イベント発行者</param>
/// <param name="e">イベント引数</param>
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    base.OnElementPropertyChanged(sender, e);

    switch (e.PropertyName)
    {
        case "IsEnableStop":
            this.stopButton.Enabled = this.Element.IsEnableStop;
            break;
    }
}

こうしないと OnElementPropertyChanged ViewRenderer に変更を伝搬できないからです;

うーん・・・やっぱり手続きである Command をデータバインドするということ自体に違和感あるし、MVVM ではなくてデータと手続きを分離する MVPVM にしたいところですね