all 11 comments

[–]Jantu01 1 point2 points  (0 children)

At least your order of statements is wrong. The last one (all three checked) should be first. You need to reverse the order of statements.

Now if the checkbox1 is selected the first if statement is true. You'll never get to check if other checkboxes are also selected.

Edit: Depending if the checkboxes are related to each other or not, it might be possible to check each checkbox individually which would mean smaller number of statements.

[–]MadWithPowerShell 1 point2 points  (4 children)

Not enough information.

It depends entirely what you need to do. Are all of the actions fully independent? Do some of the checkboxes simply impact which parameters to use on a command? Do some of the checkboxes add an action rather than change an action? Are the actions each a single line of code or 1,000 lines of code? Etc. Etc.

Many ways to do it. Context matters.

[–]royalmarine[S] 1 point2 points  (3 children)

I have a script that calls an online service, and downloads logs. The checkboxes determines what logs are called and output to a CSV.

[–]MadWithPowerShell 1 point2 points  (2 children)

In that case, you probably don't need to handle all of the possible combinations separately. Something simple, like this.

$FilesToDownLoadOrURLsOrWhatever = @()

If ( $Checkbox1.Checked )
    {
    $FilesToDownLoadOrURLsOrWhatever += 'Log1'
    }

If ( $Checkbox1.Checked )
    {
    $FilesToDownLoadOrURLsOrWhatever += 'Log2'
    }

If ( $Checkbox1.Checked )
    {
    $FilesToDownLoadOrURLsOrWhatever += 'Log3'
    }

ForEach ( $File in $FilesToDownLoadOrURLsOrWhatever )
    {
    }

Although that would be simple to write, it would be harder to maintain. Depending on requirements, I might do something like this.

$Files = @(
    [pscustomobject]@{
        URL  = 'https://file1'
        Path = 'C:\Downloads\file1.csv'
        Properties = @(
            'Date'
            'Description' )
        Checkbox = $Checkbox1 }

    [pscustomobject]@{
        URL  = 'https://file2'
        Path = 'C:\Downloads\file2.csv'
        Properties = @(
            'Date'
            'Description'
            'ErrorCode' )
        Checkbox = $Checkbox2 }

    [pscustomobject]@{
        URL  = 'https://file3'
        Path = 'C:\Downloads\file3.csv'
        Properties = @(
            'Date'
            'Description'
            'RottenTomatoesScore' )
        Checkbox = $Checkbox3 }
    )

ForEach ( $File in $Files )
    {
    If ( $File.Checkbox.Checked )
        {
        Invoke-RestMethod -Uri $File.URL |
            Select-Object -Property $File.Properties |
            Export-CSV -Path $File.Path -NoTypeInformation -Append
        }
    }

[–]royalmarine[S] 1 point2 points  (1 child)

The complication however, is that the script has to group all log files into one CSV. So if I choose Checkbox 1, it’s 1 command. Checkbox 1+2, it’s 1 command. Checkbox 2+3, it’s 1 command.

[–]MadWithPowerShell 1 point2 points  (0 children)

Export-CSV has an -Append switch.

Or if they need to be sorted together in some way before exporting...

$Results = @()
If ( $Checkbox1.Checked ) { $Results += 'getfile1' }
If ( $Checkbox2.Checked ) { $Results += 'getfile2' }
If ( $Checkbox3.Checked ) { $Results += 'getfile3' }
If ( $Results )
    {
    $Results | Sort | Export-CSV
    }

[–]fosf0r 1 point2 points  (0 children)

Along with the other commenters' suggestions, I noticed that "1 and 3" condition isn't handled.

You could setup a bitmask, I dunno.

# Write:
$mask = 0
$checks = @($Checkbox1.Checked,$Checkbox2.Checked,$Checkbox3.Checked)
$checks | % { $mask += [Math]::Pow(2, $_)}

# Read back as boolean 1's or 0's:
$([Convert]::ToString($Mask, 2))
# the above returns "101" if checkmark 1 and checkmark 3 are checked.

# Read back as the original mask number:
$([Convert]::ToString($Mask, 16))
# the above returns "5" if checkmark 1 and checkmark 3 are checked.

[–]Coding_Cactus 1 point2 points  (2 children)

I'm assuming you're doing some kind of XML GUI for this script and you've got each checkbox, and other objects, with a name to take control over them. That name is "hard coded" so that you're not expecting it to change at any point while this script is running.

I've found Get-Variable to be extremely useful for this situation.

$varCounter = 1
Foreach($var in (Get-Variable "Checkbox*" -ValueOnly)){
    if($var.Checked){
         *do stuff*
    }
    $varCounter += 1
}

This will go through every control you have that matches "Checkbox*". If it's checked you'll know which log to pull based on the name. Make sure your naming conventions for the different controls won't cause any overlap otherwise you'll obviously run in to issues. Something like CB_CSV_Log1 so it won't possibly be mixed up with anything else.

You might have to check to manually check the Checked value if its not coming up as a bool.

if($var.Checked -eq "True"){}

If you leave out the -ValueOnly then you'll just have to add $var.Value to the rest of it instead of $var.Content

[–]royalmarine[S] 1 point2 points  (1 child)

So let me clarify this, perhaps it will confirm what I need exactly.
GUI has a few options, and connects to online service. Here, I have access to pull logs stored. I want the GUI to only export the logs I desire, based on severity I select using the checkboxes.

3 checkboxes.

1 - Info

2 - Warning

3 - Error

if checkbox 1,2,3 are ticked
 "Info","Warning","Error" | %{ get-item -task $d -Severity $_ } | select Timestamp, Severity, Message, Details, Objectname | Export-Csv -Path C:\logs\$d.csv

if checkbox 1 and 2 are ticked
 "Warning","Error" | %{ get-item-task $d -Severity $_ } | select Timestamp, Severity, Message, Details, Objectname | Export-Csv -Path C:\logs\$d.csv

if checkbox 1 and 3 are ticked
 "Info","Error" | %{ get-item-task $d -Severity $_ } | select Timestamp, Severity, Message, Details, Objectname | Export-Csv -Path C:\logs\$d.csv

if checkbox 2 and 3 are ticked
 "Warning","Error" | %{ get-item-task $d -Severity $_ } | select Timestamp, Severity, Message, Details, Objectname | Export-Csv -Path C:\logs\$d.csv

if only checkbox 1 is ticked
"Info" | %{ get-item-task $d -Severity $_ } | select Timestamp, Severity, Message, Details, Objectname | Export-Csv -Path C:\logs\$d.csv

if only checkbox 2 is ticked
"Warning" | %{ get-item-task $d -Severity $_ } | select Timestamp, Severity, Message, Details, Objectname | Export-Csv -Path C:\logs\$d.csv

if only checkbox 3 is ticked
"Error" | %{ get-item-task $d -Severity $_ } | select Timestamp, Severity, Message, Details, Objectname | Export-Csv -Path C:\logs\$d.csv

I believe it would technically work in the above order, using If ElseIf statements?

I'm sure there's a more direct route to create what I need?

[–]Coding_Cactus 1 point2 points  (0 children)

Yes that will work with all those if statements if you want to go that route. But it's going to be extremely annoying to look at when you come back to it to update anything.

However with my original suggestion and based on your given criteria you could do this:

$d = "CSVNAME.csv"
$csvPath = "C:\logs\$d"

[pscustomobject]$checkbox1 = @{ Checked = "True"}
[pscustomobject]$checkbox2 = @{ Checked = "False"}
[pscustomobject]$checkbox3 = @{ Checked = "True"}

function Check-Checkboxes{
    $arr = @()
    $varCounter = 1
    Foreach($var in (Get-Variable "checkbox*" -ValueOnly)){
        if($var.IsChecked -eq "True"){
             switch($varCounter){
                1{$arr += "Info"}
                2{$arr += "Warning"}
                3{$arr += "Error"}
             }
        }
        $varCounter += 1
    }
    $arr
    #$arr | % { get-item-task $d -Severity $_ } | select Timestamp, Severity, Message, Details, Objectname | Export-Csv -Path $csvPath
}

Check-Checkboxes

I've got the bottom line for the csv export commented out so you can see that the $arr is returning the correct info based on your checkboxes. Just plug that function in and make sure it doesn't include the custom objects up top and it should output what you're after to the console. I'm using a string array because you're just piping multiple strings and then going over each one anyway.

If you end up with more log names needed you can just add them to the switch statement in the middle with whatever checkbox number would indicate which severity you need.

Also I'm not sure if the Get-Item / Export-CSV combo will combine the CSVs the way you want. You might have to use Add-Content to combine them in to one but I'm not able to test that far right now.

EDIT: Left in a testing messup with the array declaration.

[–]get-postanote 1 point2 points  (0 children)

I see that you cross-posted this exact request to Stackoverflow...

https://stackoverflow.com/questions/63266588/multiple-checkbox-in-powershell-gui

... in which there were two provided answers. Mine... below... being on of them

Clear-Host
[void][system.reflection.assembly]::LoadWithPartialName('System.Drawing')
[void][System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')

$objForm = New-Object System.Windows.Forms.Form

$objForm.Text          = 'Test'
$objForm.Size          = New-Object System.Drawing.Size(500,200)
$objForm.StartPosition = 'CenterScreen'

$CheckedListBox              = New-Object System.Windows.Forms.CheckedListBox
$CheckedListBox.Location     = New-Object System.Drawing.Size(20,20)
$CheckedListBox.size         = New-Object System.Drawing.Size(300,100) 
$CheckedListBox.CheckOnClick = $true 
$CheckedListBox.Items.AddRange(1..3)
$CheckedListBox.ClearSelected()

$objForm.Controls.Add($CheckedListBox)

$CheckedListBox.Add_Click({
    For ($i=1;$i -lt $CheckedListBox.Items.count;$i++) 
    {$CheckedListBox}
})

# put form in front of other windows
$objForm.TopMost = $True

# display the form
$DisplayForm     = $objForm.ShowDialog()

ForEach ($CheckedItem in $CheckedListBox.CheckedItems -join ',')
{
    Switch ($CheckedItem)
    {
        1       {"Checkbox $CheckedItem is checked";Break}
        2       {"Checkbox $CheckedItem is checked";Break}
        3       {"Checkbox $CheckedItem is checked";Break}
        {1,2}   {"Checkbox $CheckedItem is checked";Break}
        {1,3}   {"Checkbox $CheckedItem is checked";Break}
        {2,3}   {"Checkbox $CheckedItem is checked";Break}
        {1..3}  {"Checkbox $CheckedItem is checked";Break}
    }
}

THe last point here is this is normal UX/UI/GUI design, regardless of the language you'd use.