2017년 9월 25일 월요일

(WPF예제)데이터 바인딩을 이용한 계산기 실습(MVVM, ViewModel)_추천닷...

(WPF예제)데이터 바인딩을 이용한 계산기 실습(MVVM, ViewModel)_추천닷넷/C#/자마린/WPF 실무교육센터 
n  지금까지 배운 데이터 바인딩 및 MVVM을 이용하여 간단한 계산기를 구현해 보자.
n  MainWindow.xaml
<Window x:Class="CalculatorNew.MainWindow"
        xmlns:local="clr-namespace:Calc"
        Title="Topcredu.co.kr WPF Calculator" Height="350" Width="425">

    <Window.Resources>
        <local:CalcViewModel x:Key="calcViewModel"/>
    </Window.Resources>
   
    <Grid HorizontalAlignment="Center"
        VerticalAlignment="Center">

        <!-- Grid의 행과 열을 정의 -->
        <Grid.RowDefinitions>
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="60" />
        </Grid.ColumnDefinitions>
       
        <!-- 최상단 행을 위한 내부 그리드 정의출력텍스트박스, BACK버튼, Clear버튼 -->
        <Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="50*" />
                <ColumnDefinition Width="50*" />
            </Grid.ColumnDefinitions>

            <!-- ViewModel 클래스의 DisplayText 속성과 바인딩 -->
            <Border BorderThickness="1" BorderBrush="Black">
                <TextBlock FontSize="15" VerticalAlignment="Center"
                           HorizontalAlignment="Center"                          
                       Text="{Binding DisplayText,Source={StaticResource calcViewModel}}" />
            </Border>


            <!-- 버튼의 Command 이벤트에 Command명을 지정하고 바인딩-->
            <!-- ViewModel 클래스에서 DeleteCharCommand 속성이 정의되야 하고 -->
            <!-- 실제 버튼이 눌러졌을 때 Command에 대한 이벤트 핸들러를 정의해야 한다.-->
            <Button Content="BACK"
              Command="{Binding DeleteCharCommand,Source={StaticResource calcViewModel}}"
              Grid.Column="1"/>
            <Button Content="Clear"
              Command="{Binding ClearCommand,Source={StaticResource calcViewModel}}"
              Grid.Column="2"/>
        </Grid>

        <Button Content="1"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="1"
            Grid.Row="1" Grid.Column="0" />

        <Button Content="2"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="2"
            Grid.Row="1" Grid.Column="1" />

        <Button Content="3"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="3"
            Grid.Row="1" Grid.Column="2" />
        <Button Content="+"
            Command="{Binding OperationCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="+"
            Grid.Row="1" Grid.Column="3" />

        <Button Content="4"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="4"
            Grid.Row="2" Grid.Column="0" />

        <Button Content="5"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="5"
            Grid.Row="2" Grid.Column="1" />

        <Button Content="6"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="6"
            Grid.Row="2" Grid.Column="2" />
        <Button Content="-"
            Command="{Binding OperationCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="-"
            Grid.Row="2" Grid.Column="3" />

        <Button Content="7"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="7"
            Grid.Row="3" Grid.Column="0" />

        <Button Content="8"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="8"
            Grid.Row="3" Grid.Column="1" />

        <Button Content="9"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="9"
            Grid.Row="3" Grid.Column="2" />
        <Button Content="*"
            Command="{Binding OperationCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="*"
            Grid.Row="3" Grid.Column="3" />

        <Button Content="0"
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="0"
            Grid.Row="4" Grid.Column="0" />

        <Button Content="."
            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="."
            Grid.Row="4" Grid.Column="1" />

        <Button Content="="
            Command="{Binding CalcCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="="
            Grid.Row="4" Grid.Column="2" />
        <Button Content="/"
            Command="{Binding OperationCommand,Source={StaticResource calcViewModel}}"
            CommandParameter="/"
            Grid.Row="4" Grid.Column="3" />
    </Grid>
</Window>
n  CalcViewModel.cs
using System;
using System.ComponentModel;
using System.Windows.Input;

namespace Calc
{   
    public class CalcViewModel : INotifyPropertyChanged
    {
        //아래 두 필드는 속성으로 구현되어 있다.
        //출력될 문자들을 담아둘 변수
        string inputString = "";

        //계산기화면의 출력 텍스트박스에 대응되는 필드
        string displayText = "";

        //속성이 바뀔때 이벤트 발생하도록 이벤트 정의
        public event PropertyChangedEventHandler PropertyChanged;

        //생성자
        public CalcViewModel()
        {
            //이벤트 핸들러 정의
            //숫자 버튼을 클릭할 때 실행
            this.AddCharCommand = new AddCharCommand(this);

            //백스페이스 버튼을 클릭할 때 실행한글자 삭제
            this.DeleteCharCommand = new DeleteCharCommand(this);

            //Clear 버튼을 클릭할 때 실행출력창을 전부 지운다.
            this.ClearCommand = new ClearCommand(this);

            //+,-,*,/ 버튼을 클릭할 때 실행
            //현재출력창의 숫자를 Op1 속성에 저장, Op속성에 클릭한 연산자 저장
            //계산기의 출력화면을 Clear
            this.OperationCommand = new OperationCommand(this);

            // =버튼을 클릭시 실행
            this.CalcCommand = new CalcCommand(this);
        }

        // Public 속성들을 정의
        public string InputString
        {
            internal set
            {               
                if (inputString != value)
                {
                    inputString = value;
                    OnPropertyChanged("InputString");
                    this.DisplayText = inputString;
                    ((DeleteCharCommand)this.DeleteCharCommand).OnCanExecuteChanged();
                }               
            }

            get { return inputString; }
        }

        //계산기의 출력창과 바인딩된 속성
        public string DisplayText
        {
            protected set
            {
                if (displayText != value)
                {
                    displayText = value;
                    OnPropertyChanged("DisplayText");
                }
            }
            get { return displayText; }
        }

        public string Op { getset; }
        public double Op1 { getset; }
        public double Op2 { getset; }

        // 숫자 클릭
        public ICommand AddCharCommand { protected setget; }

        // <- 클릭한글자씩 삭제
        public ICommand DeleteCharCommand { protected setget; }

        // C 클릭시 DisplayText 전체를 지운다.
        public ICommand ClearCommand { protected setget; }

        // +, -, *, / 클릭
        public ICommand OperationCommand { protected setget; }

        // = 클릭
        public ICommand CalcCommand { protected setget; }


        protected void OnPropertyChanged(string propertyName)
        {
            //이벤트를 발생시킨다.
            if (PropertyChanged != null)
                PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
        }
    }

    // CanExecute를 호출하기 위한 메소드
    // 예를들면
    // 처음 화면이 뜰때 BACK 버튼이 비활성화 되었다가
    // 입력이 들어오묜 활성화 되는때 이 메소드를 호출한다.
    public interface IBaseCommand : ICommand
    {
        void OnCanExecuteChanged();
    }

    class AddCharCommand : ICommand
    {
        private CalcViewModel viewModel;
        public event EventHandler CanExecuteChanged;

        public AddCharCommand(CalcViewModel viewModel)
        {
            this.viewModel = viewModel;
        }
        public bool CanExecute(object parameter)
        {
            return true;
        }
        // 1,2,,,, 숫자들을 눌렀을때 실행됨
        public void Execute(object parameter)
        {
            viewModel.InputString += parameter;                       
        }       
    }

    class DeleteCharCommand : IBaseCommand
    {
        private CalcViewModel viewModel;

        // OnCanExecuteChanged 메소드의
        // ommandManager.InvalidateRequerySuggested()를 호출하면
        // CanExecuteChanged 이벤트가 호출되어
        // CanExecute로 해당 Command가 있는 버튼을 활성화 또는 비활성화
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested += value; }
        }       
        public DeleteCharCommand(CalcViewModel viewModel)
        {
            this.viewModel = viewModel;
        }
        public bool CanExecute(object parameter)
        {           
            return viewModel.InputString.Length > 0;
        }
        // BACK 버튼을 눌렀을 때 실행됨
        public void Execute(object parameter)
        {
            viewModel.InputString = viewModel.InputString.Substring(0, viewModel.InputString.Length - 1);
        }
        public void OnCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }

    class ClearCommand : IBaseCommand
    {
        private CalcViewModel viewModel;

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested += value; }
        }

        public ClearCommand(CalcViewModel viewModel)
        {
            this.viewModel = viewModel;
        }
        public bool CanExecute(object parameter)
        {
            return viewModel.InputString.Length > 0; ;
        }
        public void Execute(object parameter)
        {
            // Clear
            viewModel.InputString = "";
        }
        public void OnCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }

    class OperationCommand : IBaseCommand
    {
        private CalcViewModel viewModel;

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested += value; }
        }

        public OperationCommand(CalcViewModel viewModel)
        {
            this.viewModel = viewModel;
        }
        public bool CanExecute(object parameter)
        {
            return viewModel.InputString.Length > 0;
        }
        public void Execute(object parameter)
        {
            viewModel.Op = parameter.ToString();
            viewModel.Op1 = Convert.ToDouble(viewModel.InputString);
            viewModel.InputString = "";
        }
        public void OnCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }

    class CalcCommand : IBaseCommand
    {
        private CalcViewModel viewModel;

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested += value; }
        }

        public CalcCommand(CalcViewModel viewModel)
        {
            this.viewModel = viewModel;
        }
        public bool CanExecute(object parameter)
        {
            return (viewModel.Op1.ToString() != string.Empty
                    && viewModel.Op2.ToString() != string.Empty);
        }
        public void Execute(object parameter)
        {
            viewModel.Op2 = Convert.ToDouble(viewModel.InputString);

            switch (viewModel.Op)
            {
                case "+": viewModel.InputString = (viewModel.Op1 + viewModel.Op2).ToString(); break;
                case "-": viewModel.InputString = (viewModel.Op1 - viewModel.Op2).ToString(); break;
                case "*": viewModel.InputString = (viewModel.Op1 * viewModel.Op2).ToString(); break;
                case "/": viewModel.InputString = (viewModel.Op1 / viewModel.Op2).ToString(); break;
            }
            viewModel.Op1 = Convert.ToDouble(viewModel.InputString);
        }
        public void OnCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }

}

n  실행결과
 066117be0ad9045c67d53e9da88866cf_1482335

댓글 없음:

댓글 쓰기