(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={StaticResourcecalcViewModel}}" />
</Border>
<!-- 버튼의 Command 이벤트에 Command명을 지정하고 바인딩-->
<!-- ViewModel 클래스에서 DeleteCharCommand 속성이 정의되야 하고 -->
<!-- 실제 버튼이 눌러졌을 때 Command에 대한 이벤트 핸들러를 정의해야 한다.-->
<Button Content="BACK"
Command="{Binding DeleteCharCommand,Source={StaticResourcecalcViewModel}}"
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 { get; set; }
public double Op1 { get; set; }
public double Op2 { get; set; }
// 숫자 클릭
public ICommand AddCharCommand { protected set; get; }
// <- 클릭, 한글자씩 삭제
public ICommand DeleteCharCommand { protected set; get; }
// C 클릭시 DisplayText 전체를 지운다.
public ICommand ClearCommand { protected set; get; }
// +, -, *, / 클릭
public ICommand OperationCommand { protected set; get; }
// = 클릭
public ICommand CalcCommand { protected set; get; }
protected void OnPropertyChanged(string propertyName)
{
//이벤트를 발생시킨다.
if (PropertyChanged != null)
PropertyChanged(this, new 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 실행결과