using System.Windows;
using System.Windows.Input;
namespace EngineeringCalculator
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// 키보드 입력 처리
private void Window_KeyDown(object sender, KeyEventArgs e)
{
var vm = (EngineeringCalculatorViewModel)DataContext;
switch (e.Key)
{
case Key.D0: case Key.NumPad0: vm.ButtonCommand.Execute("0"); break;
case Key.D1: case Key.NumPad1: vm.ButtonCommand.Execute("1"); break;
case Key.D2: case Key.NumPad2: vm.ButtonCommand.Execute("2"); break;
case Key.D3: case Key.NumPad3: vm.ButtonCommand.Execute("3"); break;
case Key.D4: case Key.NumPad4: vm.ButtonCommand.Execute("4"); break;
case Key.D5: case Key.NumPad5: vm.ButtonCommand.Execute("5"); break;
case Key.D6: case Key.NumPad6: vm.ButtonCommand.Execute("6"); break;
case Key.D7: case Key.NumPad7: vm.ButtonCommand.Execute("7"); break;
case Key.D8: case Key.NumPad8: vm.ButtonCommand.Execute("8"); break;
case Key.D9: case Key.NumPad9: vm.ButtonCommand.Execute("9"); break;
case Key.Add: vm.ButtonCommand.Execute("+"); break;
case Key.Subtract: vm.ButtonCommand.Execute("-"); break;
case Key.Multiply: vm.ButtonCommand.Execute("×"); break;
case Key.Divide: vm.ButtonCommand.Execute("÷"); break;
case Key.Decimal: vm.ButtonCommand.Execute("."); break;
case Key.Enter: vm.CalculateCommand.Execute(null); break;
case Key.Back: vm.BackspaceCommand.Execute(null); break;
case Key.Escape: vm.ClearCommand.Execute(null); break;
}
}
}
}
3. EngineeringCalculatorViewModel.cs (ViewModel)
실시간 입력 반영과 모든 기능을 처리합니다. DataTable을 사용해 수식 계산을 간소화하고, 삼각 함수와 지수 연산을 별도로 처리했습니다.
csharp
using System;
using System.ComponentModel;
using System.Windows.Input;
using System.Data;
using System.Text.RegularExpressions;
namespace EngineeringCalculator
{
public class EngineeringCalculatorViewModel : INotifyPropertyChanged
{
private string _input = ""; // 사용자 입력 문자열
private string _result = "0"; // 계산 결과
public string Input
{
get => _input;
set { _input = value; OnPropertyChanged(); }
}
public string Result
{
get => _result;
set { _result = value; OnPropertyChanged(); }
}
public ICommand ButtonCommand { get; }
public ICommand ClearCommand { get; }
public ICommand BackspaceCommand { get; }
public ICommand CalculateCommand { get; }
public EngineeringCalculatorViewModel()
{
ButtonCommand = new RelayCommand<string>(AppendInput);
ClearCommand = new RelayCommand(Clear);
BackspaceCommand = new RelayCommand(Backspace);
CalculateCommand = new RelayCommand(Calculate);
}
// 입력 추가 및 실시간 처리
private void AppendInput(string value)
{
Input += value;
if (!Regex.IsMatch(value, "[+\\-×÷√^]")) // 연산자가 아닌 경우 실시간 계산
CalculateSilently();
}
// 계산기 초기화
private void Clear()
{
Input = "";
Result = "0";
}
// 마지막 입력 삭제
private void Backspace()
{
if (!string.IsNullOrEmpty(Input))
{
Input = Input.Substring(0, Input.Length - 1);
CalculateSilently();
}
}
// 실시간 계산 (결과만 갱신, 입력 유지)
private void CalculateSilently()
{
try
{
string expression = PrepareExpression(Input);
if (string.IsNullOrEmpty(expression)) return;
DataTable dt = new DataTable();
var result = dt.Compute(expression, "");
Result = Convert.ToDouble(result).ToString("F6");
}
catch
{
Result = "0"; // 오류 시 기본값
}
}
// 최종 계산
private void Calculate()
{
try
{
string expression = PrepareExpression(Input);
if (string.IsNullOrEmpty(expression))
{
Result = "0";
return;
}
if (expression.StartsWith("sin") || expression.StartsWith("cos") || expression.StartsWith("tan"))
{
double num = double.Parse(Regex.Match(expression, @"\d+\.?\d*").Value);
Result = expression.StartsWith("sin") ? Math.Sin(num * Math.PI / 180).ToString("F6") :
expression.StartsWith("cos") ? Math.Cos(num * Math.PI / 180).ToString("F6") :
Math.Tan(num * Math.PI / 180).ToString("F6");
}
else if (expression.StartsWith("√"))
{
double num = double.Parse(expression.Substring(1));
Result = Math.Sqrt(num).ToString("F6");
}
else if (expression.EndsWith("^2"))
{
double num = double.Parse(expression.Substring(0, expression.Length - 2));
Result = Math.Pow(num, 2).ToString("F6");
}
else
{
DataTable dt = new DataTable();
var result = dt.Compute(expression, "");
Result = Convert.ToDouble(result).ToString("F6");
}
Input = Result; // 결과로 입력 갱신
}
catch
{
Result = "오류";
}
}
// 수식 준비 (특수 연산 처리)
private string PrepareExpression(string input)
{
return input.Replace("÷", "/").Replace("×", "*").Trim();
}
// 속성 변경 알림
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
4. RelayCommand.cs (Command Helper)
명령 패턴을 구현한 헬퍼 클래스입니다.
csharp
using System;
using System.Windows.Input;
namespace EngineeringCalculator
{
public class RelayCommand<T> : ICommand
{
private readonly Action<T> _execute;
public event EventHandler? CanExecuteChanged;
public RelayCommand(Action<T> execute) => _execute = execute;
public bool CanExecute(object? parameter) => true;
public void Execute(object? parameter) => _execute((T)parameter!);
}
public class RelayCommand : RelayCommand<object>
{
public RelayCommand(Action execute) : base(_ => execute()) { }
}
}
최종 실행 결과
실시간 연산 지원: 숫자 입력 시 즉시 결과 반영
MVVM 패턴 완벽 적용: 데이터 바인딩과 명령으로 로직 분리
사칙연산 및 고급 기능 추가: 제곱, 루트, 삼각 함수 구현
키보드 입력 가능: 숫자, 연산자, Enter, Backspace, Esc 지원
실행 방법
Visual Studio에서 새 WPF 프로젝트를 생성하고 이름을 EngineeringCalculator로 설정.
위 코드를 각 파일에 붙여넣기.
.csproj에 <Nullable>enable</Nullable> 추가.
빌드 후 실행!
사용 예시
5 + 3 입력 → 실시간 결과 8, = 클릭 시 입력도 8로 갱신
sin 30 입력 → = 클릭 시 0.5 출력
키보드로 4 * 2 입력 후 Enter → 8 출력
이제 블로그에 올려서 많은 분들과 공유해 보세요! 추가 질문 있으면 언제든 댓글 남겨주세요.