{"id":15593215,"url":"https://github.com/igorocampos/wpfcalc","last_synced_at":"2025-04-12T10:32:27.554Z","repository":{"id":206330846,"uuid":"201830898","full_name":"igorocampos/WPFCalc","owner":"igorocampos","description":"A WPF simple calculator ","archived":false,"fork":false,"pushed_at":"2022-10-07T20:57:33.000Z","size":62,"stargazers_count":1,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-22T23:30:57.220Z","etag":null,"topics":["hacktoberfest","hacktoberfest-accepted","hacktoberfest2022"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/igorocampos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-08-12T00:35:18.000Z","updated_at":"2023-10-07T07:39:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"1e92db62-df5a-4e64-abb2-ff75c88abe14","html_url":"https://github.com/igorocampos/WPFCalc","commit_stats":null,"previous_names":["igorocampos/wpfcalc"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorocampos%2FWPFCalc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorocampos%2FWPFCalc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorocampos%2FWPFCalc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorocampos%2FWPFCalc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igorocampos","download_url":"https://codeload.github.com/igorocampos/WPFCalc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":232662470,"owners_count":18557469,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","hacktoberfest-accepted","hacktoberfest2022"],"created_at":"2024-10-03T00:05:31.220Z","updated_at":"2025-01-06T16:10:57.117Z","avatar_url":"https://github.com/igorocampos.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WPFCalc\n[![Build Status](https://dev.azure.com/igorocampos/PersonalProjects/_apis/build/status/igorocampos.WPFCalc?branchName=master)](https://dev.azure.com/igorocampos/PersonalProjects/_build?definitionId=3)\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Figorocampos%2FWPFCalc.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Figorocampos%2FWPFCalc?ref=badge_shield)\n\nThis is a simple calculator WPF project that used Class Interface on the operations. It is more of a tutorial for those starting with WPF, especially those who are migrating from WinForms.  \n\n![](WPFCalc.png)\n\n# What will you need\nThis project was initially made using\n- Visual Studio 2015\n- .NET Framework 4.5.2\n\nBut it was updated to \n- Visual Studio 2022\n- .NET 6.0\n\nBasically only the target framework was updated, so if you really need, you can downgrade it back to your version instead of using the latest. If you wish to downgrade it back to .NET Framework, be sure to see [this commit](https://github.com/igorocampos/WPFCalc/commit/5b3c95390b9a22fadd1df1f0afa36e367011118e)\n\n# Getting started\nFirst of all open Visual Studio and Create a new project and select New WPF Application.\nThere should be now inside your project a file named `MainWindow.xaml`. This file is the WPF version of WinForms `designer` file. It is a markup file, where we can change some attributes in order to set the design of the ~~form~~ window and its components as we want.\n\nFor example in the opening tag of `Window`, we could change the attribute `Title` to `WPF Calculator`, set the `Width` and `Height` and even set the Window startup location.\n\n```xaml\nTitle=\"WPF Calculator\" \nHeight=\"400\" \nWidth=\"280\" \nWindowStartupLocation=\"CenterScreen\"\n```\n\nAlternatively, you can use Visual Studio to change the `XAML` file for you, just select the `Window` tag and press `F4`, or your personal shortcut, to open `Properties window`. There you will have all the properties of the `Window`.\n\nThese properties can also be changed by code after the `InitializeComponent` method call. This part of the code will be in the constructor of `MainWindow` class, you can reach it opening the file named `MainWindow.xaml.cs`, or usually just pressing the shortcut `F7` while having`MainWindow.xaml` opened on the screen.\n\n# Let's get to it\nIf you come from `WinForms` type of design you would rather insert a bunch of buttons inside this `Window` thing, setting their position and size. However WPF would accept it, Here we will use more of a responsive way, in other words, when our calculator window gets stretched we want all buttons to follow it. This is a little bit different than `Anchoring` in `WinForms` since we want all buttons to stretch equally and without overlaying each other.\n\n## Grid\nThe component that we need to get it done is called `Grid` and we can insert it as easy as typing `\u003cGrid\u003e\u003c/Grid\u003e` inside the `Window` tag.\n\nAfter that, we want to set the Rows and Columns definitions, as below:\n```xaml\n\u003cGrid\u003e\n    \u003cGrid.RowDefinitions\u003e\n        \u003cRowDefinition Height=\"40\"/\u003e\n        \u003cRowDefinition Height=\"*\"/\u003e\n        \u003cRowDefinition Height=\"*\"/\u003e\n        \u003cRowDefinition Height=\"*\"/\u003e\n        \u003cRowDefinition Height=\"*\"/\u003e\n        \u003cRowDefinition Height=\"*\"/\u003e\n    \u003c/Grid.RowDefinitions\u003e\n    \u003cGrid.ColumnDefinitions\u003e\n        \u003cColumnDefinition Width=\"*\"/\u003e\n        \u003cColumnDefinition Width=\"*\"/\u003e\n        \u003cColumnDefinition Width=\"*\"/\u003e\n        \u003cColumnDefinition Width=\"*\"/\u003e\n    \u003c/Grid.ColumnDefinitions\u003e\n\u003c/Grid\u003e\n```\nHere we said that we want 6 rows and 4 columns in our grid, and all columns should have the same width dividing the total width of the Grid equally. Same for the rows height, except the first one, which will have a fixed height of `40`. We will use this row for our Textbox.\n\nJust like the `Window`, this `Grid` component properties can be changed by Visual Studio in the Properties window.\n\n## Textbox\nOk, now we can insert all our visual components inside this grid. First, let's add a `Textbox` that will work as our calculator's display.\n\n```xaml\n\u003cTextBox x:Name=\"txtInput\" Grid.Row=\"0\" Grid.ColumnSpan=\"4\" Margin=\"2\" TextAlignment=\"Right\" Text=\"0\" FontSize=\"20\" IsReadOnly=\"True\"/\u003e\n```\n In this we are saying that:\n- Our `TextBox` should be named `txtInput`\n- Its row position should be the first one (position `0`)\n- It should Span for 4 columns\n- It should have a margin of `2` in every direction\n- Its text aligment should be to the right\n- Its default text should be `\"0\"`\n- The text font size should be `20`\n- Finally it should be a read-only Textbox.\n\nAgain all this could also be set in the Properties window, but for me editing directly the XAML is so much easier!\n\n## Number buttons\nNow, pretty much the same for the number buttons:\n```xaml\n\u003cButton x:Name=\"btn7\" Content=\"7\" Grid.Row=\"2\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btn8\" Content=\"8\" Grid.Row=\"2\" Grid.Column=\"1\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btn9\" Content=\"9\" Grid.Row=\"2\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btn4\" Content=\"4\" Grid.Row=\"3\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btn5\" Content=\"5\" Grid.Row=\"3\" Grid.Column=\"1\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btn6\" Content=\"6\" Grid.Row=\"3\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btn1\" Content=\"1\" Grid.Row=\"4\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btn2\" Content=\"2\" Grid.Row=\"4\" Grid.Column=\"1\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btn3\" Content=\"3\" Grid.Row=\"4\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btn0\" Content=\"0\" Grid.Row=\"5\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" /\u003e\n```\nThere is no `Text` property in a WPF Button, instead we use its `Content` property.\n\n## Other buttons\nWe will finish our design by adding the remaining buttons:\n```xaml\n\u003cButton x:Name=\"btnPoint\" Content=\".\" Grid.Row=\"5\" Grid.Column=\"1\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btnDivision\" Content=\"/\" Grid.Row=\"1\" Grid.Column=\"3\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btnMultiplication\" Content=\"*\" Grid.Row=\"2\" Grid.Column=\"3\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btnSum\" Content=\"+\" Grid.Row=\"3\" Grid.Column=\"4\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btnSubtraction\" Content=\"-\" Grid.Row=\"4\" Grid.Column=\"3\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btnEquals\" Content=\"=\" Grid.Row=\"5\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" Grid.ColumnSpan=\"2\" /\u003e\n\u003cButton x:Name=\"btnBack\" Content=\"←\" Grid.Row=\"1\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btnClearEntry\" Content=\"CE\" Grid.Row=\"1\" Grid.Column=\"1\" FontSize=\"20\" Margin=\"2\" /\u003e\n\u003cButton x:Name=\"btnClearAll\" Content=\"C\" Grid.Row=\"1\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" /\u003e\n```\n- `C` will be the Clear All button\n- `CE` will be Clear Entry, wich only clears what you are currently typing\n- `←` will be the Backspace button that erases the last typed digit\n- `.` will be the Decimal Separator, however we shall talk about this later\n\n## Events\nAll set with the design, we need to set some click events for the buttons.\nThis is very simple, just add the `Click` attribute in the Button tag like this:\n```xaml\n\u003cButton x:Name=\"btn7\" Content=\"7\" Grid.Row=\"2\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" Click=\"\"/\u003e\n```\nVisual Studio will open a suggestion window where the first option is `\u003cNew Event Handler\u003e` if you select it, it will automatically create a method for this event in the `MainWindow.xaml.cs` file, like this:\n```cs\nprivate void btnPoint_Click(object sender, RoutedEventArgs e)\n{\n\n}\n```\n\nBut we don't need an event handler for every button. Some behaviors are pretty much the same.  Let's create the `regularButtonClick` method and link it with all number buttons:\n\n```cs\nprivate void regularButtonClick(object sender, RoutedEventArgs e)\n{\n    //Prevent 0 from appearing on the left of new numbers\n    if (txtInput.Text == \"0\")\n        txtInput.Text = \"\";\n\n    txtInput.Text = $\"{txtInput.Text}{((Button)sender).Content}\";\n}\n```\n```xaml\n\u003cButton x:Name=\"btn7\" Content=\"7\" Grid.Row=\"2\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n\u003cButton x:Name=\"btn8\" Content=\"8\" Grid.Row=\"2\" Grid.Column=\"1\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n\u003cButton x:Name=\"btn9\" Content=\"9\" Grid.Row=\"2\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n\u003cButton x:Name=\"btn4\" Content=\"4\" Grid.Row=\"3\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n\u003cButton x:Name=\"btn5\" Content=\"5\" Grid.Row=\"3\" Grid.Column=\"1\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n\u003cButton x:Name=\"btn6\" Content=\"6\" Grid.Row=\"3\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n\u003cButton x:Name=\"btn1\" Content=\"1\" Grid.Row=\"4\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n\u003cButton x:Name=\"btn2\" Content=\"2\" Grid.Row=\"4\" Grid.Column=\"1\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n\u003cButton x:Name=\"btn3\" Content=\"3\" Grid.Row=\"4\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n\u003cButton x:Name=\"btn0\" Content=\"0\" Grid.Row=\"5\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" Click=\"regularButtonClick\"/\u003e\n```\nThis will pretty much, add the clicked digit to the ending of the display, except when the display has a single `0`.\n\nThe decimal separator has a little bit different behavior: \n- Only one decimal separator is allowed in numbers\n\nAlso, your decimal separator symbol can be different from mine! We should get wich symbol to use from the OS. Here is how we can do it:\n- Create a get only `string` property called `DecimalSeparator`\n- In the class constructor let's change the Button content to display the correct symbol\n\n```cs\nstring DecimalSeparator =\u003e CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator;\n\npublic MainWindow()\n{\n    InitializeComponent();\n    btnPoint.Content = DecimalSeparator;\n}\n```\n\nNow we can just create an Event Handler for our decimal separator button\n\n```cs\nprivate void btnPoint_Click(object sender, RoutedEventArgs e)\n{\n    if (txtInput.Text.Contains(this.DecimalSeparator))\n        return;\n\n    regularButtonClick(sender, e);\n}\n```\n\nNow a handler for `Backspace`, `Clear Entry` and `Clear All` buttons\n```cs\nprivate void btnBack_Click(object sender, RoutedEventArgs e)\n{\n    //Prevent from clearing zero\n    if (txtInput.Text == \"0\")\n        return;\n\n    txtInput.Text = txtInput.Text.Substring(0, txtInput.Text.Length - 1);\n    if (txtInput.Text == \"\")\n        txtInput.Text = \"0\";\n}\nprivate void btnClearEntry_Click(object sender, RoutedEventArgs e)\n   =\u003e txtInput.Text = \"0\";\n\nprivate void btnClearAll_Click(object sender, RoutedEventArgs e)\n{\n    //ToDo: This should also clear the current operation and saved first value\n    txtInput.Text = \"0\";\n}\n```\n```xaml\n\u003cButton x:Name=\"btnBack\" Content=\"←\" Grid.Row=\"1\" Grid.Column=\"0\" FontSize=\"20\" Margin=\"2\" Click=\"btnBack_Click\"/\u003e\n\u003cButton x:Name=\"btnClearEntry\" Content=\"CE\" Grid.Row=\"1\" Grid.Column=\"1\" FontSize=\"20\" Margin=\"2\" Click=\"btnClearEntry_Click\"/\u003e\n\u003cButton x:Name=\"btnClearAll\" Content=\"C\" Grid.Row=\"1\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" Click=\"btnClearAll_Click\"/\u003e\n```\n\nThe remaining buttons will get their events later on. First, we need to talk about the operations.\n\n## Operations\nBasically, all 4 operations will need 2 numbers and will return a decimal result.\nIn order to save some code lines and getting things more maintainable, we will use here a Class Interface. A very simple one indeed:\n\n```cs\npublic interface IOperation\n{\n    decimal DoOperation(decimal val1, decimal val2);\n}\n```\n\nAnd now we will have 4 classes that implement this interface\n\n```cs\npublic class Sum : IOperation\n{\n    public decimal DoOperation(decimal val1, decimal val2) =\u003e val1 + val2;\n}\n\npublic class Subtraction : IOperation\n{\n    public decimal DoOperation(decimal val1, decimal val2) =\u003e val1 - val2;\n}\n\npublic class Division : IOperation\n{\n    public decimal DoOperation(decimal val1, decimal val2) =\u003e val1 / val2;\n}\npublic class Multiplication : IOperation\n{\n    public decimal DoOperation(decimal val1, decimal val2) =\u003e val1 * val2;\n}\n```\n\nI just coded all this in the same `Operations.cs` file, but if you prefer, you can get a different file for every class and another one for the interface.\n\nNow we can have some polymorphism with the operation buttons.\n\n### Current Operation\nIn order to know which Operation to do, we will need an `IOperation` variable, which we will call `CurrentOperation`.\n```cs\nIOperation CurrentOperation;\n```\n\n### First Value\nWe will also need to save somewhere the first value typed right before the Operation Button Click. \nFor this, let's create a property called `FirstValue`.\n```cs\ndecimal FirstValue { get; set; }\n```\n\n### Linking the Operations to their class\nNow we need to link all 4 operation buttons to its related class. We can do this using the `Tag` property of the buttons. This property has the `object` type, which can very well be our implementation of `IOperation`.\n\n```cs\npublic MainWindow()\n{\n    InitializeComponent();\n    btnPoint.Content = DecimalSeparator;\n    btnSum.Tag = new Sum();\n    btnSubtraction.Tag = new Subtraction();\n    btnDivision.Tag = new Division();\n    btnMultiplication.Tag = new Multiplication();\n}\n```\n\n### Event Handler\nAnd finally using all this together we will have something like this:\n```cs\nprivate void operationButton_Click(object sender, RoutedEventArgs e)\n{\n    //if current operation is not null then we already have the FirstValue\n    if (CurrentOperation == null)\n        FirstValue = Convert.ToDecimal(txtInput.Text);\n\n    CurrentOperation = (IOperation)((Button)sender).Tag;\n    txtInput.Text = \"\";\n}\n```\n```xaml\n\u003cButton x:Name=\"btnDivision\" Content=\"/\" Grid.Row=\"1\" Grid.Column=\"3\" FontSize=\"20\" Margin=\"2\" Click=\"operationButton_Click\"/\u003e\n\u003cButton x:Name=\"btnMultiplication\" Content=\"*\" Grid.Row=\"2\" Grid.Column=\"3\" FontSize=\"20\" Margin=\"2\" Click=\"operationButton_Click\"/\u003e\n\u003cButton x:Name=\"btnSum\" Content=\"+\" Grid.Row=\"3\" Grid.Column=\"4\" FontSize=\"20\" Margin=\"2\" Click=\"operationButton_Click\"/\u003e\n\u003cButton x:Name=\"btnSubtraction\" Content=\"-\" Grid.Row=\"4\" Grid.Column=\"3\" FontSize=\"20\" Margin=\"2\" Click=\"operationButton_Click\"/\u003e\n```\n\n### Clear All\nNow we can change the Clear All button click to \n```cs\nprivate void btnClearAll_Click(object sender, RoutedEventArgs e)\n{\n    FirstValue = 0;\n    CurrentOperation = null;\n    txtInput.Text = \"0\";\n}\n```\n\n### Equals Button\nFor last we can code our Equals button click\n```cs\nprivate void btnEquals_Click(object sender, RoutedEventArgs e)\n{\n    if (CurrentOperation == null)\n        return;\n\n    if (txtInput.Text == \"\")\n        return;\n\n    decimal val2 = Convert.ToDecimal(txtInput.Text);\n    txtInput.Text = CurrentOperation.DoOperation(FirstValue, val2).ToString();\n}\n```\n```xaml\n\u003cButton x:Name=\"btnEquals\" Content=\"=\" Grid.Row=\"5\" Grid.Column=\"2\" FontSize=\"20\" Margin=\"2\" Grid.ColumnSpan=\"2\" Click=\"btnEquals_Click\"/\u003e\n```\n\n#### Multiple Equals button click\nPerhaps you are familiar with the behavior of pressing multiple times the button equals in the calculator. The result is the last operation repeated again and again.\nIn order to get this, we will also need to save somehow the last typed value instead of using what appears in the display.\nLet's create a nullable decimal property for this.\n```cs\ndecimal? SecondValue { get; set; }\n```\n\nWhen SecondValue is null then button equals was not pressed yet.\nThus, we can reset it on the operation buttons click event\n```cs\nprivate void operationButton_Click(object sender, RoutedEventArgs e)\n{\n    //if current operation is not null then we already have the FirstValue\n    if (CurrentOperation == null)\n        FirstValue = Convert.ToDecimal(txtInput.Text);\n\n    CurrentOperation = (IOperation)((Button)sender).Tag;\n    SecondValue = null;\n    txtInput.Text = \"\";\n}\n```\nOur event handler will now look like this:\n```cs\nprivate void btnEquals_Click(object sender, RoutedEventArgs e)\n{\n    if (CurrentOperation == null)\n        return;\n\n    if (txtInput.Text == \"\")\n        return;\n\n    //SecondValue is used for multiple clicks on Equals bringing the newest result of last operation\n    decimal val2 = SecondValue ?? Convert.ToDecimal(txtInput.Text);\n    txtInput.Text = (FirstValue = CurrentOperation.DoOperation(FirstValue, (decimal)(SecondValue = val2))).ToString();\n}\n```\nThis last line is a little bit tricky. It is actually the simplification of \n```cs\nvar result = CurrentOperation.DoOperation(FirstValue, val2);\ntxtInput.Text = result.ToString();\nFirstValue = result;\nSecondValue = val2;\n```\n# Improving UX\nWe can do better allowing not only clicks on the buttons but also keyboard typing.\nHere we will have one more difference from `WinForms`. There is no `KeyPress` event that gives the pressed `KeyChar`.\n\nYou could try and use the `KeyDown` event, along with the `e.Key` in a switch, but it gets tricky with the `*` and `/` because they are different in some keyboards...\n\nSo instead we should use the `PreviewTextInput` event. Our xaml will be changed to:\n```xaml\n\u003cWindow x:Class=\"WPFCalc.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:WPFCalc\"\n        mc:Ignorable=\"d\"\n        Title=\"WPF Calculator\" \n        Height=\"400\" \n        Width=\"280\" \n        PreviewTextInput=\"Window_PreviewTextInput\" \n        WindowStartupLocation=\"CenterScreen\"\u003e\n```\nAnd we will have this method on `MainWindow.xaml.cs`\n```cs\nprivate void Window_PreviewTextInput(object sender, TextCompositionEventArgs e)\n{\n   \n}\n```\n\n## SendToInput()\nInstead of firing `regularButtonClick` we can create a method called `SendToInput` since we will already know which string to send, and will not need to check the button's `Content` for this.\n\n```cs\nprivate void regularButtonClick(object sender, RoutedEventArgs e)\n    =\u003e SendToInput(((Button)sender).Content.ToString());\n\nprivate void SendToInput(string content)\n{\n    //Prevent 0 from appearing on the left of new numbers\n    if (txtInput.Text == \"0\")\n        txtInput.Text = \"\";\n\n    txtInput.Text = $\"{txtInput.Text}{content}\";\n}\n```\n## PerformClick()\nThere is no `PerformClick` method in `WPF`...\nHowever, we can work around this creating our own Extension Method:\n```cs\npublic static void PerformClick(this Button btn)\n    =\u003e btn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));\n```\nYou can put this in any public static class. I chose to create a file called `ExtMethods.cs` and create a public static class with the same name for it.\n\n## PreviewTextInput\nFinally our PreviewTextInput method will look like this:\n```cs\nprivate void Window_PreviewTextInput(object sender, TextCompositionEventArgs e)\n{\n    switch (e.Text)\n    {\n        case \"0\":\n        case \"1\":\n        case \"2\":\n        case \"3\":\n        case \"4\":\n        case \"5\":\n        case \"6\":\n        case \"7\":\n        case \"8\":\n        case \"9\":\n            SendToInput(e.Text);\n            break;\n\n        case \"*\":\n            btnMultiplication.PerformClick();\n            break;\n\n        case \"-\":\n            btnSubtraction.PerformClick();\n            break;\n\n        case \"+\":\n            btnSum.PerformClick();\n            break;\n\n        case \"/\":\n            btnDivision.PerformClick();\n            break;\n\n        case \"=\":\n            btnEquals.PerformClick();\n            break;\n\n        default:\n            //Can't use directly from switch because it is not a constant\n            if (e.Text == DecimalSeparator)\n                btnPoint.PerformClick();\n            else if (e.Text[0] == (char)8)\n                btnBack.PerformClick();\n            else if (e.Text[0] == (char)13)\n                btnEquals.PerformClick();\n\n            break;\n    }\n\n    //This will prevent other buttons focus firing its click event on \u003cENTER\u003e while typing\n    btnEquals.Focus();\n}\n```\n\n# Unit Tests\nThis might be silly, but the most important here is the principle of Unit Testing. Here, our main goal is:\n- If anyone in the future changes the Operations' classes and interface, we guarantee that it did not break our calculator, in other words, our calculator will still do successfully all 4 operations.\n\n## Adding a Test Project\nThis is pretty straight forward, you just add a new `Test Project` in your solution, I even chose a .NET Core 2.2 Test Project since we won't be needing any WFP components for this tests.\n\nAfter that, you'll need to add the Calculator's project as a dependency to the Test project (Right-click in Dependencies\u003eAdd Reference...)\n\n## Testing the polymorphism \nWe want to be sure that every operation class is an implementation of `IOperation`. We can do that simple as below:\n```cs\n[TestMethod]\npublic void Sum_IsIOperation()\n{\n    var sum = new Sum();\n    Assert.IsTrue(sum is IOperation);\n}\n\n[TestMethod]\npublic void Subtraction_IsIOperation()\n{\n    var subtraction = new Subtraction();\n    Assert.IsTrue(subtraction is IOperation);\n}\n\n[TestMethod]\npublic void Division_IsIOperation()\n{\n    var division = new Division();\n    Assert.IsTrue(division is IOperation);\n}\n\n[TestMethod]\npublic void Multiplication_IsIOperation()\n{\n    var multiplication = new Multiplication();\n    Assert.IsTrue(multiplication is IOperation);\n}\n```\n\nThe test will fail where the operation class is not an implementation of `IOperation`.\n\n\n## Testing the operations\nBasically, we guarantee in these tests that 2 random decimal numbers will result in the desired operation when method `DoOperation` is called.\n\n```cs\n[TestMethod]\npublic void Sum_IsSuming()\n{\n    var sum = new Sum();\n    var rnd = new Random();\n    var a = (Decimal)rnd.NextDouble();\n    var b = (Decimal)rnd.NextDouble();\n    var result = sum.DoOperation(a, b);\n    Assert.AreEqual(a + b, result);\n}\n\n[TestMethod]\npublic void Subtraction_IsSubtracting()\n{\n    var subtraction = new Subtraction();\n    var rnd = new Random();\n    var a = (Decimal)rnd.NextDouble();\n    var b = (Decimal)rnd.NextDouble();\n    var result = subtraction.DoOperation(a, b);\n    Assert.AreEqual(a - b, result);\n}\n\n[TestMethod]\npublic void Multiplication_IsMultiplying()\n{\n    var multiplication = new Multiplication();\n    var rnd = new Random();\n    var a = (Decimal)rnd.NextDouble();\n    var b = (Decimal)rnd.NextDouble();\n    var result = multiplication.DoOperation(a, b);\n    Assert.AreEqual(a * b, result);\n}\n\n[TestMethod]\npublic void Division_IsDividing()\n{\n    var division = new Division();\n    var rnd = new Random();\n    var a = (Decimal)rnd.NextDouble();\n    var b = (Decimal)rnd.NextDouble();\n\n    //Can't divide by 0\n    while (b == 0)\n        b = (Decimal)rnd.NextDouble();\n\n    var result = division.DoOperation(a, b);\n    Assert.AreEqual(a / b, result);\n}\n```\n\nThis is not a 100% guarantee because 2 random numbers can make a successfully operation by using `DoOperation`, while other 2 would not.\nHowever, these unit tests will at least warn us when any 2 random numbers do not match with the operation's result.\n\n### Division by Zero\nIf by any chance our division test gets `0` as the second random number, we should generate a new one. And actually, we should do so until the generated number is different from zero!\n\n## Testing Division by Zero\nFor last, we can make a unit test to make sure when using `DoOperation` of `Division` class with a `0` in the second parameter will result in a `DivideByZeroException`.\n\n```cs\n[TestMethod]\npublic void DivisionByZero_IsGivingException()\n{\n    var division = new Division();\n    var rnd = new Random();\n    var a = (Decimal)rnd.NextDouble();\n    Assert.ThrowsException\u003cDivideByZeroException\u003e(() =\u003e division.DoOperation(a, 0));\n}\n```\n\n## Porting to .NET 6.0\nAs of 2022-10-07 this project has been updated to the latest LTS version of .NET, compiled and tested using Visual Studio 2022.\n\n## Porting to .NET Core 3.0\n~~For now, Visual Studio 2019 only supports the WPF Designer in .NET Framework projects. I have the intention to port this project as soon as Visual Studio starts to support WPF Designer in .NET Core. However, if you wish to do it sooner, there is a workaround explained [here](https://docs.microsoft.com/en-us/dotnet/core/porting/wpf#wpf-designer).~~\n\n### Update 2019-09-25\nFrom this week's release of VS2019 16.3.0, VS will render WPF design, so as promissed, I ported this project to .NETCore 3.0. For further information about the porting please see this [commit](https://github.com/igorocampos/WPFCalc/commit/5b3c95390b9a22fadd1df1f0afa36e367011118e) \nDon't forget to check Tools|Options|Environment|Preview Features|Use Previews of the .NET Core SDK (requires restart), otherwise the designer won't load.\n\n**Why Azure Pipelines is not passing**\n\u003e Version 3.0.100 of the .NET Core SDK requires at least version 16.3.0 of MSBuild. The current available version of MSBuild is 16.0.0.0. Change the .NET Core SDK specified in global.json to an older version that requires the MSBuild version currently available.\n\nIt seems like it is too early to use .NETCore 3.0 in Azure Pipelines\n\n### Update 2019-09-30\nAzure Pipelines was updated and all is well now. The only change that I needed to do was in the `vmImage`, when I ported to .NET Core 3.0 I left it as `ubuntu-latest`, however since this is a WPF project it needs to run in a Windows machine, so I changed to `windows-latest` and pipeline is green again ;)\n\n\n## License\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Figorocampos%2FWPFCalc.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Figorocampos%2FWPFCalc?ref=badge_large)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorocampos%2Fwpfcalc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figorocampos%2Fwpfcalc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorocampos%2Fwpfcalc/lists"}