Solved

Flat/Raised Buttons in VB.NET

Posted on 2003-10-29
33
1,626 Views
Last Modified: 2010-08-05
In Visual Basic.NET, how can I make my button look like flat/raised when in a normal state, and flat/sunken when it is being clicked? I can't figure out how to do this in VB.NET.
0
Comment
Question by:Produkt
  • 16
  • 12
  • 3
  • +1
33 Comments
 
LVL 48

Expert Comment

by:AlexFM
ID: 9641049
Create Checkbox with Appearance = Button, FlatStyle = System. Maybe this object behaves as you need.
0
 
LVL 44

Expert Comment

by:Arthur_Wood
ID: 9641324
change the FlatStyle property of the Command Button to Flat.  The default is Standard.

AW
0
 

Author Comment

by:Produkt
ID: 9642254
Here, I took a screenshot of what it should look like:
http://distortnet.hypermart.net/buttonstyle.JPG
0
 

Author Comment

by:Produkt
ID: 9642255
Here, I took a screenshot of what it should look like:
http://distortnet.hypermart.net/buttonstyle.JPG
0
 

Author Comment

by:Produkt
ID: 9642256
Here, I took a screenshot of what it should look like:
http://distortnet.hypermart.net/buttonstyle.JPG
0
 

Author Comment

by:Produkt
ID: 9642257
Here, I took a screenshot of what it should look like:
http://distortnet.hypermart.net/buttonstyle.JPG
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 9642298
So, my suggestion is not what you want?
0
 

Author Comment

by:Produkt
ID: 9643355
Nope.
0
 
LVL 44

Expert Comment

by:Arthur_Wood
ID: 9644831
you appear to be asking about the Minimize and Close buttons that are added automatically to the Form design, rahter than about buttons that you have created yourself, on the form.  Is that the case?
0
 

Author Comment

by:Produkt
ID: 9646480
No, I just want that type of style. I set the BorderStyle to none, and I am making my own minimize and close buttons. So essentially, yes they are minimize and close buttons, but they are custom buttons, not from the ControlBox property.
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9646717
At first I thought it would be possible using a panel, which once supported the styles raised and sunken - but properties are different in the .NET version.

How about using a button and a label together? It's not a really good solution, but maybe you have a look at it ...
I am using a button with default settings and a label with properties BorderStyle Fixed3D, FlatStyle Flat, Text Button1, TextAlign MiddleCenter and Visible false.
Make sure they both have the same size and location (this might be done at Form_Load)

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Button1.Visible = False
        Label1.Visible = True
    End Sub

    Private Sub Label1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Label1.Click
        Button1.Visible = True
        Label1.Visible = False
    End Sub

Unfortunately, the button raises higher that the label sinks.
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9646899
You might bring this to work: your own clickable Label. The idea is to draw the rectangle borders yourself, so that is has black/white exchanged wheter the controls property Raised is set to true or false. If you bring this to work, you can even use it in the form designer like you would use normal labels.

Imports System
Public Class Class1
    Inherits Label

    Public Raised As Boolean

    Protected Shadows Sub DrawImage( _
       ByVal g As Graphics, _
       ByVal image As Image, _
       ByVal r As Rectangle, _
       ByVal align As ContentAlignment _
    )
        Dim pen1 As System.Drawing.Pen
        Dim pen2 As System.Drawing.Pen

        If Raised Then
            pen1 = System.Drawing.Pens.Black
            pen2 = System.Drawing.Pens.White
        Else
            pen1 = System.Drawing.Pens.White
            pen2 = System.Drawing.Pens.Black
        End If

        g.DrawLine(pen1, 0, 0, 0, g.VisibleClipBounds.Height)
        g.DrawLine(pen1, 0, 0, g.VisibleClipBounds.Width, 0)
        g.DrawLine(pen2, g.VisibleClipBounds.Width, 0, g.VisibleClipBounds.Width, g.VisibleClipBounds.Height)
        g.DrawLine(pen2, 0, g.VisibleClipBounds.Height, g.VisibleClipBounds.Width, g.VisibleClipBounds.Height)

        Debug.Write("Painting")
    End Sub
End Class

I am missing something, as it does never paint. Maybe someone else can help. It's the first time I try to inherit a control.
0
 
LVL 44

Expert Comment

by:Arthur_Wood
ID: 9647446
again, I ask, why do you not set the FlatStyle Property of the Command Button to Flat.  Is that not theeffect you want, or am I missing something?

AW
0
 

Author Comment

by:Produkt
ID: 9647449
Look at the image I provided. You'll see exactly how it should look.
0
 

Author Comment

by:Produkt
ID: 9649189
Would it be easier just to draw the buttons and use images for this?
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9649380
Maybe yes, but I think you a) can't scale your buttons then and b) they're not aware of system colors.
a) If you scale the image, you also scale the border pixels, which makes them look more raised or more sunken. Of course, you can provide different images for different sizes of your buttons.
b) Your images are fixed in color. If the user changes his desktop scheme to aubergine, your buttons will look strange.

Inheriting a Label should not be difficult. I had a look at TFM and Microsoft says:
"Notes to Inheritors:  When you are creating your own control that derives from Label, you can use this method [DrawImage] to draw images onto the surface of the control."
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 2

Expert Comment

by:wellilein
ID: 9649629
Ok, working variant of customized label:

Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class Class1
    Inherits Label

    Public raised As Boolean = True

    Protected Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)
        ' The Graphics to Paint on
        Dim gc As Graphics = pevent.Graphics

        'Fill the background
        Dim backcolor As Brush = New SolidBrush(Me.BackColor)
        gc.FillRectangle(backcolor, pevent.ClipRectangle)

        Dim pen1 As Pen
        Dim pen2 As Pen
        'swap colors if raised or not
        If raised Then
            pen1 = Pens.White
            pen2 = Pens.Black
        Else
            pen1 = Pens.Black
            pen2 = Pens.White
        End If

        Dim x As Integer = pevent.ClipRectangle.Location.X
        Dim y As Integer = pevent.ClipRectangle.Location.Y
        Dim height As Integer = pevent.ClipRectangle.Height
        Dim width As Integer = pevent.ClipRectangle.Width

        'pen1
        gc.DrawLine(pen1, x, y, x + width - 1, y) ' topleft to topright
        gc.DrawLine(pen1, x, y, x, y + height - 1) ' topleft to bottomleft
        'pen2
        gc.DrawLine(pen2, x + width - 1, y, x + width - 1, y + height - 1) 'topright to bottomright
        gc.DrawLine(pen2, x, y + height - 1, x + width - 1, y + height - 1) 'bottomleft to bottomright
    End Sub
End Class


Set TextAlign to MiddleCenter.
Note that you have to switch the state of the button manually and redraw it then.

    Private Sub Label1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Label1.Click
        Label1.raised = Not Label1.raised  ' Flip the state
        Label1.Refresh() ' Redraw
    End Sub
0
 

Author Comment

by:Produkt
ID: 9650399
I'm getting the errors "Inherits can only appear once within a class" and "raised not a member of System.Windows.Forms.Label"

Sorry, I've just started .NET, I've converted from VB6, and I don't recognize a lot of these new things.
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9650631
Have you put the source code from
         Imports
up to
         End Class
into a new empty file?

Do a right click on the project in your solution explorer window (usually located at the right side) and select Add/Add Class...
Then delete all code which might have been auto-generated and replace it by the code from Imports up to End Class.
You perhaps want to change the name Class1 to something more meaningful, lets say SinkableButton.

Then go back to the form designer and drag a normal Label onto your form. Doubleclick it to see the code. There is a region described "Windows Form Designer generated code". Open it and replace the following commands (Replace --> By):
Friend WithEvents Label1 As System.Windows.Forms.Label --> Friend WithEvents Label1 As SinkableButton
Me.Label1 = New System.Windows.Forms.Label --> Me.Label1 = New SinkableButton

That should be all. Your customized button should behave like a label when used in the form designer. And it should behave like a sinkable button when beeing run.
0
 

Author Comment

by:Produkt
ID: 9654813
Hmm, I can't get it to work. Can you post a picture of the final product, and if it looks correct, I'll go into greater detail of the problems.
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9656192
Yes. http://www.wellisolutions.de/experts-exchange/sinkable.gif shows a screenshot, terribly reduced in colors by MS Paint :-) The Buttons on that picture are just for comparing reasons.

I experimented a bit with that sinkable button and found out, that it has repaint problems when the form is resized, in special if the forms borders cross the buttons. http://www.wellisolutions.de/experts-exchange/sinkproblem.gif   I'm working on it...
0
 
LVL 2

Accepted Solution

by:
wellilein earned 500 total points
ID: 9656341
Ok. The ClipRectangle may be only a portion of the whole button. I think this is done for performance reasons, to not have to repaint everything. I think calculation for painting only parts of the sinkable button would be difficult and in your case, drawing is not complicated and should be fast. So I chose to always paint whole buttons.

Second, I found out how to update graphics when the property 'raised' is toggled. Using the button is more easy now, as you don't have to call the .Refresh() Method after changing the state of the button.

Here's the new source code.

Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class SinkableButton
    Inherits Label
    Private m_Raised As Boolean = True
    Public Property Raised() As Boolean
        Get
            Return m_Raised
        End Get
        Set(ByVal Value As Boolean)
            m_Raised = Value
            Me.Refresh()
        End Set
    End Property

    Protected Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)
        ' The Graphics to Paint on
        Dim gc As Graphics = pevent.Graphics

        'Fill the background
        Dim backcolor As Brush = New SolidBrush(Me.BackColor)
        gc.FillRectangle(backcolor, pevent.ClipRectangle)

        Dim pen1 As Pen
        Dim pen2 As Pen
        'swap colors if raised or not
        If m_Raised Then
            pen1 = Pens.White
            pen2 = Pens.Black
        Else
            pen1 = Pens.Black
            pen2 = Pens.White
        End If

        Dim x As Integer = 0
        Dim y As Integer = 0
        Dim height As Integer = Me.Height
        Dim width As Integer = Me.Width

        'pen1
        gc.DrawLine(pen1, x, y, x + width - 1, y) ' topleft to topright
        gc.DrawLine(pen1, x, y, x, y + height - 1) ' topleft to bottomleft
        'pen2
        gc.DrawLine(pen2, x + width - 1, y, x + width - 1, y + height - 1) 'topright to bottomright
        gc.DrawLine(pen2, x, y + height - 1, x + width - 1, y + height - 1) 'bottomleft to bottomright
    End Sub
End Class

I developed the older versions at home and tried this one at work. Works good for me. If you get errors, let me know.
0
 

Author Comment

by:Produkt
ID: 9658211
Wow, this works great! Thanks a lot, but I have one last question: Is it possible to set a custom pen color, besides the ones provided? For example, if I want to set it to an RGB value?
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9658492
Do you want to a) set it to a custom pen color permanently, or shall it b) be settable like the Raised-Property?

in case a), you can
     a1) choose other Pens provided by Microsoft. Just type "pen1 = Pens." and wait until the list of Pens appears
     a2) choose a RGB Color by 'pen1 = New Pen(Color.FromArgb(alpha, red, green, blue))'

for b) you have to provide two properties. I'll call them SunColor for the bright color and ShadeColor for the dark color
    Add this near the definition of "Raised":

    Private m_SunColor As Color = Color.White 'Default SunColor if not set otherwise by SinkableButton1.SunColor = ...
    Public Property SunColor() As Color
        Get
            Return m_SunColor
        End Get
        Set(ByVal Value As Color)
            m_SunColor = Value
            Me.Refresh()
        End Set
    End Property

    Private m_ShadeColor As Color = Color.Black 'Default ShadeColor
    Public Property ShadeColor() As Color
        Get
            Return m_ShadeColor
        End Get
        Set(ByVal Value As Color)
            m_ShadeColor = Value
            Me.Refresh()
        End Set
    End Property

    Then change the pen definition:

        'swap colors if raised or not
        If m_Raised Then
            pen1 = New Pen(m_SunColor)
            pen2 = New Pen(m_ShadeColor)
        Else
            pen1 = New Pen(m_ShadeColor)
            pen2 = New Pen(m_SunColor)
        End If

As I also like playing with this button, I think I'll improve it even more and upload the sources somewhere. For example I also found out that you can paint the button's background gradually (like the title bar of any windows application) or change the width of the pen, which makes the button more or less sunken (or raised).
0
 

Author Comment

by:Produkt
ID: 9659078
This is really great! I'm doubling your points! I'm having a minor problem though, it doesn't really affect the outcome of the button, but when I change the settings inside the Windows generated form code, the label disappears form the form. When I load the application, it appears, but it's gone on the design form. Does this happen for you?
0
 

Author Comment

by:Produkt
ID: 9659245
Also, is there a way to move the text diagnally slightly to the right/down, not as severely as setting the alignment to BottomRight? If not it's alright, just a minor visual detail.
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9669477
What setting do you change inside the Windows-Form generated code? You only have to tell the designer to use Sinkablebutton instead of Label. Everything else should be in Form_Load or something similar.

---

This will move the button's description slightly right/down:

    Protected Overrides Sub OnPaint(ByVal pevent As PaintEventArgs)
        Dim gc As Graphics = pevent.Graphics
        Dim textsize As SizeF = gc.MeasureString(Me.Text, Me.Font)
        If Me.Raised Then
            textsize.Height = textsize.Height + 2.0
            textsize.Width = textsize.Width + 2.0
        End If
        gc.DrawString(Me.Text, Me.Font, Brushes.Black, (Me.Width - textsize.Width) / 2, (Me.Height - textsize.Height) / 2)
    End Sub

Add it to the definition of the button. Note that TextAlign does not work any more. This method only calculates middle/centered text. If you want the text to move a greater distance, change the "2.0"
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9669532
Just another minor improvement: I should have taken the assigned forground color instead of a black brush:

        Dim brsh As Brush = New SolidBrush(Me.ForeColor)
        gc.DrawString(Me.Text, Me.Font, brsh, (Me.Width - textsize.Width) / 2, (Me.Height - textsize.Height) / 2)
0
 

Author Comment

by:Produkt
ID: 9671000
I tried doing the same thing with a textbox, and I just copied and pasted all the code into a new class called CustomTextBox, changed the stuff in the Form1 window generated code, and nothing is happening. What do you think is going wrong?
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9671234
If you want a textbox, you have to inherit from it.
The SinkableButton is not a Button by the way. It is a Label. But we have made it behave like a button.

You can see it in the first lines of code:
Public Class SinkableButton
    Inherits Label

If you want to have a customized Textbox, you would probably write something like
Public Class MyCustomTextBox
    Inherits TextBox

What shall it look like? Do you also have a screenshot? Maybe I can help again (I slowly begin to know how it works)
0
 

Author Comment

by:Produkt
ID: 9673467
This is what is in SteamTextBox.vb:
Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class SteamTextBox
    Inherits TextBox

    Protected Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)
        ' The Graphics to Paint on
        Dim gc As Graphics = pevent.Graphics

        'Fill the background
        Dim backcolor As Brush = New SolidBrush(Me.BackColor)
        gc.FillRectangle(backcolor, pevent.ClipRectangle)

        Dim pen1 As Pen
        Dim pen2 As Pen

            pen1 = Pens.Black
            pen2 = New Pen(Color.FromArgb(117, 128, 111))

        Dim x As Integer = 0
        Dim y As Integer = 0
        Dim height As Integer = Me.Height
        Dim width As Integer = Me.Width

        'pen1
        gc.DrawLine(pen1, x, y, x + width - 1, y)
        gc.DrawLine(pen1, x, y, x, y + height - 1)
        'pen2
        gc.DrawLine(pen2, x + width - 1, y, x + width - 1, y + height - 1) 'topright to bottomright
        gc.DrawLine(pen2, x, y + height - 1, x + width - 1, y + height - 1) 'bottomleft to bottomright
    End Sub
End Class

I also changed inside windows generated code to SteamTextBox. Nothing changes. Any ideas?
0
 
LVL 2

Expert Comment

by:wellilein
ID: 9677127
Ok, TextBox does not work for me, too. I inserted a Debug.Write command and it is never executed.

I found a website that says you can add
Sub New()
       SetStyle(ControlStyles.UserPaint, True)
End Sub
to tell the control that it is painted by you. This works, but it doesn't seem to be that easy. The characters are not deleted when pressing backspace.
0
 

Author Comment

by:Produkt
ID: 9677679
No big deal; thanks for the help!
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

This is about my first experience with programming Arduino.
Whether you’re a college noob or a soon-to-be pro, these tips are sure to help you in your journey to becoming a programming ninja and stand out from the crowd.
An introduction to basic programming syntax in Java by creating a simple program. Viewers can follow the tutorial as they create their first class in Java. Definitions and explanations about each element are given to help prepare viewers for future …
In this fourth video of the Xpdf series, we discuss and demonstrate the PDFinfo utility, which retrieves the contents of a PDF's Info Dictionary, as well as some other information, including the page count. We show how to isolate the page count in a…

708 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

13 Experts available now in Live!

Get 1:1 Help Now