Scepticalist
Scepticalist

Reputation: 3923

Powershell Forms - How to add context menu event handler to prevent open unless item selected?

I have a form with a listview for which I have a right click context menu. I'd like the menu to only appear if the right clikc is above the selected item in the listview - at the moment it appears anywhere in the listview windows and even if nothing is selected.

Add-Type -AssemblyName System.Windows.Forms

$form = New-Object System.Windows.Forms.Form
$form.Text = "Foo"
$form.Size = '400,400'

$CMitemEnable = New-Object System.Windows.Forms.ToolStripMenuItem
$CMitemEnable.Text = 'Enable'
$CMitemDisable = New-Object System.Windows.Forms.ToolStripMenuItem
$CMitemDisable.Text = 'Disable'
$lvcontextmenu = New-Object System.Windows.Forms.ContextMenuStrip
$lvcontextmenu.ShowImageMargin = $false
$lvcontextmenu.Items.AddRange(@($CMitemEnable,$CMitemDisable))

$listviewbox = New-Object System.Windows.Forms.ListView
$listviewbox.View = [System.Windows.Forms.View]::Details
$listviewbox.Location = '15,20'
$listviewbox.Size = '340,150'
$listviewbox.Columns.Add('Host',180) | Out-Null
$listviewbox.FullRowSelect = $true
$listviewbox.MultiSelect = $false
# $listviewbox.ContextMenuStrip = $lvcontextmenu

$InfoText = New-Object System.Windows.Forms.TextBox
$InfoText.Location = '15,180'
$InfoText.Size = '340,120'
$InfoText.Multiline = $true
$InfoText.ScrollBars = "Vertical"
$InfoText.ReadOnly = $true

$form.Controls.AddRange(@($listviewbox,$InfoText))

$listviewbox.Items.AddRange(@('Foobar1','foobar2'))


$listviewbox.Add_MouseDown({
    $listviewbox.contextmenustrip = $null
    If($listviewbox.SelectedItems.Count){
        $listviewbox.contextmenustrip = $lvcontextmenu
        $InfoText.AppendText("`r`n" + ($this.SelectedItems[0].Text))
    }
    Else {
        $InfoText.AppendText("`r`nNothing selected")
        $listviewbox.contextmenustrip = $null
    }
})

# Show form
$form.ShowDialog() | Out-Null
$form.Dispose()

I've not done context menus before and can't see for the life of me how to implement the event handler for this - there's examples for C etc but can someone point me in the right direction for Powershell please?

Upvotes: 1

Views: 2594

Answers (3)

Reza Aghaei
Reza Aghaei

Reputation: 125207

Considering the following points:

  • Don't set ContextMenuStrip for the ListView
  • Handle MouseClick event, so first the item will be selected, then your code run. (As an alternative, if you prefer mouse down, then you need to need to put your logic in BeginInvoke to make sure it will run after selection of item.)
  • Using GetBound method of the selected item and e.Location, check if the clicked location is inside the item rectangle
  • Show the ContextMenuStrip using Show by passing Cursor.Position

Here is a working example:

Add-Type -AssemblyName System.Windows.Forms

$form = New-Object System.Windows.Forms.Form
$form.Text ="Test"
$form.Controls.AddRange(@(
    ($listView1 = [System.Windows.Forms.ListView] @{
        Dock = [System.Windows.Forms.DockStyle]::Fill;
        FullRowSelect = $true;
        View = [System.Windows.Forms.View]::Details;
    })
))
$contextMenuStrip1 = [System.Windows.Forms.ContextMenuStrip]@{}
$listView1.Columns.Add("C1") | Out-Null
$listView1.Columns.Add("C2") | Out-Null
$listView1.Items.Add("Item1") | Out-Null
$listView1.Items.Add("Item2") | Out-Null
$contextMenuStrip1.Items.Add("Menu 1") | Out-Null
$contextMenuStrip1.Items.Add("Menu 2") | Out-Null

$listView1.Add_MouseClick({param($sender,$e)
    if ($e.Button -eq [System.Windows.Forms.MouseButtons]::Right){
        if ($listView1.FocusedItem.GetBounds(
            [System.Windows.Forms.ItemBoundsPortion]::Entire).Contains($e.Location)){
            $contextMenuStrip1.Show([System.Windows.Forms.Cursor]::Position)
        }
    } 
})
$form.ShowDialog() | Out-Null
$form.Dispose()
$contextMenuStrip1.Dispose()

Upvotes: 3

Scepticalist
Scepticalist

Reputation: 3923

The following event handler works as intended - a combination of hints from both of you, thanks.

$listviewbox.Add_MouseUp({
    $listviewbox.contextmenustrip = $null
    If($listviewbox.SelectedItems.Count){
        $listviewbox.contextmenustrip = $lvcontextmenu
        $InfoText.AppendText("`r`n" + ($this.SelectedItems[0].Text))
    }
    Else {
        $InfoText.AppendText("`r`nNothing selected")
        $listviewbox.contextmenustrip = $null
    }
})

Upvotes: 0

Theo
Theo

Reputation: 61068

Maybe there is a cleaner way, but this works for me:

First, remove line 22 ($listviewbox.ContextMenuStrip = $lvcontextmenu) and then change the Click-handler for the $listviewbox into:

$listviewbox.Add_Click({
    If($this.SelectedItems.Count){
        $this.ContextMenuStrip = $lvcontextmenu
        $InfoText.AppendText("`r`n" + ($this.SelectedItems[0].Text))
    }
    Else {
        $this.ContextMenuStrip = $null
        $InfoText.AppendText("`r`nNothing selected")
    }
})

Upvotes: 0

Related Questions