There are several examples online of how to disable a menu item's child (e.g. using the DropDownOpening event of the parent), but I'd like to create a class that inherits ToolStripMenuItem and can decide on its own whether it should be enabled or not.
Something like this:
Public Class SmartMenuItem
Inherits ToolStripMenuItem
Public Sub New(text As String)
MyBase.New(text)
AddHandler MyBase.VisibleChanged, AddressOf enableSelf
End Sub
Private Sub enableSelf(sender As Object, e As System.EventArgs)
Me.Enabled = MagicFunctionBooleanResult()
End Sub
End Class
But the VisibleChanged event doesn't work as I hoped it would nor can I find any other event.
I also tried the DropDownOpening event for the item itself, but that only gets fired with quite a delay so, if the users are fast enough, they could still click the item once it gets displayed.
It seems to be such an obvious feature that I'm afraid I'm missing something ...obvious.
Any ideas?
Edit: Changing the Checked property is the same deal of course...
I was anxious, but tired. Waking up I found the solution, which indeed appeared obvious once I translated the phrase "is displayed" in my question to "on paint":
Public Class SmartMenuItem
Inherits ToolStripMenuItem
Public Sub New(text As String)
MyBase.New(text)
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
Me.Enabled = MagicEnabledFunction()
Me.Checked = MagicCheckedFunction()
Me.Text = MagicTextFunction()
MyBase.OnPaint(e)
End Sub
End Class
By overriding the event handling function (instead of using AddHandler Me.Paint) you can be sure the custom code is executed before the base class deals with the paint event, which is the optimal opportuntiny for changing display relevant properties, e.g. Enabled, Checked, Text.
Update: After utilizing the above technique in my project I ended up with a base class that removes a big drawback in the initial solution: the shortcut keys would still trigger the item's click event, even though it was disabled.
First, the base class:
Public Class MenuItem
Inherits ToolStripMenuItem
Public Delegate Sub ClickDelegate()
Public Sub New(text As String, shortcut As Windows.Forms.Keys, clickCallback As ClickDelegate)
MyBase.New(text)
AddHandler Me.Click, Sub(sender As Object, e As System.EventArgs)
If Me.enabledCallback Then
'NOTE: shortcut keys trigger the event even, if the item is not enabled; so check here to prevent their execution
clickCallback.Invoke()
End If
End Sub
If shortcut <> Keys.None Then
Me.ShortcutKeys = shortcut
Me.ShowShortcutKeys = True
End If
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
'Store the current Enabled state before painting
Dim _enabled As Boolean = Me.Enabled
'Set Enabled/Checked according to the callbacks
Me.Enabled = enabledCallback()
Me.Checked = checkedCallback()
'Paint the item
MyBase.OnPaint(e)
'Restore Enabled
Me.Enabled = _enabled
'NOTES:
'- If the native Enabled-property is not disabled, then the mechanism above allows the item to always respond to shortcut keys.
'- In the lamda click handler (which is also called when a shortcut is used) the enabledCallback will be checked to verify
' that the clickCallback should really be executed.
'- This way, if the criteria for enabling/disabling the item (coded in enabledCallback) change, the shortcut keys will work as expected.
'- Otherwise the enabled state would only be refreshed on paint and, if enabledCallback() = false, then the shortcut keys could not
' be used (until the item is painted again).
'- Query Me.Enabled (or MyBase.enabledCallback) within the enabledCallback override to allow for enabling/disabling regardless of
' the criteria coded in the callback.
'- A similar mechanism for Checked is not implemented, assuming the property is only relevant for painting and is not queried anywhere else.
End Sub
Protected Overridable Function enabledCallback() As Boolean
Return Me.Enabled
End Function
Protected Overridable Function checkedCallback() As Boolean
Return Me.Checked
End Function
End Class
Second, a derived class:
Public Class SelectionMenuItem
Inherits Wd.Menu.MenuItem
Public Sub New(text As String, shortCut As Windows.Forms.Keys, callback As MenuItem.ClickDelegate, minCount As Integer, Optional maxCount As Integer = 1000)
MyBase.New(text, shortCut, callback)
_minCount = minCount
_maxCount = maxCount
End Sub
Private _minCount As Integer
Private _maxCount As Integer
Protected Overrides Function enabledCallback() As Boolean
Return (Magic.Selection.Count >= _minCount) AndAlso (Magic.Selection.Count <= _maxCount)
End Function
End Class
I hope the comments included in the code above help explain how i got around that; haven't got the time right now to elaborate ;o)