How to create an NPS (Net Promoter Score) logic in windows forms?

59 Views Asked by At

I need to create a windows forms GUI via Powershell which shows an NPS-scale from 0-10 and the user should be able to select only 1 of these values (or change it to another value) like radio buttons, but with text inside of each button. Below is the code I have so far, but I am struggling to work with the evnt, when one button is pressed. I need to set the CheckState of all other buttons back to "unpressed" and I also need to report the value of the last selected button, when someone presses the OK-button. This is what I have so far:

Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Windows.Forms 
Add-Type -AssemblyName System.Drawing

$form = [System.Windows.Forms.Form]::new()
$form.Text = 'NPS-Test'
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$form.Size = @{Width=410;Height=140}
$form.StartPosition = 'CenterScreen'
$form.FormBorderStyle = 'Fixed3D'

function add-FormControl($parent, $type, $x, $y, $width, $height, $text, $font) {
    $c = new-object System.Windows.Forms.$type
    $c.Location = @{x=$x; y=$y}
    $c.Size = @{Width=$width;Height=$height}
    $c.Text = $text
    $c.Font = $font
    $parent.Controls.Add($c)
    return $c
}

$nps = [object[]]::new(11)
foreach($i in 0..10) {
    $nps[$i] = add-FormControl $form 'Checkbox' (10+35*$i) 20 30 30 $i
    $nps[$i].TextAlign = 'MiddleCenter'
    $nps[$i].Appearance = 'button'
    $nps[$i].Add_Click({$finalResult = $i})
    if ($i -gt 8) {$nps[$i].BackColor = 'PaleGreen'; continue}
    if ($i -gt 6) {$nps[$i].BackColor = 'Yellow'; continue}
    $nps[$i].BackColor = 'OrangeRed'

}

$ok = add-FormControl $form 'Button' 170 70 60 20 'OK'
$ok.DialogResult = 'OK'

$form.TopMost = $true
$form.BringToFront()
$form.Activate()
$result = $form.ShowDialog()

which creates a form like this:

NPS-Sample

Can someone please support on the event-part? Is there a "this"-logic inside a click-event or similar to work with focus to that clicked object?

2

There are 2 best solutions below

1
On BEST ANSWER

Based on the good input from @Doofus here the final code:

Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Windows.Forms 
Add-Type -AssemblyName System.Drawing

$form = [System.Windows.Forms.Form]::new()
$form.Text = 'NPS-Test'
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$form.Size = @{Width=410;Height=140}
$form.StartPosition = 'CenterScreen'
$form.FormBorderStyle = 'Fixed3D'

function add-FormControl($parent, $type, $x, $y, $width, $height, $text, $font) {
    $c = new-object System.Windows.Forms.$type
    $c.Location = @{x=$x; y=$y}
    $c.Size = @{Width=$width;Height=$height}
    $c.Text = $text
    $c.Font = $font
    $parent.Controls.Add($c)
    return $c
}

function set-nps($list, $nps) {
    if ($script:npsScore -ne $null) {
        $list[$script:npsScore].Checked = $false
    }
    $script:npsScore = $nps
}

$ok = add-FormControl $form 'Button' 170 70 60 20 'OK'
$ok.DialogResult = 'OK'

$nps = [object[]]::new(11)
foreach($i in 0..10) {
    $nps[$i] = add-FormControl $form 'Checkbox' (10+35*$i) 20 30 30 $i
    $nps[$i].TextAlign = 'MiddleCenter'
    $nps[$i].Appearance = 'button'
    $nps[$i].Add_Click({set-nps -list $nps -nps $this.text})
    if ($i -gt 8) {$nps[$i].BackColor = 'PaleGreen'; continue}
    if ($i -gt 6) {$nps[$i].BackColor = 'Yellow'; continue}
    $nps[$i].BackColor = 'OrangeRed'

}

$form.TopMost = $true
$form.BringToFront()
$form.Activate()
$result = $form.ShowDialog()

cls
write-host "The selected NPS is $npsScore out of 10."
4
On

Nice paint-job, here's my approach to getting the wheels turning. Hope to see others. My approach isn`t always ruthlessly efficient code, but I code for my own needs, (and so I can read it 6 months later).

  1. Enable use of the click event on the 'fake' buttons, utilising "this" notation.

$this will refer to the clicked object, I would want to use a property of that object in the click event, by adding a unique Name value to each button:

$nps[$i].Name = "BUTTON_$i"

So your checkbox 'buttons' are named "BUTTON_0", "BUTTON_1" etc to match their on screen values.

... then in the Add_Click event on the button, pass that 'Name' value to a handler function using $this:

$nps[$i].Add_Click({HANDLER($this.Name)})

The Handler function can manage the rest: 'unchecking' the rest of the buttons so they act like a set of radio buttons, and keeping track of which was the last selected button.

My Handler function looks like this:

function HANDLER($CLICKED_THING){

# SET A SCRIPT WIDE VARIABLE HOLDING THE NAME OF THE BUTTON THAT WAS CLICKED
$script:SELECTED_BUTTON = $CLICKED_THING 

 foreach ($control in $Form.Controls) { # CHECK ALL FORM CONTROLS ...

  if ($control.gettype().Name -eq "Checkbox"){ # IF IT'S A CHECKBOX ...

   if ($control.Name -ne "$CLICKED_THING"){ # AND IT ISN'T THE ONE I CLICKED ...

    $control.checked = $False # MAKE SURE IT'S UN-CHECKED.

   } # END IF

  } # END IF

 } # END FOR EACH

} # END FUNCTION

Lastly add a click event to the "O.k." button to report the name of the clicked button

$ok.Add_Click({write-host $SELECTED_BUTTON })

You could use a pop-up box, write to a file or whatever you need.

As an aside, I might move the "O.k." button creation before all the CheckBoxes are created, so when the Form is initially displayed the O.k. button will have focus and not the first checkbox. You could add your own handling for when none of the checkboxes are checked.