{"id":9809,"date":"2015-09-09T06:00:00","date_gmt":"2015-09-09T13:00:00","guid":{"rendered":"https:\/\/www.sapien.com\/blog\/?p=9809"},"modified":"2015-09-07T00:42:41","modified_gmt":"2015-09-07T07:42:41","slug":"scope-in-a-powershell-gui-app","status":"publish","type":"post","link":"https:\/\/dev.sapien.com\/blog\/2015\/09\/09\/scope-in-a-powershell-gui-app\/","title":{"rendered":"Scope in a PowerShell GUI App"},"content":{"rendered":"<p>If you search the SAPIEN blog, you&#8217;ll realize that this is the not our first blog about scope in PowerShell GUIs. In March 2013, SAPIEN CTO Alex Riedel wrote <a href=\"https:\/\/www.sapien.com\/blog\/2013\/03\/06\/first-rule-of-powershell-scoping-rules\/\">First Rule of PowerShell Scoping Rules<\/a>, a title that implies the final word in a discussion that wasn&#8217;t easy to end. Alex&#8217;s blog explained the differences between scoping rules in PowerShell and other languages, like VB Script.<\/p>\n<p>Then, just a month later, SAPIEN Senior Developer David Corrales wrote <a href=\"https:\/\/www.sapien.com\/blog\/2013\/04\/10\/powershell-scoping-revisited\/\">PowerShell Scoping Revisited<\/a>, another great post about function scope in PowerShell. A quick search reveals even older posts by Don Jones and Jeffery Hicks.<\/p>\n<p>It seems that everyone is a bit baffled by scopes in PowerShell GUIs and each of us needs to resolve the scope issue that most confounds us. For me, the most baffling part of scoping in a very simple PowerShell GUI app was the lack of an evident scope boundary.<\/p>\n<h3>Where is the scope boundary?<\/h3>\n<p>I know from <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113260\">about_Scopes<\/a> and other sources that when a script includes functions, the script is a scope and each function in the script has its own child scope. The child scope can read from, but not change (write to), the values of variables in the parent scope. If you try to write to a parent scope, you create a local variable with the same name.<\/p>\n<p>But, a very simple PowerShell GUI app is just one big script or function. The script contains event handlers, but those <i>seem to be<\/i> just commands assigned to a variable.<\/p>\n<p>$buttonClick _Close = { $form.Close() }<br \/>\n<i>Is this a scope?<\/i><\/p>\n<p>However, when I create a variable that is designed to be shared by the event handlers, the event handlers can <i>read<\/i> the value, but they can&#8217;t <i>change<\/i> it.<\/p>\n<p>For example, in this little GUI app, the <strong>$sharedValue<\/strong> variable is supposed to be shared by all event handlers. The <strong>$buttonReadValue_Click<\/strong> event handler reads the value and assigns it to a read-only textbox. The <strong>$textboxShared.TextChanged<\/strong> event handler changes the shared value to whatever you type in the text box.<\/p>\n<blockquote><p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/09\/clip_image001.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"clip_image001\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/09\/clip_image001_thumb.png\" alt=\"clip_image001\" width=\"321\" height=\"215\" border=\"0\" \/><\/a><\/p><\/blockquote>\n<pre lang=\"PowerShell\">$sharedValue = 'Start'\r\n\r\n$formTestScope_Load = {\r\n    $textboxShared.Text = $sharedValue\r\n}\r\n\r\n$buttonReadValue_Click={\r\n    $textboxRead.Text = $sharedValue\r\n}\r\n\r\n$textboxShared_TextChanged={\r\n    $sharedValue = $textboxShared.Text.Trim()\r\n}<\/pre>\n<p>But, it doesn&#8217;t work. The <strong>$buttonReadValue_Click<\/strong> event handler can read the shared value (and assign it to its textbox), but the <strong>$textboxShared_TextChanged<\/strong> event handler doesn&#8217;t change it. When you change the value in the <b>Shared value<\/b> textbox and click <b>Read value<\/b> again, it gets (and displays) the initial value.<\/p>\n<blockquote><p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/09\/clip_image002.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"clip_image002\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/09\/clip_image002_thumb.png\" alt=\"clip_image002\" width=\"346\" height=\"234\" border=\"0\" \/><\/a><\/p><\/blockquote>\n<p>This behaves like a scope issue, but I couldn&#8217;t see the scope boundary.<\/p>\n<h1>Event Handlers and Script Scope<\/h1>\n<p>Then it occurred to me:<\/p>\n<p><b>Each event handler contains a script block. And, each script block is a script with its own scope. <\/b><\/p>\n<p>To prove my theory about the scope boundaries, in the console, I created two variables that contained script blocks. My variables are <b><i>not<\/i><\/b> event handler, but they have the same syntax.<\/p>\n<p>&#8212; The $readScript script block reads a variable value from the parent scope.<br \/>\n&#8212; The $writeScript script block changes the value of a variable in the parent scope.<\/p>\n<p>To run the code in the variables that contain script blocks, you need to use the invoke operator (&amp;). The results confirm that each script block is a script that has its own scope. The attempt (in $writeScript) to change the $name variable value fails.<\/p>\n<pre class=\"output\">PS C:\\&gt; $name = 'PowerShell'\r\nPS C:\\&gt; $readScript = { Write-Host \"I love $name.\" }\r\nPS C:\\&gt; $writeScript = { $name = 'Windows PowerShell'; Write-Host \"I love $name.\" }\r\n\r\nPS C:\\&gt; &amp; $readScript\r\nI love PowerShell.\r\n\r\nPS C:\\&gt; &amp; $writeScript\r\nI love Windows PowerShell.\r\n\r\nPS C:\\&gt; &amp; $readScript\r\nI love PowerShell.\r\n\r\nPS C:\\&gt; $name\r\nPowerShell\r\n<\/pre>\n<p>Next, I&#8217;ll scope the variables. I almost never use Global scope. As a best practice, I use the smallest effective scope, but Global is the smallest scope for the command line.<\/p>\n<p>This time, $writeScript changes the value of $name.<\/p>\n<pre class=\"output\">PS C:\\&gt; $name = 'PowerShell'\r\nPS C:\\&gt; $readScript = { Write-Host \"I love $name.\" }\r\nPS C:\\&gt; $writeScript = { $global:name = 'Windows PowerShell'; Write-Host \"I love $name.\" }\r\n\r\nPS C:\\&gt; &amp; $readScript\r\nI love PowerShell.\r\n\r\nPS C:\\&gt; &amp; $writeScript\r\nI love Windows PowerShell.\r\n\r\nPS C:\\&gt; &amp; $readScript\r\nI love Windows PowerShell.\r\n\r\nPS C:\\&gt; $name\r\nWindows PowerShell\r\n<\/pre>\n<p>The alternative to using a scope modifier (e.g. $global:&lt;varName&gt;), is to use the <a href=\"http:\/\/go.microsoft.com\/fwlink\/p\/?linkid=294015\">Set-Variable<\/a> cmdlet with its <strong>Scope<\/strong> parameter.<\/p>\n<pre class=\"output\">PS C:\\&gt; $name = 'PowerShell'\r\nPS C:\\&gt; $writeScript = {\r\n&gt;&gt; Set-Variable -Name Name -Scope Global -Value 'Windows PowerShell'\r\n&gt;&gt; Write-Host \"I love $name\"\r\n&gt;&gt; }\r\n&gt;&gt;\r\n\r\nPS C:\\&gt; &amp; $writeScript\r\nI love Windows PowerShell\r\n\r\nPS C:\\&gt; $name\r\nWindows PowerShell\r\n<\/pre>\n<p>That works.<\/p>\n<p>Now that I understand that the event handler variables contains scripts with their own scope. Let&#8217;s fix the little PowerShell GUI app.<\/p>\n<p>I scoped the <strong>$sharedValue<\/strong> variable to the script and, in the <strong>$textboxShared_TextChanged<\/strong> event, which is the only place that I change that variable value, I scope the reference to the <strong>$sharedValue<\/strong> variable.<\/p>\n<pre lang=\"PowerShell\">$script:sharedValue = 'Start'\r\n\r\n$formTestScope_Load = {\r\n    $textboxShared.Text = $sharedValue\r\n}\r\n\r\n$buttonReadValue_Click={\r\n    $textboxRead.Text = $sharedValue\r\n}\r\n\r\n$textboxShared_TextChanged={\r\n    # scoped variable\r\n    $script:sharedValue = $textboxShared.Text.Trim()\r\n}<\/pre>\n<p>Now, when I type in the <b>Shared value <\/b>box, $textboxShared_TextChanged changes the value of the $sharedValue variable. And, when I click <b>Read value <\/b>the $buttonReadValue_Click reads the changed value.<\/p>\n<blockquote><p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/09\/clip_image003.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"clip_image003\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/09\/clip_image003_thumb.png\" alt=\"clip_image003\" width=\"339\" height=\"232\" border=\"0\" \/><\/a><\/p><\/blockquote>\n<p>In a real script that I plan to save and share, I would explicitly scope all references to a variable in a parent scope, even though it&#8217;s not necessary. This makes it very clear that I intended to create a script-scope variable and that I&#8217;m referencing a variable in a script scope.<\/p>\n<pre lang=\"PowerShell\"># Script-scoped variables\r\n$script:sharedValue = 'Start'\r\n\r\n$formTestScope_Load = {\r\n    # scoped variable\r\n    $textboxShared.Text = $script:sharedValue\r\n}\r\n\r\n$buttonReadValue_Click={\r\n    # scoped variable\r\n    $textboxRead.Text = $script:sharedValue\r\n}\r\n\r\n$textboxShared_TextChanged={\r\n    # scoped variable\r\n    $script:sharedValue = $textboxShared.Text.Trim()\r\n}<\/pre>\n<p>One more consideration. PowerShell Studio encloses every form in a separate function. This structure make it very easy open one form as a modal dialog of another form or share data between forms. However, it makes the scoping one level more complex.<\/p>\n<p>So, this is now the last word about scoping in a PowerShell GUI app. Well, probably not.<\/p>\n<p><i>June Blender is a technology evangelist at SAPIEN Technologies, Inc and a Windows PowerShell MVP. You can reach her at <\/i><a href=\"mailto:juneb@sapien.com\"><i>juneb@sapien.com<\/i><\/a><i>\u00a0and follow her on Twitter at <\/i><a href=\"https:\/\/twitter.com\/juneb_get_help\"><i>@juneb_get_help<\/i><\/a><i>.<\/i><\/p>\n<p>Thanks to Windows PowerShell MVPs\u00a0<a href=\"https:\/\/twitter.com\/r_keith_hill\" target=\"_blank\">Keith Hill<\/a> and <a href=\"https:\/\/twitter.com\/FoxDeploy\" target=\"_blank\">Stephen Owen<\/a> for their help with this post.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you search the SAPIEN blog, you&#8217;ll realize that this is the not our first blog about scope in PowerShell GUIs. In March 2013, SAPIEN CTO Alex Riedel wrote First Rule of PowerShell Scoping Rules, a title that implies the final word in a discussion that wasn&#8217;t easy to end. Alex&#8217;s blog explained the differences [&hellip;]<\/p>\n","protected":false},"author":31,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[932,946,703,25],"tags":[1022,934,28,966,1016,997],"class_list":["post-9809","post","type-post","status-publish","format-standard","hentry","category-beginners","category-guiprogramming","category-powershell-studio","category-windows-powershell","tag-guiprogramming","tag-juneb","tag-powershell","tag-powershell-help","tag-powershell-studio","tag-windows-powershell"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/9809","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/comments?post=9809"}],"version-history":[{"count":13,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/9809\/revisions"}],"predecessor-version":[{"id":9823,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/9809\/revisions\/9823"}],"wp:attachment":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/media?parent=9809"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/categories?post=9809"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/tags?post=9809"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}