See Tutorial: Introduction to ExtenderProviders in VB .NET for an overview of creating ExtenderProviders.
This provider stores MenuImage and MenuFont values for each of its client MenuItem objects in its private MenuInfo class. It stores MenuInfo objects in its m_MenuInfo hashtable. It also adds event handler to catch each client MenuItem's MeasureItem and DrawItem events.
When the MeasureItem event fires, the provider's event handler determines the space needed to display the MenuItem's image and text in the desired font.
When the DrawItem event fires, the provider's event handler draws the MenuItem's image and text. See the code for the details.
The GetMenuInfo helper function returns a MenuItem's MenuInfo object or a default object it the MenuItem doesn't have an entry.
The AddOrRemoveIfNecessary helper routine compares a MenuItem object to a default object. If the object has the default values, then the routine ensures that it is removed from the m_MenuInfo hashtable. If the object does not hold the default values, then the routine ensures that it is in the hashtable.
|
Imports System.ComponentModel
<ToolboxBitmap(GetType(MenuImageProvider), _
"menu_image_provider.bmp"), _
ProvideProperty("MenuImage", GetType(MenuItem)), _
ProvideProperty("MenuFont", GetType(MenuItem))> _
Public Class MenuImageProvider
Inherits System.ComponentModel.Component
Implements IExtenderProvider
... Component Designer generated code ...
' Space between the image and text.
Private Const IMAGE_SPACE As Integer = 5
' Information about a MenuItem.
Private Class MenuInfo
Public MenuImage As Image
Public MenuFont As Font
' Return True if this object represents no range.
Public Function IsDefault() As Boolean
Return (MenuImage Is Nothing) And _
(MenuFont Is Nothing)
End Function
End Class
' The information about menus.
Private m_MenuInfo As New Hashtable
' We can extend MenuItems.
Public Function CanExtend(ByVal extendee As Object) As _
Boolean Implements _
System.ComponentModel.IExtenderProvider.CanExtend
Return (TypeOf extendee Is MenuItem)
End Function
' Return this MenuItem's MenuImage.
<Category("Appearance"), _
DefaultValue(GetType(Image), Nothing)> _
Public Function GetMenuImage(ByVal menu_item As _
MenuItem) As Image
Return GetMenuInfo(menu_item).MenuImage
End Function
' Set this control's minimum value.
<Category("Appearance"), _
DefaultValue(GetType(Image), Nothing)> _
Public Sub SetMenuImage(ByVal menu_item As MenuItem, _
ByVal menu_image As Image)
' Get the MenuItem's MenuInfo object.
Dim menu_info As MenuInfo = GetMenuInfo(menu_item)
' See if the image is Nothing.
If menu_image Is Nothing Then
' The image is Nothing.
menu_info.MenuImage = menu_image
Else
' The image is not Nothing.
' Copy it into a new Bitmap.
Dim bm As New Bitmap(menu_image.Width, _
menu_image.Height)
Dim gr As Graphics = Graphics.FromImage(bm)
gr.DrawImage(menu_image, 0, 0)
' Use the pixel in the upper left corner
' to set the image's transparent color.
' See if this pixel is already transparent.
If bm.GetPixel(0, 0).A > 0 Then
' This pixel is not already transparent.
' Use it.
bm.MakeTransparent(bm.GetPixel(0, 0))
End If
' Set the new image.
menu_info.MenuImage = bm
End If
' Add or remove the MenuInfo if necessary.
AddOrRemoveIfNecessary(menu_item, menu_info)
End Sub
' Return this MenuItem's MenuFont.
<Category("Appearance"), _
DefaultValue(GetType(Font), Nothing)> _
Public Function GetMenuFont(ByVal menu_item As _
MenuItem) As Font
Return GetMenuInfo(menu_item).MenuFont
End Function
' Set this control's minimum value.
<Category("Appearance"), _
DefaultValue(GetType(Font), Nothing)> _
Public Sub SetMenuFont(ByVal menu_item As MenuItem, _
ByVal menu_font As Font)
' Get the MenuItem's MenuInfo object.
Dim menu_info As MenuInfo = GetMenuInfo(menu_item)
' Set the new image.
menu_info.MenuFont = menu_font
' Add or remove the MenuInfo if necessary.
AddOrRemoveIfNecessary(menu_item, menu_info)
End Sub
' Return this MenuItem's MenuInfo.
Private Function GetMenuInfo(ByVal menu_item As _
MenuItem) As MenuInfo
' See if we have MenuInfo for this control.
If m_MenuInfo.Contains(menu_item) Then
' We have MenuInfo for this control. Return it.
Return DirectCast(m_MenuInfo(menu_item), _
MenuInfo)
Else
' We do not have MenuInfo for this control.
' Return a new default MenuInfo.
Return New MenuInfo
End If
End Function
' Add or remove this MenuInfo if necessary.
Private Sub AddOrRemoveIfNecessary(ByVal menu_item As _
MenuItem, ByVal menu_info As MenuInfo)
' See if the MenuInfo should be present but is not,
' or should not be present but is.
If menu_info.IsDefault <> Not _
m_MenuInfo.Contains(menu_item) Then
If menu_info.IsDefault Then
' The MenuInfo should not be present but is.
m_MenuInfo.Remove(menu_item)
menu_item.OwnerDraw = False
RemoveHandler menu_item.MeasureItem, _
AddressOf Client_MeasureItem
RemoveHandler menu_item.DrawItem, AddressOf _
Client_DrawItem
Else
' The MenuInfo should be present but is not.
m_MenuInfo.Add(menu_item, menu_info)
menu_item.OwnerDraw = True
AddHandler menu_item.MeasureItem, AddressOf _
Client_MeasureItem
AddHandler menu_item.DrawItem, AddressOf _
Client_DrawItem
End If
End If
End Sub
Private Sub Client_MeasureItem(ByVal sender As Object, _
ByVal e As _
System.Windows.Forms.MeasureItemEventArgs)
Dim menu_item As MenuItem = DirectCast(sender, _
MenuItem)
Dim menu_info As MenuInfo = GetMenuInfo(menu_item)
' Get the size of the MenuImage.
If Not (menu_info.MenuImage Is Nothing) Then
e.ItemWidth = menu_info.MenuImage.Width + _
IMAGE_SPACE
e.ItemHeight = menu_info.MenuImage.Height
End If
' Get the size of the text.
Dim the_font As Font
If (menu_info.MenuFont Is Nothing) Then
the_font = menu_item.GetMainMenu.GetForm.Font
Else
the_font = menu_info.MenuFont
End If
Dim text_size As SizeF = _
e.Graphics.MeasureString(menu_item.Text, _
the_font)
' Add room for the text.
e.ItemWidth += CInt(text_size.Width * 1.5)
If e.ItemHeight < text_size.Height * 1.5 Then _
e.ItemHeight = CInt(text_size.Height * 1.5)
End Sub
Private Sub Client_DrawItem(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DrawItemEventArgs)
' See if the item is selected.
Dim fg_brush As Brush
Dim bg_brush As Brush
If (e.State And DrawItemState.Selected) = 0 Then
' Not selected.
' Use a light background and dark foreground.
bg_brush = New SolidBrush(SystemColors.Menu)
fg_brush = New SolidBrush(SystemColors.MenuText)
Else
' Selected.
' Use a dark background and light foreground.
bg_brush = New _
SolidBrush(SystemColors.Highlight)
fg_brush = New _
SolidBrush(SystemColors.HighlightText)
End If
' Draw the MenuItem's background.
e.Graphics.FillRectangle(bg_brush, e.Bounds)
' Get the MenuItem and its MenuInfo data.
Dim menu_item As MenuItem = DirectCast(sender, _
MenuItem)
Dim menu_info As MenuInfo = GetMenuInfo(menu_item)
' Draw the image.
Dim x As Integer = e.Bounds.X
Dim y As Integer = e.Bounds.Y
If Not (menu_info.MenuImage Is Nothing) Then
y += (e.Bounds.Height - _
menu_info.MenuImage.Height) \ 2
e.Graphics.DrawImage(menu_info.MenuImage, x, y)
x += menu_info.MenuImage.Width + IMAGE_SPACE
End If
' Draw the text.
Dim the_font As Font
If (menu_info.MenuFont Is Nothing) Then
the_font = menu_item.GetMainMenu.GetForm.Font
Else
the_font = menu_info.MenuFont
End If
Dim layout_rect As New RectangleF( _
x, e.Bounds.Y, _
e.Bounds.Width - (x - e.Bounds.X), _
e.Bounds.Height)
Dim string_format As New StringFormat
string_format.Alignment = StringAlignment.Near
string_format.LineAlignment = StringAlignment.Center
string_format.HotkeyPrefix = _
System.Drawing.Text.HotkeyPrefix.Show
e.Graphics.DrawString(menu_item.Text, the_font, _
fg_brush, layout_rect, string_format)
' Free resources.
fg_brush.Dispose()
bg_brush.Dispose()
End Sub
End Class
|