「Androidは電気羊の夢を見るか」を読みたい管理者のブログ

仕事などでの色々な発見を記事にしてます。不定期更新。

MVVMモデルでカウンタを作るには

えー、これまで見てきた、オブジェクト指向を説明するために書かれたソースコードの中で、もっともシンプルなものはカウンターだと思ってます。

 

どういったプログラムかと言いますと、

 

「ボタンを押せば数字が上がっていく」

 

たったこれだけです。たったこれだけと侮るなかれ。

 

今までの、OOP以前のプログラミングで同じことを実現することを考えてみましょう。

たとえばC言語なら

int UpCount(int num){

  return ++num;

}

 と書けば、済む話かも知れません。

でも、カウンターを複数持ちたい場合は?

 

このカウンターが鳥を数えるためのバードカウンターだったとします。

カラスを数えるのに1つほしい

スズメを数えるのに1つほしい

ついでに野鳥研究会人数分欲しい

 

そう、1つだけでは足りないわけです。

 

そこでOOPの出番ですよ奥さん。

 

たとえばこんなコードになります。

class Counter{
    int num
    
    public UpCount(){
        num++;
    }
}

int main()
{
    Counter KarasuCounter = new Counter();
   
    Counter SuzumeCounter = new Counter();
   
    //UpCountを呼ぶたびに数値が上がっていきます。
    KarasuCounter.UpCount();
    KarasuCounter.UpCount();
    KarasuCounter.UpCount();
   
    //スズメを数えたい時はそれようのインスタンスを呼びます。
    SuzumeCounter.UpCount();
    SuzumeCounter.UpCount();
    SuzumeCounter.UpCount();
   
}

 だいたい雰囲気は伝わったのではないでしょうか。

 

 

これをMVVMModelでやりたいというのが今回の試みです。

 

ViewModelはこんな感じ

    class CounterViewModel:INotifyPropertyChanged
    {
        private int num = 0;
        public int Num
        {
            get
            {
                return num;
            }
            private set
            {
                num = value;
                NotifyPropertyChanged("Num");
            }
        }

        DelegateCommand upCommand = new DelegateCommand();
        public DelegateCommand UpCommand
        {
            get
            {
                return upCommand;
            }
            set
            {
                upCommand = value;
            }
        }

        public CounterViewModel()
        {
            UpCommand.ExecuteHandler = upCount;
        }

        private void upCount()
        {
            Num++;
        }

        // 概要:
        //     プロパティ値が変更されたときに発生します。
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

コマンドクラス

    public delegate void myDelegate();
    public class DelegateCommand : ICommand
    {
        public myDelegate ExecuteHandler;

        #region ICommand メンバー



        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            var d = ExecuteHandler;
            if (d != null)
                d();
        }

        public event EventHandler CanExecuteChanged;

        public void RaiseCanExecuteChanged()
        {
            var d = CanExecuteChanged;
            if (d != null)
                d(this, null);
        }

        #endregion
    }

 

XAMLの方はこんな感じ

<UserControl x:Class="othello.Counter"

             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:othello"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.DataContext>
        <local:CounterViewModel/>
    </UserControl.DataContext>
    <StackPanel>
        <TextBlock Text="{Binding Num,UpdateSourceTrigger=PropertyChanged}"/>
        <Button Command="{Binding UpCommand}" Content="upcount"/>
    </StackPanel>
</UserControl>

ついでだからWindowも作っちゃいましょう。

 <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:othello" x:Class="othello.CounterWindow"
        Title="CounterWindow" Height="300" Width="300">
    <Grid>
        <StackPanel Orientation="Horizontal">
            
        <StackPanel Margin="10">
            <TextBlock>カラスカウンター</TextBlock>
                <local:Counter HorizontalAlignment="Left" Margin="0,10,0,0" VerticalAlignment="Top" Width="70"/>

            </StackPanel>
        
        <StackPanel Margin="10">
            <TextBlock>スズメカウンター</TextBlock>
                <local:Counter HorizontalAlignment="Left" Margin="0,10,0,0" VerticalAlignment="Top" Width="70"/>

            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

 完成図はこんな感じ

 

ポイントはCommandクラスにDelegeteを使って関数を渡しているところ

 

あとはCommandのBindingSourceをプロパティにすることを忘れないようにしよう!(これ、よく忘れてしまうんですよね)

 

こんな風に、MVVMでオブジェクト指向ができるとめっさ便利ですよね...