WPF MVVM 實作

最近在研究Xamarin的MVVM,

想說以前寫過WPF,

卻是用Windows Form的寫法去做,

從未使用MVVM,

所以就來研究MVVM吧。

需求:金額乘以數量算出總價

第一個範例是透過Button去觸發計算

Model物件

namespace WPFMVVM
{
    public class Calc
    {
        public int count { get; set; }

        public int money { get; set; }

        public int total { get; set; }// { return count * money; } }
    }
}

View Model物件

需實作INotifyPropertyChanged介面

RelayCommand該類別內容為天空的垃圾場的範例中取得

namespace WPFMVVM
{
    public class CalcViewModel : INotifyPropertyChanged
    {
        public Calc calc { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 建立Command物件,可設定於Button Command屬性中
        /// </summary>
        public ICommand UpdateName { get { return new RelayCommand(UpdateTotalExecute, CanUpdateNameExecute); } }
        public CalcViewModel()
        {
            calc = new Calc()
            { count = 0 };

        }

        /// <summary>
        /// 數量
        /// </summary>
        public int Count
        {
            get { return calc.count; }
            set
            {
                if (calc.count != value)
                {
                    calc.count = value;
                    //當值有改變時,觸發event
                    RaisePropertyChanged("count");
                }
            }
        }

        /// <summary>
        /// 單價
        /// </summary>
        public int Money
        {
            get { return calc.money; }
            set
            {
                if (calc.money != value)
                {
                    calc.money = value;
                    //當值有改變時,觸發event
                    RaisePropertyChanged("money");
                }
            }
        }

        /// <summary>
        /// 金額
        /// </summary>
        public int Total
        {
            get { return calc.total; }

            set
            {
                if (calc.total != value)
                {
                    calc.total = value;
                    //當值有改變時,觸發event
                    RaisePropertyChanged("total");
                }
            }
        }

        private void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Command執行的商業邏輯
        /// </summary>
        void UpdateTotalExecute()
        {
            Total = Money * Count;
        }

        bool CanUpdateNameExecute()
        {
            return true;
        }
    }


}

Xaml部分

第一步驟:設定DataContext

第二步驟:綁定數量及單價的TextBox、金額的Label

第三步驟:設定計算Button的Command屬性

<Window x:Class="WPFMVVM.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFMVVM"
        mc:Ignorable="d"
        Title="Window1" Height="300" Width="300">
    <Window.DataContext>
        <local:CalcViewModel />
    </Window.DataContext>
    <Grid>
        <Label x:Name="label_Copy" Content="數量" HorizontalAlignment="Left" Margin="10,23,0,0" VerticalAlignment="Top" Width="51"/>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="78,25,0,0" TextWrapping="Wrap" Text="{Binding Count, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="66" />

        <Label x:Name="label_Copy1" Content="單價" HorizontalAlignment="Left" Margin="10,53,0,0" VerticalAlignment="Top" Width="51"/>
        <TextBox x:Name="textBox_Copy" HorizontalAlignment="Left" Height="23" Margin="78,55,0,0" TextWrapping="Wrap" Text="{Binding Money, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="66" />
        
        <Label x:Name="label_Copy2" Content="{Binding Total}" HorizontalAlignment="Left" Margin="10,98,0,0" VerticalAlignment="Top" Width="51"/>
        <Button x:Name="button" Content="計算" HorizontalAlignment="Left" Margin="30,140,0,0" VerticalAlignment="Top" Width="75" Command="{Binding UpdateName}"/>
    </Grid>
</Window>

結果

該範例參考天空的垃圾場 http://blog.sanc.idv.tw/2011/12/wpf-mvvm.html

 

第二個範例

需求:希望設定好[單價]或[數量]時,立即計算出[金額],不透過計算按鈕去計算

Model物件

制定total屬性的get內容

namespace WPFMVVM
{
    public class Calc
    {
        public int count { get; set; }

        public int money { get; set; }

        public int total { get { return count * money; } }
    }
}

ViewModel物件

依然實作INotifyPropertyChanged介面,

但不須設定Command物件,

當Count及Money屬性改變時,觸發Event的屬性為total

namespace WPFMVVM
{
    public class CalcViewModel : INotifyPropertyChanged
    {
        public Calc calc { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;

        public CalcViewModel()
        {
            calc = new Calc()
            { count = 0 };

        }

        /// <summary>
        /// 數量
        /// </summary>
        public int Count
        {
            get { return calc.count; }
            set
            {
                if (calc.count != value)
                {
                    calc.count = value;
                    //當值有改變時,觸發event
                    RaisePropertyChanged("total");
                }
            }
        }

        /// <summary>
        /// 單價
        /// </summary>
        public int Money
        {
            get { return calc.money; }
            set
            {
                if (calc.money != value)
                {
                    calc.money = value;
                    //當值有改變時,觸發event
                    RaisePropertyChanged("total");
                }
            }
        }

        /// <summary>
        /// 金額
        /// </summary>
        public int Total
        {
            get { return calc.total; }
        }

        private void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }


}

Xaml部分

第一步驟:依然定義DataContext

第二步驟:依然綁定數量及單價的TextBox、金額的Label

第三步驟:由於沒有計算按鈕,所以不需設定

<Window x:Class="WPFMVVM.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFMVVM"
        mc:Ignorable="d"
        Title="Window1" Height="300" Width="300">
    <Window.DataContext>
        <local:CalcViewModel />
    </Window.DataContext>
    <Grid>
        <Label x:Name="label_Copy" Content="數量" HorizontalAlignment="Left" Margin="10,23,0,0" VerticalAlignment="Top" Width="51"/>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="78,25,0,0" TextWrapping="Wrap" Text="{Binding Count, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="66" />

        <Label x:Name="label_Copy1" Content="單價" HorizontalAlignment="Left" Margin="10,53,0,0" VerticalAlignment="Top" Width="51"/>
        <TextBox x:Name="textBox_Copy" HorizontalAlignment="Left" Height="23" Margin="78,55,0,0" TextWrapping="Wrap" Text="{Binding Money, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="66" />
        
        <Label x:Name="label_Copy2" Content="{Binding Total}" HorizontalAlignment="Left" Margin="10,98,0,0" VerticalAlignment="Top" Width="51"/>
    </Grid>
</Window>

結果