20장. WPF 계산기, MVVM, Command, 데이터바인딩이용
- C#/WPF
- 2021. 5. 17. 19:05
20장. WPF 계산기, MVVM, Command, 데이터바인딩이용
참조
목적
- WPF 계산기를 MVVM, Command, 데이터바인딩을 이용하여 만들어 봅니다.
실습
CalcCommand.cs
using System;
using System.Windows.Input;
namespace WPF18_Test
{
class Append : ICommand
{
private CalcViewModel c;
public event EventHandler CanExecuteChanged;
public Append(CalcViewModel c)
{
this.c = c;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
c.InputString += parameter;
}
}
class BackSpace : ICommand
{
private CalcViewModel c;
public BackSpace(CalcViewModel c)
{
this.c = c;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return c.DisplayText.Length > 0;
}
public void Execute(object parameter)
{
int length = c.InputString.Length - 1;
if(0 < length)
{
c.InputString = c.InputString.Substring(0, length);
}
else
{
c.InputString = c.DisplayText = string.Empty;
}
}
}
class Clear : ICommand
{
private CalcViewModel c;
public Clear(CalcViewModel c)
{
this.c = c;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested += value; }
}
public bool CanExecute(object parameter)
{
return c.DisplayText.Length > 0;
}
public void Execute(object parameter)
{
c.InputString = c.DisplayText = string.Empty;
c.Op1 = null;
}
}
class Operator : ICommand
{
private CalcViewModel c;
public Operator(CalcViewModel c)
{
this.c = c;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return 0 < c.InputString.Length;
}
public void Execute(object parameter)
{
string op = parameter.ToString();
double op1;
if(double.TryParse(c.InputString, out op1))
{
c.Op1 = op1;
c.Op = op;
c.InputString = ""; //3 그리고 + 누르면 DisplayText는 3, InputString는 Clear
}
else if(c.InputString == "" && op == "-")
{
c.InputString = "-";
}
}
}
class Calculate : ICommand
{
private CalcViewModel c;
public Calculate(CalcViewModel c)
{
this.c = c;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
double op2;
return c.Op1 != null && double.TryParse(c.InputString, out op2);
}
public void Execute(object parameter)
{
double op2 = double.Parse(c.InputString);
c.InputString = calculate(c.Op, (double)c.Op1, op2).ToString();
c.Op1 = null;
}
private static double calculate(string op, double op1, double op2)
{
switch(op)
{
case "+" : return op1 + op2;
case "-" : return op1 - op2;
case "*" : return op1 * op2;
case "/" : return op1 / op2;
}
return 0;
}
}
class CalcCommand
{
}
}
CalcViewModel.cs
using System.ComponentModel;
using System.Windows.Input;
namespace WPF18_Test
{
public class CalcViewModel : INotifyPropertyChanged
{
//아래 두 필드는 속성으로 구현되어 있다.
//출력될 문자들을 담아둘 변수
string inputString = string.Empty;
//계산기 화면의 출력 텍스트박스에 대응되는 필드
string displayText = "";
//View와 바인딩된 속성값이 바뀔때 이를 WPF에게 알리기 위한 이벤트
public event PropertyChangedEventHandler PropertyChanged;
//생성자, 명령 객체들을 초기화
//명령 객체들은 UI쪽 버튼의 Command에 바인딩되어 있다.
public CalcViewModel()
{
//이벤트 핸들러 정의
//숫자 버튼을 클릭할 때 실행
this.Append = new Append(this);
//백 스페이스 버튼을 클릭할 때 실행, 한글자 삭제
this.BackSpace = new BackSpace(this);
//출력화면 클리어
this.Clear = new Clear(this);
//+,-,*,/ 등 연산자 클릭할 때 실행
this.Operator = new Operator(this);
//"=" 버튼을 클릭할 때 실행
this.Calculate = new Calculate(this);
}
public string InputString
{
internal set
{
if (inputString != value)
{
inputString = value;
OnPropertyChanged("InputString");
if (value != "")
{
//숫자를 여러개 입력하면 계속 화면에 출력하기 위해
DisplayText = value;
}
}
}
get
{
return inputString;
}
}
/// <summary>
/// 계산기의 출력창과 바인딩된 속성
/// </summary>
public string DisplayText
{
internal set
{
if (displayText != value)
{
displayText = value;
OnPropertyChanged("DisplayText");
}
}
get
{
return displayText;
}
}
public string Op { get; set; } //Operator
public double? Op1 { get; set; } //Operand 1
public ICommand Append { protected set; get; }
public ICommand BackSpace { protected set; get; }
public ICommand Clear { protected set; get; }
public ICommand Operator { protected set; get; }
public ICommand Calculate { protected set; get; }
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MainWindow.xaml
<Window x:Class="WPF18_Test.MainWindow"
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:WPF18_Test"
mc:Ignorable="d"
Title="MainWindow" Height="400" Width="400">
<Grid Margin="10" HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<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>
<Border Grid.Row="0" Grid.ColumnSpan="2"
BorderBrush="Black" BorderThickness="3">
<TextBlock x:Name="txtInput"
FontSize="15" VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{Binding DisplayText}"/>
</Border>
<Button Grid.Row="0" Grid.Column="2"
Content="BACK"
x:Name="BtnBack"
Command="{Binding BackSpace}"/>
<Button Grid.Row="0" Grid.Column="3"
Content="Clear"
x:Name="BtnClear"
Command="{Binding Clear}"/>
<Button Grid.Row="1" Grid.Column="0"
Content="1"
x:Name="BtnOne"
Command="{Binding Append}"
CommandParameter="1"/>
<Button Grid.Row="1" Grid.Column="1"
Content="2"
x:Name="BtnTwo"
Command="{Binding Append}"
CommandParameter="2"/>
<Button Grid.Row="1" Grid.Column="2"
Content="3"
x:Name="BtnThree"
Command="{Binding Append}"
CommandParameter="3"/>
<Button Grid.Row="1" Grid.Column="3"
Content="+"
x:Name="BtnPlus"
Command="{Binding Operator}"
CommandParameter="+"/>
<Button Grid.Row="2" Grid.Column="0"
Content="4"
x:Name="BtnFour"
Command="{Binding Append}"
CommandParameter="4"/>
<Button Grid.Row="2" Grid.Column="1"
Content="5"
x:Name="BtnFive"
Command="{Binding Append}"
CommandParameter="5"/>
<Button Grid.Row="2" Grid.Column="2"
Content="6"
x:Name="BtnSix"
Command="{Binding Append}"
CommandParameter="6"/>
<Button Grid.Row="2" Grid.Column="3"
Content="-"
x:Name="BtnMinus"
Command="{Binding Operator}"
CommandParameter="-"/>
<Button Grid.Row="3" Grid.Column="0"
Content="7"
x:Name="BtnSeven"
Command="{Binding Append}"
CommandParameter="7"/>
<Button Grid.Row="3" Grid.Column="1"
Content="8"
x:Name="BtnEight"
Command="{Binding Append}"
CommandParameter="8"/>
<Button Grid.Row="3" Grid.Column="2"
Content="9"
x:Name="BtnNine"
Command="{Binding Append}"
CommandParameter="9"/>
<Button Grid.Row="3" Grid.Column="3"
Content="*"
x:Name="BtnMulitple"
Command="{Binding Operator}"
CommandParameter="*"/>
<Button Grid.Row="4" Grid.Column="0"
Content="0"
x:Name="BtnZero"
Command="{Binding Append}"
CommandParameter="0"/>
<Button Grid.Row="4" Grid.Column="1"
Content="."
x:Name="BtnDot"/>
<Button Grid.Row="4" Grid.Column="2"
Content="="
x:Name="BtnEqual"
Command="{Binding Calculate}"/>
<Button Grid.Row="4" Grid.Column="3"
Content="/"
x:Name="BtnDivision"
Command="{Binding Operator}"
CommandParameter="/"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace WPF18_Test
{
/// <summary>
/// MainWindow.xaml에 대한 상호 작용 논리
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new CalcViewModel();
}
}
}
실행결과
728x90
'C# > WPF' 카테고리의 다른 글
[WPF 문법] WPF MVVM 패턴 이용하여 오라클 DB 연동하기 (2) | 2021.09.19 |
---|---|
[WPF] GridSplitter 컨트롤 Position 값 가져오기 (0) | 2021.05.20 |
19장. WPF MVVM, ListBox의 컬렉션 정렬, 필터링, 탐색 실습(ListCollectionView) (0) | 2021.05.17 |
18장. WPF IValueConverter를 이용한 데이터바인딩, DataType이 다른 경우의 Data Binding (0) | 2021.05.15 |
14장. WPF Command패턴, 데이터바인딩 DataBinding 개요, 실습 (2) | 2021.05.14 |
이 글을 공유하기