1

I have got an understanding problem regarding to Powershell Code in order to remove a Click Event from a WinForm button. After several hours... several days of trying, trying to understand and despairing I thought I give it a break and probably you guys can help me. I really have read several Posts regarding this theme. But that did not help me finally. So please let me ask that question again.

I have seen that there is a possibility to use Eventhandlers and this method seems to work quite fine. As my code seems to be correct, because Powershell do not throw out an error, I would like to know why the code line seems not to be affective. I really do not understand why. Because I have found several codes with remove_Click examples, but in my case it seems not to do what I expect. As I really do not understand why I would like you to help me. Please be so kind and try to explain to me why line 30 of my script has no effect or not the desired effect.

Short: What do I want to do? I just want to remove a Click Event from a button. I could add the Event to the button using Add_Click. So I thought Remove_Click would remove the "Click Code" from this Special button. But it does not seem to work. I just want to remove the Click Property from the button if the savefiledialog is closed by using the cancel button.

This is the code:

Add-Type -AssemblyName System.Windows.Forms


function form_status(){
$form_status = New-Object System.Windows.Forms.Form
$form_status.Size = New-Object System.Drawing.Size(800,530)
$form_status.StartPosition = 'CenterScreen'
$form_status.FormBorderStyle = 'FixedToolWindow'

$form_status_button_csv_logfile = New-Object System.Windows.Forms.Button
$form_status_button_csv_logfile.Location = New-Object System.Drawing.Point(1,1)
$form_status_button_csv_logfile.Size = New-Object System.Drawing.Size(50,50)
$form_status.Controls.Add($form_status_button_csv_logfile)
$form_status_button_csv_logfile.Add_Click({Choose-Folder-For-Checksumlog})
$form_status_button_csv_logfile.add_MouseHover({button_mousehover})
$form_status_button_csv_logfile.add_MouseLeave({button_mouseleave})

[System.Windows.Forms.Application]::EnableVisualStyles();
$form_status_result = $form_status.ShowDialog()
}

Function Choose-Folder-For-Checksumlog(){
$SaveChooser = New-Object -Typename System.Windows.Forms.SaveFileDialog
$SaveChooser.InitialDirectory = [Environment]::GetFolderPath("Desktop")
$SaveChooser.Filter = "CSV Logfile (*.csv)|*.csv"
$savechooser.FileName = "testfile.csv"

if($SaveChooser.ShowDialog() -eq [System.Windows.Forms.DialogResult]::CANCEL){
$savechooser.FileName = ""
$form_status_button_csv_logfile.Remove_Click({Choose-Folder-For-Checksumlog})
}
$checksumlog_folder = $SaveChooser.FileName
}

function button_mouseleave(){
$form_status.Cursor=[System.Windows.Forms.Cursors]::Default
}

function button_mousehover(){
$form_status.Cursor=[System.Windows.Forms.Cursors]::Hand
}

form_status

I appreciate any help from you guys. Please be so kind and explain to me what I do wrong. Probably my expectaions are wrong... But I do not understand it at the moment.

With kindest Regards FernandeZ

FernandeZ
  • 11
  • 4

1 Answers1

0

I know that I'm a bit late to the party, but recently I came into the situtation where it was required to remove multiple event handlers from multiple elements (several different-purpose Click handlers on each of six buttons in my case).

While suggestion from @D'Artagnan works, it is still not helpful in case if your scriptblock handler is not stored in variable (or variable is unknown).

For example, this code removes handler from the button:

$ScriptBlock = {Write-Host 'Clicked'}
$MyButtonObject.Add_Click($ScriptBlock)
$MyButtonObject.Remove_Click($ScriptBlock)

But this one does not (I believe, it is because scriptblock is not referenced by variable, therefore it is a different object, which is not registered in event handler, and, as result, cannot be removed):

$MyButtonObject.Add_Click({Write-Host 'Clicked'})
$MyButtonObject.Remove_Click({Write-Host 'Clicked'})

In addition, I wasn't able to find something like .Remove_AllHandlers(), unfortunately.

Thankfully, I've discovered this question and was able to adapt the code given by @Douglas to PowerShell, so with credits to the original author, I'm happy to share the solution with anyone who wonders:

Function Remove-RoutedEventHandlers {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $True)]
        [ValidateNotNullorEmpty()]
        [System.Windows.UIElement]$Element,
        [Parameter(Mandatory = $True)]
        [ValidateNotNullorEmpty()]
        [System.Windows.RoutedEvent]$RoutedEvent
    )
    
    $eventHandlersStoreProperty = $Element.GetType().GetProperty("EventHandlersStore", [System.Reflection.BindingFlags]'Instance, NonPublic')
    $eventHandlersStore = $eventHandlersStoreProperty.GetValue($Element, $Null)
    If ($eventHandlersStore) {
        $getRoutedEventHandlers = $eventHandlersStore.GetType().GetMethod("GetRoutedEventHandlers", [System.Reflection.BindingFlags]'Instance, Public, NonPublic')
        $RoutedEventHandlers = [System.Windows.RoutedEventHandlerInfo[]]$getRoutedEventHandlers.Invoke($eventHandlersStore, $RoutedEvent)
        ForEach ($RoutedEventHandler in $RoutedEventHandlers) {
            $Element.RemoveHandler($RoutedEvent, $RoutedEventHandler.Handler)
        }
    }
}

To call the function, you must provide the control and required kind of events to be cleared. For example:

Remove-RoutedEventHandlers -Element $MyButtonObject -RoutedEvent $([System.Windows.Controls.Button]::ClickEvent)

Please note that you have to put event in $() to avoid it from being treated as string. Alternatively, you could alter the function to accept string (for example, with value "Click") and re-construct such event on the provided control inside the function like this $RoutedEvent = $Element.GetType()::"${RoutedEvent}Event"

This function enumerates all handlers of the specified type on provided object and removes them. If I'm not mistaken, if you call $Element.Remove_SomeEvent($ScriptBlockToRemove) in PowerShell, $Element.RemoveHandler($Element.GetType()::SomeEvent, $ScriptBlockToRemove) is being executed under the hood, so in given example this function is equivalent of enumerating all of the scriptblocks (referenced in Handler property of RoutedEventHandlerInfo object) and calling $MyButtonObject.Remove_Click($ScriptBlockToRemove) on each of them