• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1798
  • Last Modified:

Flat/Raised Buttons in VB.NET

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
Produkt
Asked:
Produkt
  • 16
  • 12
  • 3
  • +1
1 Solution
 
AlexFMCommented:
Create Checkbox with Appearance = Button, FlatStyle = System. Maybe this object behaves as you need.
0
 
Arthur_WoodCommented:
change the FlatStyle property of the Command Button to Flat.  The default is Standard.

AW
0
 
ProduktAuthor Commented:
Here, I took a screenshot of what it should look like:
http://distortnet.hypermart.net/buttonstyle.JPG
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
ProduktAuthor Commented:
Here, I took a screenshot of what it should look like:
http://distortnet.hypermart.net/buttonstyle.JPG
0
 
ProduktAuthor Commented:
Here, I took a screenshot of what it should look like:
http://distortnet.hypermart.net/buttonstyle.JPG
0
 
ProduktAuthor Commented:
Here, I took a screenshot of what it should look like:
http://distortnet.hypermart.net/buttonstyle.JPG
0
 
AlexFMCommented:
So, my suggestion is not what you want?
0
 
ProduktAuthor Commented:
Nope.
0
 
Arthur_WoodCommented:
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
 
ProduktAuthor Commented:
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
 
wellileinCommented:
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
 
wellileinCommented:
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
 
Arthur_WoodCommented:
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
 
ProduktAuthor Commented:
Look at the image I provided. You'll see exactly how it should look.
0
 
ProduktAuthor Commented:
Would it be easier just to draw the buttons and use images for this?
0
 
wellileinCommented:
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
 
wellileinCommented:
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
 
ProduktAuthor Commented:
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
 
wellileinCommented:
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
 
ProduktAuthor Commented:
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
 
wellileinCommented:
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
 
wellileinCommented:
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
 
ProduktAuthor Commented:
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
 
wellileinCommented:
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
 
ProduktAuthor Commented:
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
 
ProduktAuthor Commented:
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
 
wellileinCommented:
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
 
wellileinCommented:
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
 
ProduktAuthor Commented:
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
 
wellileinCommented:
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
 
ProduktAuthor Commented:
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
 
wellileinCommented:
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
 
ProduktAuthor Commented:
No big deal; thanks for the help!
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

  • 16
  • 12
  • 3
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now