Solved

Context Menu - Flat & Color Change

Posted on 2004-10-05
37
472 Views
Last Modified: 2008-03-17
In my little VB.net application I have a few context menus. I would like to make them flat and change the background color from system gray to white. I need as simple of a solution as possible. Thanks in advance for your assistance.
 
0
Comment
Question by:TheRoyalFalcon
  • 14
  • 12
  • 3
37 Comments
 
LVL 27

Expert Comment

by:planocz
Comment Utility
FYI.... makes great looking menu's....

check here for free XP software - like ... menus, XP format, etc.

http://www.divil.co.uk/net/
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
You need to use Owner Drawn menus.

You set the Owner Draw property of the menu and then you control the way it is painted. I don't think it's very simple, but it's the way you accomplish what you want to do.
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
Planocz . . . this site is really nice and what this guy has looks really good (I mean really good). But I need something a little more simple right now. I need to avoid adding third party controls at the moment (a stipulation of my employer).

gregasm . . . sounds like a plan. can you give me an example?


Thanks 2 both of you.
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
Have a look at this great and informative article on Owner Drawing::

http://msdn.microsoft.com/msdnmag/issues/04/02/CuttingEdge/
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
I am interested in the question. work has me swapped (i'm working this Saturday and planning to work sunday even - yuck). please do not close this question yet. i have been trying to get this working in between other projects and am hoping to post some questions next week related to the last posting by one of the experts.

would like to do it sooner but i'm swampped.

thanks

0
 
LVL 27

Expert Comment

by:planocz
Comment Utility
Also check this code for posible use....

http://msdn.microsoft.com/msdnmag/issues/04/02/CuttingEdge/
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
planocz, you must not have seen that I posted that exact link already.
0
 
LVL 27

Expert Comment

by:planocz
Comment Utility
Sorry I missed it. :)
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
Hi Bob,

I thought the link I provided does help towards
" I would like to make them flat and change the background color from system gray to white". I've used that article as the basis of writing some code that does exactly that.
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
Working 70 hr weeks. This is my 8th in a row. Just hard to get back to this. Working every day accept every other sunday.

I was trying to hold out for my boss to come up with a new version of VB.net. I cannot open the example at the reference link. He's choking at this point on getting it for me. So I need to ask that you please provide a 1.0 example. Also is there anyway to make it actually flat. The example seems to only do this for XP (can't tell for sure because of the vb.net version issue).

You mentioned in the last comment you've used the article as the basis for some code that does exactly this. Can you post that?

Thanks for your help, I appreciate it.
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
absolutely. I'll search for it and post it here asap.
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
The key is to set the OwnerDraw property to TRUE for each menuitem, and then wire the item to handle these two events:

        Private Sub MakeItemOwnerDraw(ByVal item As MenuItem)
            item.OwnerDraw = True

            AddHandler item.DrawItem, AddressOf StdDrawItem
            AddHandler item.MeasureItem, AddressOf StdMeasureItem
        End Sub

And now you'll need to handle the measureItem event to provide the minimum menu drawing functionality:

        Private Sub StdDrawItem(ByVal sender As Object, ByVal e As DrawItemEventArgs)

            ' Grab a reference to the item being drawn
            Dim item As MenuItem = CType(sender, MenuItem)

            ' Saves helper objects for easier reference
            Dim g As Graphics = e.Graphics
            Dim bounds As RectangleF = MakeRectangleF(e.Bounds)
            Dim itemText As String = item.Text
            Dim itemState As DrawItemState = e.State

            ' Define bounding rectangles to use later
            CreateLayout(bounds)

            ' Draw the menu item background and text
            DrawBackground(g, itemState)  'Here is where you set the background color

            ' Draw the text
            DrawText(g, item, itemState)
        End Sub

        Private Sub CreateLayout(ByVal bounds As RectangleF)

            ' Define the overall menu item area
            MenuItemBounds = bounds

            ' Define the Bitmap area
            BitmapBounds = MenuItemBounds
            BitmapBounds.Width = BitmapWidth + 2

            ' Define the Client area (everything right of the bitmap)
            ItemBounds = bounds
            ItemBounds.X = BitmapWidth

            ' Define the Text area (including text offset)
            ItemTextBounds.X = CType(BitmapWidth + HorizontalTextOffset, Single)
            ItemTextBounds.Y = CType(bounds.Y + VerticalTextOffset, Single)
            ItemTextBounds.Width = CType(bounds.Width, Single)
            ItemTextBounds.Height = CType(bounds.Height, Single)
        End Sub

        Private Sub DrawBackground(ByVal g As Graphics, ByVal itemState As DrawItemState)

            ' Declare some helper variables
            Dim backBrush, bitmapBrush As Brush
            Dim borderPen As Pen
            Dim selected, disabled, paintBitmapArea As Boolean
            Dim rectToPaint As Rectangle

            ' Determine the state of the item
            selected = (itemState And DrawItemState.Selected) = DrawItemState.Selected
            disabled = (itemState And DrawItemState.Disabled) = DrawItemState.Disabled

            ' Determine whether the bitmap vertical strip must be created
            paintBitmapArea = Not BitmapBackColor.Equals(Color.Empty)
            If paintBitmapArea Then
                rectToPaint = Rectangle.Round(ItemBounds)
            Else
                rectToPaint = Rectangle.Round(MenuItemBounds)
            End If

            ' Determine the brushes to use based on the state
            If selected And Not disabled Then
                backBrush = New SolidBrush(MenuItemBackColorSelected)
                borderPen = New Pen(MenuItemBorderSelected)
            Else
                If MenuItemDithered Then
                    backBrush = New LinearGradientBrush(rectToPaint, _
                        MenuItemBackColorStart, _
                        MenuItemBackColorEnd, _
                        LinearGradientMode.Horizontal)
                    borderPen = Nothing
                Else
                    backBrush = New SolidBrush(MenuItemBackColorStart)
                End If
            End If

            ' Fill the area
            ' NOTE:
            '    When you fill an area larger than the linear gradient, the
            '    end color is used to fill it. This ensures that we also have
            '    the bitmap area painted with the end color of the gradient.
            '    This is for free

            If (selected And Not disabled) Then
                rectToPaint = Rectangle.Round(MenuItemBounds)
                g.FillRectangle(backBrush, rectToPaint)

                ' Draw border
                rectToPaint.Width -= 1
                rectToPaint.Height -= 1
                g.DrawRectangle(borderPen, rectToPaint)
            Else
                g.FillRectangle(backBrush, rectToPaint)
                If paintBitmapArea Then
                    bitmapBrush = New SolidBrush(BitmapBackColor)
                    g.FillRectangle(bitmapBrush, BitmapBounds)
                End If
            End If

            ' Cleanup objects
            If Not (bitmapBrush Is Nothing) Then
                bitmapBrush.Dispose()
            End If
            backBrush.Dispose()
            If Not (borderPen Is Nothing) Then
                borderPen.Dispose()
            End If
        End Sub

 Private Sub DrawText(ByVal g As Graphics, ByVal item As MenuItem, ByVal itemState As DrawItemState)
            ' Declare the foreground brush
            Dim foreBrush As Brush

            ' Handle the separator as a special case; then return
            If item.Text = "-" Then
                DrawSeparator(g)
                Return
            End If

            ' Determine the foreground brush to use based on the state
            ' NOTE: you could use gradients too
            If Not item.Enabled Then
                foreBrush = New SolidBrush(MenuItemForeColorDisabled)
            Else
                foreBrush = New SolidBrush(MenuItemForeColor)
            End If

            ' If default item, use bold
            Dim tmpFont As Font
            Dim defaultItem As Boolean
            defaultItem = (itemState And DrawItemState.Default) = DrawItemState.Default
            If (defaultItem) Then
                tmpFont = New Font(ItemFont, FontStyle.Bold)
            Else
                tmpFont = ItemFont
            End If

            ' Get text and keyboard shortcut info to paint
            Dim textToPaint As String = GetEffectiveText(item)

            ' Text and shortcut are null-separated. Split the string in two parts
            Dim parts() As String = textToPaint.Split(Chr(0))

            ' Format the string(s) to render
            Dim strFormat As New StringFormat
            strFormat.HotkeyPrefix = HotkeyPrefix.Show
            strFormat.LineAlignment = StringAlignment.Center

            ' Paint text
            If parts.Length = 1 Then
                ' Paint when no shortcut info is found
                g.DrawString(textToPaint, tmpFont, foreBrush, ItemTextBounds, strFormat)
            Else
                ' Paint text when shortcut info is found
                g.DrawString(parts(0), tmpFont, foreBrush, ItemTextBounds, strFormat)

                ' Paint right-aligned shortcut info
                Dim rect As New RectangleF(ItemBounds.X, ItemBounds.Y, ItemBounds.Width, ItemBounds.Height)
                rect.Width -= BitmapWidth + HorizontalTextOffset + RightOffset
                strFormat.FormatFlags = StringFormatFlags.DirectionRightToLeft
                g.DrawString(parts(1), tmpFont, foreBrush, rect, strFormat)
            End If

            ' Cleanup resources
            foreBrush.Dispose()
        End Sub


0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
TheRoyalFalcon:

This is not a trivial exercise... as you can see. It takes some knowledge of GDI+. Just let me know if there is anything else I can help you with.

-Gerg
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
Hi Greg

Sorry for the delay in responding. With all that’s going on here I’ve been hard pressed to leave before 10PM (in at 7:30 to 8AM).

Thanks so much for passing along the code. I really appreciate it. I need to ask a quick question. When I place these in the forms class quite a few things flag out as undeclared. For example:

~With the line that reads:

        AddHandler item.MeasureItem, AddressOf StdMeasureItem

StdMeasureItem shows up as undeclared.

~With the line that reads:

        Dim bounds As RectangleF = MakeRectangleF(e.Bounds)

MakeRectangleF shows up as undeclared.

There are a few more thereafter. I imagine that I need to import certain name spaces. I tried a few but nothing seemed to work. Do you have any ideas what I need to do next?

Thanks for the help with this. I do appreciate it.

0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 8

Expert Comment

by:gregasm
Comment Utility
Well, I am trying to point you in the right direction. the code that I provided above is not meant to compile.

If the pattern is not clear, then I would need to perhaps provide some complete code that compiles, but alas, I am busy also..
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
@Well, I am trying to point you in the right direction. the
@code that I provided above is not meant to compile.

Sorry for the confusion, I thought you were posting the actual code referenced above.

@If the pattern is not clear, then I would need to perhaps
@provide some complete code that compiles,

I’m trying to make my way through it. I think I’m following the idea so I will try working with it some more today and tomorrow.

If you have access to the code you put together that you mentioned above, please post it if you get a chance.

I will let you know if I can get something working from the code above. If I do I will also post it back to the site.

@but alas, I am busy also..

I understand. I hear a lot about there being less IT work than there was a few years back. I "sometimes" think the amount of work didn’t change "that" much. Employers just laid some folks off and placed that work on those they kept.

I understand if you’re busy (can sympathize greatly). If I get this working for me I will post it back (thanks for the direction). But if you get a little time and can turn up your working example please post it. I’ll even bump up the question 100 points (anything to get home a little early one night). I entirely understand if you can’t though.

Thanks again, I do appreciate it.

Well back to the salt mine.
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
Hi Bob,

As long as TheRoyalFalcon does not have any objections, please leave this question open. I am really meaning to post some compile-able code here soon.

Sorry it is taking so long. He's probably already got it licked by now though.
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
Hi Bob,

We’ve been without power for over a week here in Ohio due to the snow and ice. Just got it back yesterday. Please leave this question open for a little while longer (I appreciate it).

Hi Gregasm,

@Sorry it is taking so long. He's probably already got it licked by now though.

I wish. I can see now this was way more than I anticipated. Would it have been too much for MS to include these things on their own? :-)

Thanks for putting something together for me as I do appreciate it.
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
The code below should illustrate the concept of Owner Drawing menus. Just cut and paste the whole thing into a new form.

Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
   Friend WithEvents ContextMenu1 As System.Windows.Forms.ContextMenu
   Friend WithEvents MenuItem1 As System.Windows.Forms.MenuItem
   Friend WithEvents MenuItem2 As System.Windows.Forms.MenuItem
   Friend WithEvents MenuItem3 As System.Windows.Forms.MenuItem
   Friend WithEvents MenuItem4 As System.Windows.Forms.MenuItem
   Friend WithEvents MenuItem5 As System.Windows.Forms.MenuItem
   Friend WithEvents MenuItem8 As System.Windows.Forms.MenuItem
   <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
      Me.ContextMenu1 = New System.Windows.Forms.ContextMenu
      Me.MenuItem1 = New System.Windows.Forms.MenuItem
      Me.MenuItem2 = New System.Windows.Forms.MenuItem
      Me.MenuItem3 = New System.Windows.Forms.MenuItem
      Me.MenuItem4 = New System.Windows.Forms.MenuItem
      Me.MenuItem5 = New System.Windows.Forms.MenuItem
      Me.MenuItem8 = New System.Windows.Forms.MenuItem
      '
      'ContextMenu1
      '
      Me.ContextMenu1.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.MenuItem1, Me.MenuItem2, Me.MenuItem3, Me.MenuItem4, Me.MenuItem5, Me.MenuItem8})
      '
      'MenuItem1
      '
      Me.MenuItem1.Index = 0
      Me.MenuItem1.Text = "Undo"
      '
      'MenuItem2
      '
      Me.MenuItem2.Index = 1
      Me.MenuItem2.Text = "Cut"
      '
      'MenuItem3
      '
      Me.MenuItem3.Index = 2
      Me.MenuItem3.Text = "Copy"
      '
      'MenuItem4
      '
      Me.MenuItem4.Index = 3
      Me.MenuItem4.Text = "Paste"
      '
      'MenuItem5
      '
      Me.MenuItem5.Index = 4
      Me.MenuItem5.Text = "Delete"
      '
      'MenuItem8
      '
      Me.MenuItem8.Index = 5
      Me.MenuItem8.Text = "Select All"
      '
      'Form1
      '
      Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
      Me.ClientSize = New System.Drawing.Size(292, 266)
      Me.ContextMenu = Me.ContextMenu1
      Me.Name = "Form1"
      Me.Text = "Form1"

   End Sub

#End Region

   Private m_horizontalOffset As Integer = 15
   Private m_font As Font = New Font("Arial", 12)
   Private m_backColor As Color = Color.Violet
   Private m_foreColor As Color = Color.Tomato

   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

      'Initialize the context menu
      For Each mnuItem As MenuItem In ContextMenu1.MenuItems
         MakeMenuOwnerDraw(mnuItem)
      Next mnuItem

   End Sub

   Private Sub MakeMenuOwnerDraw(ByVal ParentMenu As MenuItem)
      ' Let separators draw themselves
      If String.Compare(ParentMenu.Text, "-") = 0 Then Exit Sub

      ParentMenu.OwnerDraw = True
      AddHandler ParentMenu.DrawItem, AddressOf Menu_DrawItem
      AddHandler ParentMenu.MeasureItem, AddressOf Menu_MeasureItem

      If ParentMenu.MenuItems.Count > 0 Then
         For Each mnuItem As MenuItem In ParentMenu.MenuItems
            Call MakeMenuOwnerDraw(mnuItem)
         Next mnuItem
      End If

   End Sub

   Private Sub Menu_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs)

      Dim foreBrush, backBrush As Brush
      Try
         Dim item As MenuItem = DirectCast(sender, MenuItem)
         foreBrush = New SolidBrush(m_foreColor)
         backBrush = New SolidBrush(m_backColor)
         e.Graphics.FillRectangle(backBrush, e.Bounds)
         e.Graphics.DrawString(item.Text, Font, foreBrush, e.Bounds.X + m_horizontalOffset, e.Bounds.Y)
      Finally
         foreBrush.Dispose()
         backBrush.Dispose()
      End Try
   End Sub

   Private Sub Menu_MeasureItem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs)
      Dim item As MenuItem = DirectCast(sender, MenuItem)
      Dim stringSize As SizeF = e.Graphics.MeasureString(Font.Name, Font)

      ' Set the height and width of the item
      e.ItemHeight = CInt(stringSize.Height)
      e.ItemWidth = CInt(stringSize.Width) + m_horizontalOffset

   End Sub

End Class
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
Gregasm

Thank you for posting what you have. I have seen a few examples out on the web for changing the color of the menus and its text. This is very nice and I would use this over what I’ve seen. With that in mind though is there any way to make the menu flat. Of the two things I listed [Context Menu - Flat & Color Change ] this is the bigger of the two. Without making the menu flat changing the color looks really odd and I’d probably want to stay away from that. Thanks for any ideas you can supply. :-)
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
Flat huh?

I take it you mean, without the 3d borders. I didn't see any such property, like FlatStyle, but I'll poke around some and get back to you on this.
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
@I take it you mean, without the 3d borders.

yes.

@I didn't see any such property, like FlatStyle, but I'll poke around
@some and get back to you on this.

thank you, i REALLY appreciate it. ~trf
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
Hi Gregasm,

just wanted to touch base with you and see if you found anything on the flat border? hope things are going well. thanks.
0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
Oh, I am glad you reminded me. Thanks for being so patient.
0
 
LVL 8

Accepted Solution

by:
gregasm earned 250 total points
Comment Utility
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
Please do not close this question. Got tasked with 3 NT4 server conversions in regional areas (different states) along with a couple here. Been swampped for weeks.  Next week I get back to programming. I will try this out next week. Thanks.
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
TheLeatnedOne,

My job sucks. Getting much time to work through this had been next to impossible. When I posted this I had hoped to get a copy paste class, function, or something. I didn't expect to have to work through all this with a hope of possible completing code to make it do what I needed. But without a direct solution post that is all I could plan on doing. This is a complicated question and I need time to work through this. All I could do was squeeze a little here and there. I work 60hrs a week, live an hour from work, and my wife is pregenant (so as you can tell I have alot of time on my hands).

I appreciate gregasm's try at this. But with that in mind I needed a flat menu (which was in the posting). But at this point i just don't care any more. I can't work through what I was given with a hope of making it flat and posting it back. So at this point I give up. And I don't want to have to go through the hassle of trying to break out points or determine a score although I never got what I really needed.

Anyway, you appeare to need this closed for some reason so badly that I'm just closing it without really ever getting what I needed.

If I ever get this to work the way I needed I will post back what I have to those who may read this post and too need a flat menu.

Hope everyone is happy now that the posting is closed. :-(

0
 
LVL 8

Expert Comment

by:gregasm
Comment Utility
Hi,

If you need a cut and paste class, I suggest looking into third party tools. There is no easy way to achieve a flat menu appearance with the standard .NET menu class. To achieve the flat menu look is also too much for me to take on at this time as well (although I am not going to say I have as much going on in my life).. =]

I know you don't have any time to think about this, but other than looking at the link posted in a previous posting (the article written by Dino Esposito about context menu programming), third party tools is the way to save your time.

Good luck, and I wish I could have helped you more.
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
Comment Utility
Thanks gregasm, I do appreciate your attempts (alot). Just didn't have enough time to work through all this. Oh well, if I ever get the time to work through this, I will post back anything I workout. Thanks again.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

Suggested Solutions

Article by: jpaulino
XML Literals are a great way to handle XML files and the community doesn’t use it as much as it should.  An XML Literal is like a String (http://msdn.microsoft.com/en-us/library/system.string.aspx) Literal, only instead of starting and ending with w…
Well, all of us have seen the multiple EXCEL.EXE's in task manager that won't die even if you call the .close, .dispose methods. Try this method to kill any excels in memory. You can copy the kill function to create a check function and replace the …
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now