Solved

Context Menu - Flat & Color Change

Posted on 2004-10-05
37
478 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
ID: 12226810
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
ID: 12233014
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
ID: 12237025
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
Live: Real-Time Solutions, Start Here

Receive instant 1:1 support from technology experts, using our real-time conversation and whiteboard interface. Your first 5 minutes are always free.

 
LVL 8

Expert Comment

by:gregasm
ID: 12239658
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
ID: 12453363
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
ID: 12462124
Also check this code for posible use....

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

Expert Comment

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

Expert Comment

by:planocz
ID: 12464711
Sorry I missed it. :)
0
 
LVL 8

Expert Comment

by:gregasm
ID: 12700096
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
ID: 12700843
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
ID: 12701064
absolutely. I'll search for it and post it here asap.
0
 
LVL 8

Expert Comment

by:gregasm
ID: 12702338
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
ID: 12702341
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
ID: 12740518
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
 
LVL 8

Expert Comment

by:gregasm
ID: 12740809
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
ID: 12754806
@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
ID: 12916942
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
ID: 12927461
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
ID: 12930675
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
ID: 13121607
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
ID: 13127970
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
ID: 13128068
@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
ID: 13231504
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
ID: 13231563
Oh, I am glad you reminded me. Thanks for being so patient.
0
 
LVL 8

Accepted Solution

by:
gregasm earned 250 total points
ID: 13239418
0
 
LVL 1

Author Comment

by:TheRoyalFalcon
ID: 13345650
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
ID: 13534955
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
ID: 13537335
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
ID: 13538000
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

Live: Real-Time Solutions, Start Here

Receive instant 1:1 support from technology experts, using our real-time conversation and whiteboard interface. Your first 5 minutes are always free.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A while ago, I was working on a Windows Forms application and I needed a special label control with reflection (glass) effect to show some titles in a stylish way. I've always enjoyed working with graphics, but it's never too clever to re-invent …
It’s quite interesting for me as I worked with Excel using vb.net for some time. Here are some topics which I know want to share with others whom this might help. First of all if you are working with Excel then you need to Download the Following …
In this video I am going to show you how to back up and restore Office 365 mailboxes using CodeTwo Backup for Office 365. Learn more about the tool used in this video here: http://www.codetwo.com/backup-for-office-365/ (http://www.codetwo.com/ba…
Microsoft Active Directory, the widely used IT infrastructure, is known for its high risk of credential theft. The best way to test your Active Directory’s vulnerabilities to pass-the-ticket, pass-the-hash, privilege escalation, and malware attacks …

815 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

12 Experts available now in Live!

Get 1:1 Help Now