#How to access variables outside of Event Handler?

6 messages · Page 1 of 1 (latest)

atomic island
#

Just as the title said. I'm currently stuck trying to access a variable outside of an event handler. This is the code

  param ([int[]]$coordinates, [string]$label, [string[]]$options)
  $newGroup = New-Object System.Windows.Forms.GroupBox
  $newGroup.Location = New-Object System.Drawing.Point($coordinates[0], $coordinates[1])
  $checkbox = New-Object System.Windows.Forms.CheckBox
  $checkbox.Appearance = [System.Windows.Forms.Appearance]::Normal
  $checkbox.Text = $label
  $checkbox.AutoSize = $true
  
  $radioPanel = New-Object System.Windows.Forms.Panel
  $radioPanel.Location = New-Object System.Drawing.Point(10, 10)
  $radioPanel.AutoSize = $true
  $radioPanel.Enabled = $false
  
  function Initialize-toggler {
    param([System.Windows.Forms.Panel]$radioPanel)
    $checkbox.Add_CheckedChanged({
      param($checkbox)
      Write-Host "panel.enabled: $($radioPanel)"
      Write-Host "checkbox.checkedState: $($checkbox.CheckState)"
      if($checkbox.CheckState -eq $true){
        $radioPanel.Enabled = $true
      } else {
        $radioPanel.Enabled = $false
      }
    })
  }

  Initialize-toggler -radioPanel $radioPanel
 ///


  $newGroup.Controls.Add($checkbox)
  $newGroup.Controls.Add($radioPanel)
  return $newGroup
}

on the $checkbox.Add_CheckedChanged() i tried to access variable $radioPanel and disable it based on the state of $checkbox. However $radioPanel returns error whenever i called it inside the event handler. I tried putting it inside the event handler as a parameter but that doesn't work. the event handler only accepts $checkbox variable as its param.

So again, how do i access $radioPanel inside the event handler $checkbox.Add_CheckedChanged()?

abstract ibex
#

Global Synchronized hashtable would help

agile vault
#

The problem is that you define your $radioPanel control inside the scope of the New-Group function (same applies to the checkbox really, but perhaps it's fine to discard that one in this context).

The variable you use is accessed in the scope the event handler triggers not in the scope it is bound to the control.

UIs don't fit very well into the normal stance of pushing everything into functions.

The subjectively best way to arrange things in my opinion is to express a top-level "thing" that exposes access to your controls.

For instance I might have this defined before I start making the controls in my UI (in a top-most scope):

$ui = [PSCustomObject]@{
    Controls = @{}
}

Then every time I a new control I need to access across scopes, I add it to the hashtable:

$ui.Controls['radioPanel'] = [System.Windows.Forms.Panel]@{
    Location = '10, 10'
    AutoSize = $true
    Enabled  = $false
}

Then my event handler uses the same hashtable and variable to access other parts of the UI:

$ui.Controls['checkBox'].Add_CheckedChanged({
    param ($sender, $eventArgs)
    <# Make decisions #>

    $ui.Controls['radioPanel'].Enabled = $true
})

There's no need (so far) to make this Controls hashtable synchronized. You might do that if you expect to change the keys and values of that hashtable from multiple runspaces / threads. Reading from multiple is fine as-is.

You frequently see $Script:radioPanel or even the terrible $Global:radioPanel used to solve this. I just hate the variable sprawl.

Note that if you opt to go for a scoped variable, you need this when you create the radioPanel. Doesn't matter what you use in the event handler.

atomic island
#

First, thank you for taking the time to answer my question.

Well, the reason i put these controls in the function is because i'm trying to create these groups dynamically.
I tried using hashtable earlier but couldn't make it to work. Since there's going to be multiple of these group, the name of the variable isn't going to be "radioPanel" but "$label-radioPanel" ($label is from the function argument). But since the event handler itself couldn't access the $label variable, I don't have a way to tell the event handler where it is called from and couldn't tell which radioPanel the event handler need to change.

Is there a way to make these dynamically created UI to work or do I have to create these groups one by one?

agile vault
#

You're welcome to put creation of the controls in a function.

But you must maintain a "handle" on that control outside of the function or you can't reference it in the event handler.

The Controls hashtable is just a convenient place to stash controls you expect to access across boundaries.

If you want to make that more dynamic, you could derive the name of a control to access from the name of the control owning the event handler. For example, I might:

function New-Group {
    param (
        [string]
        $Name
    )

    # This control appears on the form, but I don't need to reference
    # it anywhere else
    $group = [System.Windows.Forms.GroupBox]@{
        Name     = "GroupBox-$Name"
        Location = $coordinates -join ','
    }
    # This control needs to be accessible within event handlers.
    $ui.Controls["CheckBox-$Name"] = [System.Windows.Forms.CheckBox]@{
        Name = "CheckBox-$Name"
        Text = $label
        <# ... #>
    }
    
    $panel  = [System.Windows.Forms.Panel]@{
        <# Props #>
    }
    $panel = Add_CheckedChanged({
        param ( $sender )

        $ui.Controls["CheckBox-$($sender.Name)"].Enabled = $true
    }
}

# My UI controls (and anything else I might want) shared across the UI
$ui = [PSCustomObject]@{
    Controls = @{}
}

New-Group ...

You can always try to use one of the Find methods from the form if you prefer. I personally find those less robust.

Whatever you do, I strongly recommend you avoid mucking with dynamically named variables (which will immediately mean you need the *-Variable commands). Using those will still require you to dynamically recreate the name in the event handler, or worse build the script block as text and resort to [ScriptBlock]::Create to make it. Doing that does not in any way fix the scoping problem though.

atomic island
#

Thank you!!