如何在显示时禁用或(取消)检查 ToolStripMenuItem?
How to disable or (un)check a ToolStripMenuItem when it is being displayed?
网上有几个示例说明如何禁用菜单项的 child(例如,使用 [=30= 的 DropDownOpening 事件]parent),但我想创建一个 class 继承 ToolStripMenuItem 并且可以自行决定是否启用它。
像这样:
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
但是 VisibleChanged 事件没有像我希望的那样工作,我也找不到任何其他事件。
我还为项目本身尝试了 DropDownOpening 事件,但它只是在相当长的延迟后才被触发,所以如果用户足够快,他们仍然可以点击该项目一旦显示。
这似乎是一个如此明显的特征,恐怕我遗漏了一些东西......很明显。
有什么想法吗?
编辑:更改 Checked 属性 当然是一样的...
当您将鼠标悬停在项目上并使用自定义 ToolStripMenuItem 的 DropDownOpening 事件时,我确实看到了您引用的延迟。当您将鼠标悬停在该项目上时,它是 然后 尝试打开任何子菜单,即使它没有任何子菜单。这就是您看到的延迟。
尝试使用 OwnerChanged 事件来了解父项何时执行 DropDownOpening 事件:
Public Class SmartMenuItem
Inherits ToolStripMenuItem
Public Sub New(text As String)
MyBase.New(text)
End Sub
Private Sub SmartMenuItem_OwnerChanged(sender As Object, e As EventArgs) _
Handles Me.OwnerChanged
If Me.OwnerItem IsNot Nothing Then
Dim dropMenu As ToolStripMenuItem = TryCast(Me.OwnerItem, ToolStripMenuItem)
If dropMenu IsNot Nothing Then
AddHandler dropMenu.DropDownOpening, Sub() Me.Enabled = MagicBooleanResult()
End If
End If
End Sub
End Class
我很着急,但也很累。一觉醒来,我找到了解决方案,当我将问题中的短语 "is displayed" 翻译成 "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
通过覆盖事件处理函数(而不是使用 AddHandler Me.Paint),您可以确保在基础 class 交易之前执行自定义代码使用 paint 事件,这是更改显示相关属性的最佳机会,例如启用、选中、文本。
更新: 在我的项目中使用上述技术后,我最终得到了一个基础 class,它消除了初始解决方案中的一个大缺点:快捷键仍然会触发项目的点击事件,即使它被禁用。
首先是基础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
二、派生一个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
我希望上面代码中包含的注释有助于解释我是如何解决这个问题的;现在没有时间详细说明 ;o)
网上有几个示例说明如何禁用菜单项的 child(例如,使用 [=30= 的 DropDownOpening 事件]parent),但我想创建一个 class 继承 ToolStripMenuItem 并且可以自行决定是否启用它。
像这样:
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
但是 VisibleChanged 事件没有像我希望的那样工作,我也找不到任何其他事件。
我还为项目本身尝试了 DropDownOpening 事件,但它只是在相当长的延迟后才被触发,所以如果用户足够快,他们仍然可以点击该项目一旦显示。
这似乎是一个如此明显的特征,恐怕我遗漏了一些东西......很明显。
有什么想法吗?
编辑:更改 Checked 属性 当然是一样的...
当您将鼠标悬停在项目上并使用自定义 ToolStripMenuItem 的 DropDownOpening 事件时,我确实看到了您引用的延迟。当您将鼠标悬停在该项目上时,它是 然后 尝试打开任何子菜单,即使它没有任何子菜单。这就是您看到的延迟。
尝试使用 OwnerChanged 事件来了解父项何时执行 DropDownOpening 事件:
Public Class SmartMenuItem
Inherits ToolStripMenuItem
Public Sub New(text As String)
MyBase.New(text)
End Sub
Private Sub SmartMenuItem_OwnerChanged(sender As Object, e As EventArgs) _
Handles Me.OwnerChanged
If Me.OwnerItem IsNot Nothing Then
Dim dropMenu As ToolStripMenuItem = TryCast(Me.OwnerItem, ToolStripMenuItem)
If dropMenu IsNot Nothing Then
AddHandler dropMenu.DropDownOpening, Sub() Me.Enabled = MagicBooleanResult()
End If
End If
End Sub
End Class
我很着急,但也很累。一觉醒来,我找到了解决方案,当我将问题中的短语 "is displayed" 翻译成 "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
通过覆盖事件处理函数(而不是使用 AddHandler Me.Paint),您可以确保在基础 class 交易之前执行自定义代码使用 paint 事件,这是更改显示相关属性的最佳机会,例如启用、选中、文本。
更新: 在我的项目中使用上述技术后,我最终得到了一个基础 class,它消除了初始解决方案中的一个大缺点:快捷键仍然会触发项目的点击事件,即使它被禁用。
首先是基础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
二、派生一个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
我希望上面代码中包含的注释有助于解释我是如何解决这个问题的;现在没有时间详细说明 ;o)