Home
Search
 
What's New
Index
Books
Links
Q & A
Newsletter
Banners
 
Feedback
Tip Jar
 
C# Helper...
 
XML RSS Feed
Follow VBHelper on Twitter
 
 
 
MSDN Visual Basic Community
 
 
 
 
 
 
TitleMake an ExtenderProvider that adds images and different fonts to MenuItems in VB .NET
DescriptionThis example shows how to make an ExtenderProvider that adds images and different fonts to MenuItems in VB .NET. The provider catches the MeasureItem and DrawItem events for a MenuItem object and draws its image and text using the indicated font.
KeywordsExtenderProvider, integer range, range, validation, validate
CategoriesSoftware Engineering, Controls, VB.NET
 
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
 
At design time, you set the IsRequiredMessage for TextBoxes that you want to be required. The main program doesn't need to use any code to validate TextBoxes.

If you want to ensure that all TextBoxes are filled in, call the HasError function in the form's Closing event handler to see if it is safe to close the form.

 
Private Sub Form1_Closing(ByVal sender As Object, ByVal e _
    As System.ComponentModel.CancelEventArgs) Handles _
    MyBase.Closing
    e.Cancel = IntegerRangeProvider1.HasError()
End Sub

Private Sub btnOk_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles btnOk.Click
    Me.Close()
End Sub

Private Sub btnCancel_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles btnCancel.Click
    IntegerRangeProvider1.Enabled = False
    Me.Close()
End Sub
 
The main program doesn't need any code to display the menus. Everything is taken care of by setting the MenuImage and MenuFont properties. The main program can handle the menus exactly as it handles other menus.

One drawback to this method is that the menus are not drawn at design time so you cannot see what they will look like until you run the program.

 
 
Copyright © 1997-2010 Rocky Mountain Computer Consulting, Inc.   All rights reserved.
  Updated