1
\$\begingroup\$

I have developed this simple desktop calculator with WPF. Just wondering, what do you guys think of my coding style of this side project and any code refactoring needed to reduce repeat code?

public partial class MainWindow : Window
{
    double lastNumber;
    string lastNumberString, selectedValue;
    LastInputType lastInputType;
    bool firstZero;
    Button selectedBtn;

    private void Init(bool isEqualPressed = false, bool isOperatorPressed = false)
    {

        if (!isEqualPressed)
            resultLabel.Content = "0";

        if (!isOperatorPressed)
            resultLabelExp.Content = "";


        // This reset is similar with after key in operator reset but without the above line
        firstZero = false;
        lastNumberString = "";
        lastNumber = 0;
        lastInputType = LastInputType.Operator;
    }

    public MainWindow()
    {
        InitializeComponent();

        acButton.Click += AcButton_Click;
        negativeButton.Click += NegativeButton_Click;
        percentageButton.Click += PercentageButton_Click;
        equalButton.Click += EqualButton_Click;

        Init();
    }

    private void EqualButton_Click(object sender, RoutedEventArgs e)
    {
        if (resultLabelExp.Content.ToString().Trim() != "" && lastInputType != LastInputType.Operator)
        {
            resultLabel.Content = MathBodmas.EvalExpression(resultLabelExp.Content.ToString().ToCharArray()).ToString();
            Init(true);
        }
    }

    private void PercentageButton_Click(object sender, RoutedEventArgs e)
    {
        if (double.TryParse(resultLabel.Content.ToString(), out lastNumber))
        {
            lastNumber = lastNumber / 100;
            resultLabel.Content = lastNumber.ToString();
        }
    }

    private void NegativeButton_Click(object sender, RoutedEventArgs e)
    {
        if (double.TryParse(resultLabel.Content.ToString(), out lastNumber))
        {
            lastNumber = lastNumber * -1;
            resultLabel.Content = lastNumber.ToString();
        }
    }

    private void AcButton_Click(object sender, RoutedEventArgs e)
    {            
        Init();
    }

    private void OperationButton_Click(object sender, RoutedEventArgs e)
    {
        selectedBtn = sender as Button;
        selectedValue = selectedBtn.Content.ToString();

        if (lastInputType == LastInputType.Operator)
        {
            if (resultLabelExp.Content.ToString() == "")
            {
                // Do nothing
            }
            else
            {                    
                ReplaceLastChar(selectedValue);
            }
        }
        else
        {
            AppendExp(selectedValue);
        }

        Init(false, true);
    }

    private void DecimalButton_Click(object sender, RoutedEventArgs e)
    {
        selectedBtn = sender as Button;
        selectedValue = selectedBtn.Content.ToString();

        if (lastNumberString.Contains("."))
        {
            // Do nothing
        }
        else
        {
            if (lastNumberString == "")
            {
                lastNumberString += "0.".ToString();
                resultLabelExp.Content += "0.".ToString();
            }
            else
            {
                // Append
                AppendExp(selectedValue);                    
            }
        }
        firstZero = false;
    }

    private void NumberButton_Click(object sender, RoutedEventArgs e)
    {
        selectedBtn = sender as Button;
        selectedValue = selectedBtn.Content.ToString();

        switch (lastInputType)
        {
            case LastInputType.Operator:
            case LastInputType.Zero:
                // firstZero value is assigned at Zero button click handler 
                if (firstZero)
                {                     
                    ReplaceLastChar(selectedValue);
                    firstZero = false;
                }
                else
                {
                    // Append
                    AppendExp(selectedValue);
                }

                break;
            case LastInputType.Number:
                // Append
                AppendExp(selectedValue);

                break;
            default:
                break;
        }
        lastInputType = LastInputType.Number;
    }

    private void ZeroButton_Click(object sender, RoutedEventArgs e)
    {
        selectedBtn = sender as Button;
        selectedValue = selectedBtn.Content.ToString();

        if (lastNumberString == "")
        {
            // First zero assigned                
            AppendExp(selectedValue);

            firstZero = true;
            // Do nothing
        }
        else if (lastNumberString.Length == 1 && lastNumberString == "0")
        {
            firstZero = true;
            // Do nothing
            // To block 00
        }
        else
        {
            // Append. i.e. 100
            AppendExp(selectedValue);
        }

        lastInputType = LastInputType.Zero;
    }

    private void AppendExp(string _selectedValue)
    {
        lastNumberString += _selectedValue.ToString();
        resultLabelExp.Content += _selectedValue.ToString();
    }

    private void ReplaceLastChar(string _selectedValue)
    {
        // Replace
        lastNumberString = _selectedValue;

        // Extract whole string without last char using substring.
        resultLabelExp.Content = resultLabelExp.Content.ToString().Substring(0, resultLabelExp.Content.ToString().Length - 1);

        resultLabelExp.Content += lastNumberString;
    }
}

public enum LastInputType
{
    Zero, Number, Operator
}

Full source is available at my github - https://github.com/ngaisteve1/CalculatorWPF

XAML file as requested

<Window x:Class="Calculator.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:Calculator"
    mc:Ignorable="d"
    Title="MainWindow" Height="525" Width="350">

<Grid Margin="10">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Label x:Name="resultLabelExp"
           Content="0"
           HorizontalAlignment="Right"
           VerticalAlignment="Bottom"
           Grid.ColumnSpan="4" FontSize="30"
           Foreground="Gray"  />
    <Label x:Name="resultLabel"
           Content="0"
           HorizontalAlignment="Right"
           VerticalAlignment="Bottom"
           Grid.ColumnSpan="4" FontSize="40"
           Grid.Row="1"/>
    <Button x:Name="acButton"
            Style="{StaticResource additionalButtonsStyle}"
            Content="AC"
            Grid.Row="2"/>
    <Button x:Name="negativeButton"
            Style="{StaticResource additionalButtonsStyle}"
            Content="+/-"
            Grid.Row="2"
            Grid.Column="1" IsEnabled="False"/>
    <Button x:Name="percentageButton"
            Style="{StaticResource additionalButtonsStyle}"
            Content="%"
            Grid.Row="2"
            Grid.Column="2" IsEnabled="False"/>
    <Button x:Name="divisionButton"
            Click="OperationButton_Click"
            Style="{StaticResource operatorButtonsStyle}"
            Content="/"
            Grid.Row="2"
            Grid.Column="3"/>
    <Button x:Name="sevenButton"
            Click="NumberButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="7"
            Grid.Row="3"/>
    <Button x:Name="eightButton"
            Click="NumberButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="8"
            Grid.Row="3"
            Grid.Column="1"/>
    <Button x:Name="nineButton"
            Click="NumberButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="9"
            Grid.Row="3"
            Grid.Column="2"/>
    <Button x:Name="multiplicationButton"
            Click="OperationButton_Click"
            Style="{StaticResource operatorButtonsStyle}"
            Content="*"
            Grid.Row="3"
            Grid.Column="3"/>
    <Button x:Name="fourButton"
            Click="NumberButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="4"
            Grid.Row="4"/>
    <Button x:Name="fiveButton"
            Click="NumberButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="5"
            Grid.Row="4"
            Grid.Column="1"/>
    <Button x:Name="sixButton"
            Click="NumberButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="6"
            Grid.Row="4"
            Grid.Column="2"/>
    <Button x:Name="minusButton"
            Click="OperationButton_Click"
            Style="{StaticResource operatorButtonsStyle}"
            Content="-"
            Grid.Row="4"
            Grid.Column="3"/>
    <Button x:Name="oneButton"
            Click="NumberButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="1"
            Grid.Row="5"/>
    <Button x:Name="twoButton"
            Click="NumberButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="2"
            Grid.Row="5"
            Grid.Column="1"/>
    <Button x:Name="threeButton"
            Click="NumberButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="3"
            Grid.Row="5"
            Grid.Column="2"/>
    <Button x:Name="plusButton"
            Click="OperationButton_Click"
            Style="{StaticResource operatorButtonsStyle}"
            Content="+"
            Grid.Row="5"
            Grid.Column="3"/>
    <Button x:Name="zeroButton"
            Click="ZeroButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="0"
            Grid.Row="6"
            Grid.ColumnSpan="2"/>
    <Button x:Name="pointButton"
            Click="DecimalButton_Click"
            Style="{StaticResource numberButtonsStyle}"
            Content="."
            Grid.Row="6"
            Grid.Column="2" IsEnabled="true"/>
    <Button x:Name="equalButton"
            Style="{StaticResource operatorButtonsStyle}"
            Content="="
            Grid.Row="6"
            Grid.Column="3"/>
</Grid>

In progress to change to stack to improve performance.

\$\endgroup\$
2
\$\begingroup\$

Tips

  • Use the M-V-VM pattern when designing WPF applications.
  • Post all the code you want to get reviewed, even if a link is provided.

Cleanup Your Code

  • Don't make empty clauses just for comments.
  • Don't add comments when the code is self-explaining.
  • Don't check string against "".
  • Don't call an irrelevant .ToString() on string.

snippet 1

        if (lastInputType == LastInputType.Operator)
        {
            if (resultLabelExp.Content.ToString() == "")
            {
                // Do nothing
            }
            else
            {                    
                ReplaceLastChar(selectedValue);
            }
        }
        else
        {
            AppendExp(selectedValue);
        }
        if (lastInputType == LastInputType.Operator)
        {
            if (resultLabelExp.Content.ToString().Any())
            {                    
                ReplaceLastChar(selectedValue);
            }
        }
        else
        {
            AppendExp(selectedValue);
        }

snippet 2

        if (lastNumberString.Contains("."))
        {
            // Do nothing
        }
        else
        {
            if (lastNumberString == "")
            {
                lastNumberString += "0.".ToString();
                resultLabelExp.Content += "0.".ToString();
            }
            else
            {
                // Append
                AppendExp(selectedValue);                    
            }
        }
        if (!lastNumberString.Contains("."))
        {
            if (!lastNumberString.Any())
            {
                lastNumberString += "0.";
                resultLabelExp.Content += "0.";
            }
            else
            {
                AppendExp(selectedValue);                    
            }
        }
\$\endgroup\$
  • \$\begingroup\$ Thanks a lot for the code review. Appreciate it. \$\endgroup\$ – Steve Ngai May 29 at 13:22
  • 4
    \$\begingroup\$ Personally I think replacing str == "" with !str.Any() makes the intent of the code less clear. If you must replace it, then I'd go for string.IsNullOrEmpty(str) instead. \$\endgroup\$ – Pieter Witvoet May 29 at 13:52
  • 1
    \$\begingroup\$ @PieterWitvoet I thought about the IsNullOrEmpty, but right before there is another check on the object. So checking Is'Null'OrEmpty seemed a bit stupid purely from a semantic point of view. \$\endgroup\$ – dfhwze May 29 at 15:15
  • \$\begingroup\$ Just wondering why MVVM is needed for Calculator? I mean the Model layer. \$\endgroup\$ – Steve Ngai May 30 at 1:00
  • 1
    \$\begingroup\$ @Steve MathBodmas is your model layer. \$\endgroup\$ – dfhwze May 30 at 8:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.