I am trying to loop through some controls in a Powershell / WPF application. Nothing fancy, just setting some text on a mouseEnter event.
It works fine if I don't loop the code:
$reader = New-Object System.Xml.XmlNodeReader $xaml
$d = [Windows.Markup.XamlReader]::Load($reader)
$d.FindName("GridOne").add_mouseEnter({
$d.FindName("HelpText").Content = "One"
})
$d.FindName("GridTwo").add_mouseEnter({
$d.FindName("HelpText").Content = "Two"
})
$d.FindName("GridThree").add_mouseEnter({
$d.FindName("HelpText").Content = "Three"
})
$d.FindName("GridFour").add_mouseEnter({
$d.FindName("HelpText").Content = "Four"
})
But if I try the same thing as a loop it sets all the controls MouseEnter events to set the text to "Four", the last element of the array:
$reader = New-Object System.Xml.XmlNodeReader $xaml
$d = [Windows.Markup.XamlReader]::Load($reader)
$arrControls = ("One","Two","Three","Four")
foreach ($control in $arrControls) {
$d.FindName("Grid$control").add_mouseEnter({
$d.FindName("HelpText").Content = $control
})
}
Anyone have any thoughts why this is, and how I can fix it?
Thanks,
Ben
OK - This is even weirder...
Tried to address this using a solution along the lines of Kent's suggestion. I got the same using $localControl, so thought I'd try using an array to ensure each entry was distinct:
$i = 0
$localControl = @()
foreach ($control in $arrControls) {
$localControl += $control
write-host "$d.FindName('Grid$control').add_mouseEnter({ $d.FindName('HelpText').Content = $control })"
$d.FindName("Grid$control").add_mouseEnter({
$d.FindName("HelpText").Content = $localControl[$i]
$i = $i + 1
})
}
The behaviour I know get is that each time I mouseOver a control, the text just increments through the array one step. For example, the first control I hover over will output "One", the next will output "Two" and so on, until my array is exhausted when it just outputs null.
This "One", "Two", "Three", "Four" output order is the same regardless of the order I hover over the controls.
... Wait a minute. Have put the $i = $i + 1 in the MouseEnter!
Amended to:
$i = 0
$localControl = @()
foreach ($control in $arrControls) {
$localControl += $control
write-host "$d.FindName('Grid$control').add_mouseEnter({ $d.FindName('HelpText').Content = $control })"
$d.FindName("Grid$control").add_mouseEnter({
$d.FindName("HelpText").Content = $localControl[$i]
})
$i = $i + 1
}
Sets all the outputs to null.
You need to make a closure from the scriptblocks. What you are facing to is like this:
So if you make a closure, it could help:
Variables in scriptblock are not bound to the scope where there were created. Instead they are evaluated in current scope:
Take a local copy of
$control
:All references to
$control
are being resolved after your loop has completed, so they all refer to the last item in your collection.