英文:
How do I create individual event handlers for buttons added dynamically to a WinForm in Powershell
问题
我正在尝试创建一个动态生成的 WinForm,其中我传递一个包含名称(键)和图像托管的 URL(值)的字典。
代码会遍历字典并为每个“键”添加一个按钮,当按钮被按下时,它会将图片框设置为该按钮对应的 URL 上的图像。
每次都可能有不同数量的按钮(最多可能达到 10 个)。
我不确定我的方法是否正确,但我得到了我想要的大部分内容。问题在于,不管按下哪个按钮,图像始终是“Mason”。
Add-Type -AssemblyName System.Windows.Forms
# 定义名称和图像 URL 的字典
$image_url_lookup = @{
'Eric' = 'https://cdn.images.express.co.uk/img/dynamic/67/285x190/1758705_1.jpg'
'Mason' = 'https://cdn.images.express.co.uk/img/dynamic/67/285x190/1758732_1.jpg'
'Other' = 'https://cdn.images.express.co.uk/img/dynamic/67/285x190/1758699_1.jpg'
}
# 创建一个新窗体
$form = New-Object System.Windows.Forms.Form
$form.Text = "Select Images"
$form.Width = 800
$form.Height = 600
$form.BackColor = [System.Drawing.Color]::White
# 创建一个新表格布局面板
$tableLayoutPanel = New-Object System.Windows.Forms.TableLayoutPanel
$tableLayoutPanel.Dock = [System.Windows.Forms.DockStyle]::Fill
# 创建一个新图片框
$pictureBox = New-Object System.Windows.Forms.PictureBox
$pictureBox.Width = $form.Width - 100
$pictureBox.Height = $form.Height - 100
$pictureBox.Left = ($form.Width - $pictureBox.Width) / 2
$pictureBox.Top = ($form.Height - $pictureBox.Height) / 2
$pictureBox.BackColor = [System.Drawing.Color]::Transparent
$pictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::Zoom
$image_index = 0
foreach ($name in $image_url_lookup.Keys) {
$image_index++
$button = New-Object System.Windows.Forms.Button
$button.Name = "Button$image_index"
$button.Text = $name
$button.AccessibleName = $image_url_lookup[$name]
$button.Add_Click({ $pictureBox.ImageLocation = $button.AccessibleName })
$button.Width = 75
$button.Height = 23
# 将按钮添加到表格布局面板
$tableLayoutPanel.Controls.Add($button, $i, 0)
}
# 将图片框添加到表格布局面板
$tableLayoutPanel.Controls.Add($pictureBox, 0, 1)
$tableLayoutPanel.SetColumnSpan($pictureBox, $image_index + 5)
# 将表格布局面板添加到窗体
$form.Controls.Add($tableLayoutPanel)
# 显示窗体
$form.ShowDialog() | Out-Null
$form.Dispose()
希望这对你有帮助。
英文:
I am trying to have a dynamically created WinForm where I pass in a dictionary of names (keys) and the url where the image is hosted (values).
The code, loops through the dictionary and adds a button for each 'key' and when the button is pressed, it sets the picturebox to be the image at the url for that button.
There can be a different number of buttons each time (maybe up to 10).
I don't know if my approach (below) is the right one but I get most of what I want. The problem is that the image is only ever 'Mason' no matter which button is pressed.
Add-Type -AssemblyName System.Windows.Forms
# Define a dictionary of names and image URLs
$image_url_lookup = @{
'Eric' = 'https://cdn.images.express.co.uk/img/dynamic/67/285x190/1758705_1.jpg'
'Mason' = 'https://cdn.images.express.co.uk/img/dynamic/67/285x190/1758732_1.jpg'
'Other' = 'https://cdn.images.express.co.uk/img/dynamic/67/285x190/1758699_1.jpg'
}
# Create a new form
$form = New-Object System.Windows.Forms.Form
$form.Text = "Select Images"
$form.Width = 800
$form.Height = 600
$form.BackColor = [System.Drawing.Color]::White
# Create a new table layout panel
$tableLayoutPanel = New-Object System.Windows.Forms.TableLayoutPanel
$tableLayoutPanel.Dock = [System.Windows.Forms.DockStyle]::Fill
# Create a new picture box
$pictureBox = New-Object System.Windows.Forms.PictureBox
$pictureBox.Width = $form.Width - 100
$pictureBox.Height = $form.Height - 100
$pictureBox.Left = ($form.Width - $pictureBox.Width) / 2
$pictureBox.Top = ($form.Height - $pictureBox.Height) / 2
$pictureBox.BackColor = [System.Drawing.Color]::Transparent
$pictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::Zoom
$image_index = 0
foreach ($name in $image_url_lookup.Keys) {
$image_index++
$button = New-Object System.Windows.Forms.Button
$button.Name = "Button$image_index"
$button.Text = $name
$button.AccessibleName = $image_url_lookup[$name]
$button.Add_Click({ $pictureBox.ImageLocation = $button.AccessibleName })
$button.Width = 75
$button.Height = 23
# Add the button to the table layout panel
$tableLayoutPanel.Controls.Add($button, $i, 0)
}
# Add the picture box to the table layout panel
$tableLayoutPanel.Controls.Add($pictureBox, 0, 1)
$tableLayoutPanel.SetColumnSpan($pictureBox, $image_index + 5)
# Add the table layout panel to the form
$form.Controls.Add($tableLayoutPanel)
# Show the form
$form.ShowDialog() | Out-Null
$form.Dispose()
答案1
得分: 2
需要在循环中添加一个.GetNewClosure()
调用,以便每个脚本块“记住”$button.AccessibleName
在分配时的值,否则会发生的情况是,你总是获取到最后一个创建的$button
的值:
foreach ($name in $image_url_lookup.Keys) {
$image_index++
$button = [System.Windows.Forms.Button]@{
Name = "Button$image_index"
Text = $name
AccessibleName = $image_url_lookup[$name]
Width = 75
Height = 23
}
$button.Add_Click({ $pictureBox.ImageLocation = $button.AccessibleName }.GetNewClosure())
$tableLayoutPanel.Controls.Add($button, $i, 0)
}
一个更好的方法是在事件中使用$this
自动变量,这样就不需要新的闭包;这是由Theo在他的评论中提到的:
foreach ($name in $image_url_lookup.Keys) {
$image_index++
$button = [System.Windows.Forms.Button]@{
Name = "Button$image_index"
Text = $name
AccessibleName = $image_url_lookup[$name]
Width = 75
Height = 23
}
$button.Add_Click({ $pictureBox.ImageLocation = $this.AccessibleName })
$tableLayoutPanel.Controls.Add($button, $i, 0)
}
英文:
<!-- language-all: sh -->
You need to add a .GetNewClosure()
call in your loop so that each scriptblock remembers what $button.AccessibleName
was while it was assigned, otherwise what happens is that you're always getting the value of the last $button
created:
foreach ($name in $image_url_lookup.Keys) {
$image_index++
$button = [System.Windows.Forms.Button]@{
Name = "Button$image_index"
Text = $name
AccessibleName = $image_url_lookup[$name]
Width = 75
Height = 23
}
$button.Add_Click({ $pictureBox.ImageLocation = $button.AccessibleName }.GetNewClosure())
$tableLayoutPanel.Controls.Add($button, $i, 0)
}
A much better approach would be to use the $this
automatic variable in your events, that way there is no need for a new closure; this was hinted at by Theo in his comment:
foreach ($name in $image_url_lookup.Keys) {
$image_index++
$button = [System.Windows.Forms.Button]@{
Name = "Button$image_index"
Text = $name
AccessibleName = $image_url_lookup[$name]
Width = 75
Height = 23
}
$button.Add_Click({ $pictureBox.ImageLocation = $this.AccessibleName })
$tableLayoutPanel.Controls.Add($button, $i, 0)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论