Implementing Calculator in WPF Using F#

Today I am going to explain how to make a calculator in WPF using F#. You will take a F# console application template than add some references, F# source files and most important the logic of coding. The steps are given below.

Step 1: First open a new project in F# (Console Application) using Visual Studio 2010 and .Net version 4.0, give a name to it. As done in the following image.

New Project Dialog Box

Step 2: Now add the following define references to your project by right-clicking on the project name in the Solution Explorer, as I have discussed in my previous article. You can see through the following link.

http://www.c-sharpcorner.com/UploadFile/f5b919/8674/

The references which you have to add are these:

  • System.Windows.Presentation

  • System.Xaml

  • PresentationCore

  • PresentationFramework

  • WindowsBase

Step 3: When you have added all these references your Solution Explorer will look like following image.

Solution Explorer

Step 4: Now Add  three source files by right-clicking on your project in the Solution Explorer by selecting add new items; name them as we have given shortname.fs, operators.fs and calculator.fs.

Step 5: Now write the following code in shortname.fs file. The Shortname.fs file will contain simply type abbreviations.

namespace FSharp_XAML_demo

open System.Windows
open System.Windows.Controls
 
type form = Window
type txt = TextBox
type btn = Button
type ehandler = RoutedEventHandler
type eargs = RoutedEventArgs

Step 6: Now add the following code in operators.fs file. The Operators.fs file will contains simply type definitions.

module FSharp_XAML_demo.operators
 
open FSharp_XAML_demo

let appname = "FSharp_XAML"
let window name =
    System.Windows.Application.LoadComponent(System.Uri(sprintf "/%s;component/%s" appname name, System.UriKind.Relative)) :?> form
 
let (?) (window : form) name =
    window.FindName name
    |> unbox
 
let inline (+=) (event : IEvent<_, _>) handler =
    (event :> Microsoft.FSharp.Control.IDelegateEvent<_>).AddHandler(ehandler(handler))

Step 7: Now add the following code in Calculator.fs; this is a class which handles all computations.

namespace FSharp_XAML_demo
 
type Calculator(callback : string -> unit) =
    let mutable value = ""
    let mutable oldvalue : float option = None
    let mutable op : (float -> float -> float) option = None
    let mutable append = true
    member private this.GetValue() =
        try
            float value
        with _ -> 0.
 
    member this.applyDigit d =
        if append then
            value <- value + d
        else
            append <- true
            value <- d
        callback value
 
    member this.setDot() =
        if append then
            value <- value + "."
        else
            append <- true
            value <- "."
        callback value
 
    member this.setOp newop =
        match op, oldvalue with
        | Some opV, Some oldvalueV ->
            oldvalue <- Some(opV oldvalueV (this.GetValue()))
            value <- ""
            op <- Some newop
        | _ ->
            oldvalue <- Some(this.GetValue())
            value <- ""
            op <- Some newop
        callback "0"
 
    member this.eq() =
        value <-
            match op, oldvalue with
            | Some opV, Some oldvalueV ->
                opV oldvalueV (this.GetValue())
                |> string
            | _ -> value
        oldvalue <- None
        op <- None
        append <- false
        callback value

Step 8: Now right-click on your project in the Solution Explorer and select add new item and add a F# XAML template; write the following code in the xaml file. Your XAML designer will look like the following image. When you will add the xaml file then set its build property to resources.

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  Width="300" Height="360" Title
="Calculator in WPF using F#">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="42*" />
            <RowDefinition Height="68*" />
            <RowDefinition Height="68*" />
            <RowDefinition Height="68*" />
            <RowDefinition Height="68*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <TextBox Grid.ColumnSpan="4" Height="36" HorizontalAlignment="Left" Margin="4,4,0,0" VerticalAlignment="Top" Width="270"  Name="txtResult"
IsReadOnly="True"
                 FontFamily="Courier New" FontSize="30" Text
="0" />
        <Button Content="1" FontSize="20" Grid.Row="1" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Name="num1" />
        <Button Content="2" FontSize="20" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Column="1" Grid.Row="1"
Name="num2" />
        <Button Content="3" FontSize="20" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Column="2" Grid.Row="1"
Name="num3" Grid.ColumnSpan="2" />
        <Button Content="4" FontSize="20" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Row="2" Name="num4" />
        <Button Content="5" FontSize="20" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Column="1" Grid.Row="2"
Name="num5" />
        <Button Content="6" FontSize="20" Grid.Column="2" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Row="2"
Name="num6" />
        <Button Content="7" FontSize="20" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Row="3" Name="num7" />
        <Button Content="8" FontSize="20" Grid.Column="1" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Row="3"
Name="num8" />
        <Button Content="9" FontSize="20" Grid.Column="2" Grid.ColumnSpan="2" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top"
Width="69" Grid.Row="3" Name="num9" />
        <Button Content="+" FontSize="20" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Column="3" Grid.Row="1"
Name="opAdd" />
        <Button Content="-" FontSize="20" Grid.Row="2" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Column="3"
Name="opSub" />
        <Button Content="x" FontSize="20" Grid.Row="3" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Column="3"
Name="opMul" />
        <Button Content="0" FontSize="20" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Row="4" Name="num0" />
        <Button Content="." FontSize="20" Grid.Column="1" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Row="4"
Name="opDot" />
        <Button Content="=" FontSize="20" Grid.Column="2" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Row="4"
Name="opEq" />
        <Button Content="÷" FontSize="20" Grid.Column="3" Height="69" HorizontalAlignment="Left" VerticalAlignment="Top" Width="69" Grid.Row="4"
Name="opDiv" />
    </Grid>
</
Window>

xaml designer

Note- If you get an error in the designer (like a problem in loading the designer) then click on reload designer; it will load the designer.

Step 9: Now finally add a Xaml.fs file by right-clicking on your project in the Solution Explorer and give name to it as we have given calc.xaml.fs and write following code in the file.

namespace FSharp_XAML_demo
 
open System.Windows
open FSharp_XAML_demo.operators
 
type Calc private (xaml : form) as this =
    let txtResult : txt = xaml?txtResult
    let num0 : btn = xaml?num0
    let num1 : btn = xaml?num1
    let num2 : btn = xaml?num2
    let num3 : btn = xaml?num3
    let num4 : btn = xaml?num4
    let num5 : btn = xaml?num5
    let num6 : btn = xaml?num6
    let num7 : btn = xaml?num7
    let num8 : btn = xaml?num8
    let num9 : btn = xaml?num9
    let opAdd : btn = xaml?opAdd
    let opSub : btn = xaml?opSub
    let opMul : btn = xaml?opMul
    let opDiv : btn = xaml?opDiv
    let opDot : btn = xaml?opDot
    let opEq : btn = xaml?opEq
    let calc = Calculator(fun value -> txtResult.Text <- value)
 
    do
        num0.Click += this.num_Click
        num1.Click += this.num_Click
        num2.Click += this.num_Click
        num3.Click += this.num_Click
        num4.Click += this.num_Click
        num5.Click += this.num_Click
        num6.Click += this.num_Click
        num7.Click += this.num_Click
        num8.Click += this.num_Click
        num9.Click += this.num_Click
        opAdd.Click += this.op_Click
        opSub.Click += this.op_Click
        opMul.Click += this.op_Click
        opDiv.Click += this.op_Click
        opDot.Click += this.opDot_Click
        opEq.Click += this.opEq_Click
 
    new () =
        Calc(window "Calc.xaml")
 
    member this.num_Click (sender : obj) (_ : eargs) =
        let asButton = sender :?> btn
        calc.applyDigit (asButton.Content.ToString())
 
    member this.op_Click (sender : obj) (_ : eargs) =
        let asButton = sender :?> btn
        match asButton.Content.ToString() with
        | "+" -> calc.setOp (+)
        | "-" -> calc.setOp (-)
        | "x" -> calc.setOp (*)
        | "÷" -> calc.setOp (/)
        | _ -> ()
 
    member this.opDot_Click (_ : obj) (_ : eargs) =
        calc.setDot()
 
    member this.opEq_Click (_ : obj) (_ : eargs) =
        calc.eq()
 
    member this.Run() =
        (new Application()).Run xaml

Step 10: Your Program.fs file will remain the same like the following code.

open System
open FSharp_XAML_demo
open FSharp_XAML_demo.operators
 
[<EntryPoint>]
[<STAThread>]
let main _ =
    Calc().Run()

Step 11: Now press F5 to run the project and your calculator is ready to use.

Output

Calculator-Output1

Calculator-Output2

Calculator-Output3

Summary

In this article I have discussed that how to implement a calculator in WPF using F#.


Similar Articles