通过引用传递似乎有效但仍然产生错误?我如何 return 来自 WinForms 对话框的多个值?

Pass by reference appears to work but still generates an error? How do I return multiple values from a WinForms dialog?

背景

我正在尝试制作一个 WinForm,当用户点击“提交”按钮时,提交函数中的所有值都会返回到主函数以供使用。出于测试目的,我一直在尝试让 $firstName 正常工作。为此,我一直在尝试找出按引用传递的参数。

这个代码块自己运行:

$txtFirstName = New-Object system.Windows.Forms.TextBox
$txtFirstName.text = "John"

function Submit([ref]$firstName){ 
    $firstName.value =  $txtFirstName.Text 
}

$firstName = $null
Submit([ref]$firstName)
$firstName

但是,当我尝试将相同的概念应用到我的 script 时,它似乎有效 但是 我仍然遇到错误。

错误:

Property 'value' cannot be found on this object; make sure it exists and is settable.

这是我的脚本:

https://github.com/InconspicuousIntern/Form/blob/master/Form.ps1

我的假设:

  1. PowerShell 的 passing/assigning 变量有问题,而 运行.
  2. 我的脚本有问题(我不习惯必须先定义函数)

答案: 脚本组件出现问题。

需要:

function Submit(){...}

<form fields>

[void]$NewUserForm.ShowDialog()
Submit([ref]$firstName)
$btnSubmit.Add_Click({ Submit }) #GUI event
$firstName

而不是:

function Submit(){...}

<form fields>

$btnSubmit.Add_Click({ Submit }) #GUI event
[void]$NewUserForm.ShowDialog()
Submit([ref]$firstName)  
$firstName

如果有人链接到我可以了解更多关于如何订购我的脚本和这样做的方法,我将不胜感激。

.

中介绍了为什么您的原始代码段有效以及为什么您的链接完整脚本无效 有效

简而言之:您有一个事件处理程序调用您的 Submit 函数 首先没有参数 之后 使用 参数正确调用它,这导致了问题。


至于中的解释:

您重新排列的代码只能正常工作意外:

它附加了包含有缺陷的、无参数的 Submit 调用的事件处理程序 在显示对话框后 ,此时它什么都不做。


如何构建代码:

你有两个基本选项,通常是组合:

  • 在显示对话框时采取行动,使用事件处理程序

    • 您将事件处理程序作为脚本块传递给各个控件的 .Add_<eventName>() 方法,就像您在 $btnSubmit.Add_Click({ ... }).

      [=78 中所做的那样=]
    • 警告:脚本块在 子变量作用域 中运行,因此需要额外的工作才能修改调用者作用域中的变量,例如使用script 范围说明符;例如,$script:var(或者甚至使用 [ref] 方法,但那是笨手笨脚的)。

  • 在对话框关闭后执行操作

    • 然后您可以直接访问表单控件的属性。

以下自包含代码段演示了这两种技术:

Add-Type -AssemblyName System.Windows.Forms

# Create the form.
$form = New-Object system.Windows.Forms.Form -Property @{
    ClientSize      = New-Object System.Drawing.Point 400,100
    Text            = "Dialog Demo"
}    

# Create the controls and add them to the form.
$form.Controls.AddRange(@(
    New-Object system.Windows.Forms.Label -Property @{
        Text           = "First Name:"
        AutoSize       = $true
        Location       = New-Object System.Drawing.Point 10,20
        Font           = 'Microsoft Sans Serif,10'
    }

    ($txtFirstName = New-Object system.Windows.Forms.TextBox -Property @{
        Width          = 250
        Location       = New-Object System.Drawing.Point 100, 20
    })

    ($btnSubmit = New-Object system.Windows.Forms.Button -Property @{
        Text              = "Submit"
        Location          = New-Object System.Drawing.Point 160, 60
    })
))


# Attach an event handler to the first-name field.
$txtFirstName.Add_KeyPress({ param($obj, $evArgs)
    # Example: Convert every character typed to uppercase. 
    $evArgs.KeyChar = [char]::ToUpper($evArgs.KeyChar)
})

# Make pressing Enter the same as clicking the Submit button.
$form.AcceptButton = $btnSubmit
# Make the Submit button act like an OK button, which means:
#  - auto-close the form
#  - make .ShowDialog() return [System.Windows.Forms.DialogResult]::OK
$btnSubmit.DialogResult = [System.Windows.Forms.DialogResult]::OK

# Show the form synchronously (during which event handlers may fire)
# and take action based on the return value, which is
# a [System.Windows.Forms.DialogResult] enumeration value that
# reflects the button used to close the form.
if ($form.ShowDialog() -eq 'OK') { # Dialog was confirmed.

    # You can now access the controls' properties as needed.
    # In this example we output a custom object that contains
    # the first name; this can easily be extended to include
    # other properties
    [pscustomobject] @{
        FirstName = $txtFirstName.Text 
    }

} else { # Dialog was canceled.
    Write-Warning "Dialog canceled."
}