I'm making a sample GUI for a new application we are developing at work. The language has already been decided for me, but I am allowed to use any 3rd party DLLs or add-ins or whatever I need in order to make the GUI work as seamlessly as possible.
They want it very mac/ubuntu/vista/Windows 7-like, so I've come up with some very interesting controls and pretty GUI features. One of which are some growing/shrinking buttons near the top of the screen that increase in size when you mouse over them (it uses the distance formula to calculate the size it needs to increase by). When you take your mouse off of the controls, they shrink back down. The effect looks very professional and flashy, except that there is a ghosting effect as the button shrinks back down (and the buttons to the right of it since they are fixed-at-the-hip).
Here is what the buttons look like in the designer:
Here are some code snippets that I am using to do this:
pops child buttons underneath when parent is hovered
Private Sub buttonPop(ByVal sender As Object, ByVal e As System.EventArgs)
For Each control As System.Windows.Forms.Control In Me.Controls
If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.Y > sender.Location.Y AndAlso control.Location.X >= sender.Location.X AndAlso control.Width < sender.Width AndAlso control.Location.X + control.Width < sender.Location.X + sender.Width Then
control.Visible = True
End If
Next
End Sub
size large buttons back to normal after mouse leaves
Private Sub shrinkpop(ByVal sender As Object, ByVal e As System.EventArgs)
Dim oldSize As Size = sender.Size
sender.Size = New Size(60, 60)
For Each control As System.Windows.Forms.Control In Me.Controls
If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.X > sender.Location.X AndAlso (Not control.Location.X = control.Location.X + (sender.size.width - oldSize.Width)) Then
control.Location = New Point(control.Location.X + (sender.size.width - oldSize.Width), control.Location.Y)
End If
If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.Y > sender.Location.Y AndAlso control.Location.X = sender.Location.X AndAlso control.Width < sender.Width Then
control.Location = New Point(control.Location.X, control.Location.Y + (sender.size.height - oldSize.Height))
If Windows.Forms.Control.MousePosition.X < control.Location.X Or Windows.Forms.Control.MousePosition.X > control.Location.X + control.Width Then
control.Visible = False
End If
End If
Next
End Sub
increase size of large command buttons based on the mouse location in the button, happens on mouse move
Private Sub buttonMouseMovement(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
Dim oldSize As Size = sender.Size
Dim middle As Point = New Point(30, 30)
Dim adder As Double = Math.Pow(Math.Pow(middle.X - e.X, 2) + Math.Pow(middle.Y - e.Y, 2), 0.5)
Dim total As Double = Math.Pow(1800, 0.5)
adder = (1 - (adder / total)) * 20
If Not (sender.size.width = 60 + adder And sender.size.height = 60 + adder) Then
sender.Size = New Size(60 + adder, 60 + adder)
End If
For Each control As System.Windows.Forms.Control In Me.Controls
If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.X > sender.Location.X AndAlso (Not control.Location.X = control.Location.X + (sender.size.width - oldSize.Width)) Then
control.Location = New Point(control.Location.X + (sender.size.width - oldSize.Width), control.Location.Y)
End If
If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.Y > sender.Location.Y AndAlso control.Location.X >= sender.Location.X AndAlso control.Width < sender.Width AndAlso control.Location.X + control.Width < sender.Location.X + sender.Width AndAlso (Not control.Location.Y = control.Location.Y + (sender.size.height - oldSize.Height)) Then
control.Location = New Point(control.Location.X, control.Location.Y + (sender.size.height - oldSize.Height))
End If
Next
End Sub
increase size of smaller command buttons
Private Sub SmallButtonMouseMovement(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
Dim oldSize As Size = sender.Size
Dim middle As Point = New Point(22.5, 22.5)
Dim adder As Double = Math.Pow(Math.Pow(middle.X - e.X, 2) + Math.Pow(middle.Y - e.Y, 2), 0.5)
Dim total As Double = Math.Pow(1012.5, 0.5)
adder = (1 - (adder / total)) * 15
If Not (sender.size.Width = 45 + adder And sender.size.height = 45 + adder) Then
sender.Size = New Size(45 + adder, 45 + adder)
End If
For Each control As System.Windows.Forms.Control In Me.Controls
If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.Y > sender.Location.Y AndAlso control.Location.X = sender.location.X AndAlso (Not control.Location.Y = control.Location.Y + (sender.size.height - oldSize.Height)) Then
control.Location = New Point(control.Location.X, control.Location.Y + (sender.size.height - oldSize.Height))
End If
Next
End Sub
decrease puts command buttons back to correct location and hides them if appropriate
Private Sub SmallShrinkPop(ByVal sender As Object, ByVal e As System.EventArgs)
Dim oldsize As Size = sender.Size
If Not (sender.size.width = 45 AndAlso sender.size.height = 45) Then
sender.size = New Size(45, 45)
End If
Dim ChildCounter As Integer = 0
For Each control As System.Windows.Forms.Control In Me.Controls
If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.X = sender.location.X AndAlso control.Width = sender.width AndAlso control.Location.Y > sender.location.y Then
ChildCounter += 1
control.Location = New Point(control.Location.X, control.Location.Y + (sender.size.height - oldsize.Height))
If Windows.Forms.Control.MousePosition.X < control.Location.X Or Windows.Forms.Control.MousePosition.X > control.Location.X + control.Width Then
sender.visible = False
control.Visible = False
End If
End If
Next
If (ChildCounter = 0 AndAlso Windows.Forms.Control.MousePosition.Y > sender.Location.Y + sender.Height) Or (Windows.Forms.Control.MousePosition.X < sender.Location.X Or Windows.Forms.Control.MousePosition.X > sender.Location.X + sender.Width) Then
sender.visible = False
For Each control As System.Windows.Forms.Control In Me.Controls
If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.X = sender.location.x AndAlso control.Width = sender.width Then
control.Visible = False
End If
Next
End If
End Sub
What I know:
- If the form didn't have a background image, I wouldn't have the ghosting problem.
- If this were just a normal button I am drawing, I probably wouldn't have the ghosting problem.
What I've done, and how I've tried to fix it:
- Ensuring the form's doublebuffering is turned on (it was)
- Manually doublebuffering using the bufferedGraphics class (did not help or made it worse)
- Convince the designers that it doesn't need background images or the pretty glass buttons (no go)
- Run Invalidate() on the rectangle containing the form (did not help)
- Run Refresh() on the form (fixed the ghosting, but now the entire screen flashes as it reloads image)
- Sit in the corner of my cubicle and cry softly to myself (helped the stress, but also did not fix issue)
What I am looking for are the answers to these questions:
- Does anyone have any idea how to get rid of the ghosting I'm describing? Should I focus on changing the size less often? should I be focusing on buffering the background image?
- Are there other technologies I should be using here? Are there ActiveX controls that would be better at this than .NET user inherited ones? Is it possible to make a DirectX user-control to use the graphics card to draw itself?
- Is there something else I'm not thinking of here?
~~~~~~~~~~~~~~~~~~~~~~~~~ Update 1: 11/17/2009 9:21 AM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I've improved the efficiency of the draw methods by first checking to see if they need to be redrawn by checking what the new values will be vs what they already are(code changed above). This fixes some of the ghosting, however the core problem still remains to be solved.