[HowTo]Aero-Form mit Non-Client Controls

    • VB.NET

    Es gibt 31 Antworten in diesem Thema. Der letzte Beitrag () ist von Björn.

      [HowTo]Aero-Form mit Non-Client Controls

      Da in letzter Zeit des Öfteren nach Aero-Design und Controls in der Non-Client Area (Titelbar) gefragt wird, habe ich beschlossen, ein Tutorial dazu zu schreiben.


      Grundlagen
      Im Grunde genommen ist jede WinForm in zwei Bereiche unterteilt. In einen Client-Bereich (Den Bereich, den man im Designer mit Controls, etc. gestalten kann) und
      in einen Non-Client Bereich (Titelleiste; Der Programmierer kann nicht ohne Weiteres darauf zugreifen).
      Um überhaupt in die Titelleiste zeichnen zu können, muss der Client Bereich der Form erweitert werden.
      Im Allgemeinen wird dazu die 'DWMAPI.dll' benötigt.


      Grundcode
      Um die Form zu erweitern und die Möglichkeit zu bieten, Controls in die Titelleiste zu zeichnen, greife ich auf folgenden Code zurück: [vb@rchiv]Toolstrip-Elemente in der Titelleiste
      Mit diesem Code wird der obere Teil (ca. 25 Pixel) der Form beim starten automatisch in die Titelleiste eingefügt. D.h. die oberen 25 Pixel der Form können ganz normal im Designer gestaltet werden und werden beim Start in der Titelleiste angezeigt.
      Im Beispiel des Grundcodes ist ein ToolStrip genommen worden. Fügt euch ebenfalls ein (leeres) ToolStrip mit dem Namen tsNCToolStrip ein, damit es keine Fehler gibt, wenn der Grundcode kopiert wird (auf welchen ich jetzt nicht mehr näher eingehen werde).

      Ich erkläre euch jedoch, wie man sich einen HeaderMenuButton (wie in Firefox 4) machen kann.


      Design
      Dazu müssen wir zuerst ein Bild des Buttons haben und zwar in 3-facher Ausführung: Normal, Hover, Pressed (Evtl. noch ein 4. für Disabled)
      Diese 3 Bilder habe ich hier mal in der Farbe grün angehängt. Ihr solltet diese Bilder in die Ressourcen des Programms importieren.


      Nun muss eine Picturebox mit folgenden Eigenschaften erstellt werden:
      Location: 6;1
      Size: 88; 19
      Image: Das Bild vom "Normalzustand" des Buttons

      Nun könnt ihr das Programm mal starten. Ihr solltet nun folgendes sehen:

      Wie ihr sicher bemerkt habt, wird kein Form Text angezeigt, selbst wenn ihr einen angegeben habt.
      Das ist auch klar, weil die ganze Titelleiste überschrieben wird. Also muss der Text ebenfalls selber gezeichnet werden (Mit Glass-/Glow-Effekt). Da kann ich euch das GlassLabel-Control von diesem Toolkit empfehlen.
      Das GlassLabel sollte folgende Eigenschaften haben:
      Location: [(Breite der Form - Breite des Labels) / 2]; -6
      Size: 56; 23
      Padding: 10; 10; 10; 0


      Code
      Damit der Text mittig angezeigt wird, muss folgender Code ins Size_Changed Event der Form:

      VB.NET-Quellcode

      1. GlassLabel1.Location = New Point((Me.Width - GlassLabel1.Width) / 2, 0)


      Damit der Button bei einer Mausberührung heller wird (Hover) und bei einem Mausklick dunkler (Pressed), müssen folgende Codeteile eingefügt werden:

      VB.NET-Quellcode

      1. Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
      2. PictureBox1.Image = My.Resources.HeaderGreen_pressed
      3. End Sub
      4. Private Sub PictureBox1_MouseEnter(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureBox1.MouseEnter
      5. PictureBox1.Image = My.Resources.HeaderGreen_hover
      6. End Sub
      7. Private Sub PictureBox1_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureBox1.MouseLeave
      8. PictureBox1.Image = My.Resources.HeaderGreen_normal
      9. End Sub
      10. Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
      11. PictureBox1.Image = My.Resources.HeaderGreen_normal
      12. End Sub
      Wobei "PictureBox1" der Button ist.

      Desweiteren kann man sehen, dass die Position der Controls bei der maximierten Form nicht mehr stimmen. Deshalb muss bei einem Resize noch geprüft werden, ob die Form maximiert wurde, um ggf. die Position des Buttons und des Labels anzupassen:

      VB.NET-Quellcode

      1. Private Sub Form2010_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.SizeChanged
      2. GlassLabel1.Location = New Point((Me.Width - GlassLabel1.Width) / 2, 0)
      3. If Me.WindowState = FormWindowState.Maximized Then
      4. PictureBox1.Location = New Point(9, 6)
      5. GlassLabel1.Location = New Point((Me.Width - GlassLabel1.Width) / 2, 2)
      6. GlassLabel1.ForeColor = Color.White
      7. ElseIf Me.WindowState = FormWindowState.Normal Then
      8. PictureBox1.Location = New Point(6, 1)
      9. GlassLabel1.Location = New Point((Me.Width - GlassLabel1.Width) / 2, 0)
      10. GlassLabel1.ForeColor = Color.Black
      11. End If
      12. End Sub

      Beim Label wird zusätzlich noch die Schriftfarbe geändert (Wie bei der originalen Form).

      Nun wollen wir noch, dass sich beim Ändern des Formtextes auch der Text auf dem Glasslabel anpasst (Der Glasslabeltext wird auf der Form gezeigt, der Formtext in der Taskbar)

      VB.NET-Quellcode

      1. Private Sub Form2010_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.TextChanged
      2. GlassLabel1.Text = Me.Text
      3. End Sub



      Text auf dem Button/Menu beim Buttonklick
      Um einen Text auf den Button zuschreiben, muss auf GDI zurückgegriffen werden:

      VB.NET-Quellcode

      1. Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
      2. e.Graphics.DrawString("Button", New Font("Segoe UI", 8.75, FontStyle.Bold), Brushes.White, 7, 0) 'ACHTUNG! Die Location kann entweder ausprobiert werden, damit der Text in der Mitte ist, oder man kann die MeasureString Funktion verwenden, worauf ich hier aber verzichtet habe
      3. End Sub


      Um ein Menu beim Buttonklick aufpoppen zu lassen, kann ein einfaches ContextMenu verwendet werden.

      VB.NET-Quellcode

      1. Private Sub PictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox1.Click
      2. ContextMenuStrip1.Show(PictureBox1, New Point(0, PictureBox1.Height))
      3. End Sub



      Ich hoffe, dieses etwas längere Tutorial ist einigen von euch eine kleine Hilfe.
      Ich bedanke mich schon einmal fürs Anschauen und durchlesen und würde mich über Kritik/Rückmeldungen freuen :)

      Zum Schluss sollte die Form etwa so aussehen:



      EDIT: Es gibt einen bekannten Bug, welchen ich nicht beheben konnte, nämlich, dass die 3 Buttons (Minimize, Maximize und Close) nicht angezeigt werden, wenn die Form maximiert ist.



      Gruss
      Pascal

      Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Marcus Gräfe“ ()

      Ja klar, denn die ganze Client-Area wird sozusagen nach oben geschoben und überschreibt jetzt die Standart-Titelleiste ;)
      P.S. Ich kann das Bild des Designers nicht einmal sehen, beim anderen ist der Link kaputt... Erledigt :)
      Du darfst in diesem Fall wie ich das auf dem Bild sehe nicht die Dock-Eigenschaft benutzen, sondern musst die Position von Hand anpassen und dann mit der Anchor-Eigenschaft arbeiten.


      Gruss
      Pascal

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „PascalN“ ()



      mach ich was falsch weil bei mir kommt nur das raus
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Option Explicit On
      2. Option Strict On
      3. Imports System.Runtime.InteropServices
      4. Imports System.Drawing.Drawing2D
      5. Imports System.Drawing.Printing
      6. Public Class form1
      7. ' DLL-API-Importe
      8. <DllImport("dwmapi.dll")> _
      9. Public Shared Function DwmDefWindowProc(ByVal hwnd As IntPtr, _
      10. ByVal msg As Integer, _
      11. ByVal wParam As IntPtr, _
      12. ByVal lParam As IntPtr, _
      13. <System.Runtime.InteropServices.Out()> ByRef result As IntPtr) As Integer
      14. End Function
      15. <DllImport("dwmapi.dll")> _
      16. Public Shared Function DwmIsCompositionEnabled( _
      17. ByRef pfEnabled As Integer) As Integer
      18. End Function
      19. <DllImport("dwmapi.dll")> _
      20. Public Shared Function DwmExtendFrameIntoClientArea( _
      21. ByVal hdc As IntPtr, _
      22. ByRef marInset As Margins) As Integer
      23. End Function
      24. Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
      25. PictureBox1.Image = My.Resources.HeaderGreen_pressed
      26. End Sub
      27. Private Sub PictureBox1_MouseEnter(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureBox1.MouseEnter
      28. PictureBox1.Image = My.Resources.HeaderGreen_hover
      29. End Sub
      30. Private Sub PictureBox1_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureBox1.MouseLeave
      31. PictureBox1.Image = My.Resources.HeaderGreen_normal
      32. End Sub
      33. Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
      34. PictureBox1.Image = My.Resources.HeaderGreen_normal
      35. End Sub
      36. Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
      37. e.Graphics.DrawString("Button", New Font("Segoe UI", 8.75, FontStyle.Bold), Brushes.White, 7, 0) 'ACHTUNG! Die Location kann entweder ausprobiert werden, damit der Text in der Mitte ist, oder man kann die MeasureString Funktion verwenden, worauf ich hier aber verzichtet habe
      38. End Sub
      39. Private Sub PictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox1.Click
      40. ContextMenuStrip1.Show(PictureBox1, New Point(0, PictureBox1.Height))
      41. End Sub
      42. Private Sub form1_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.SizeChanged
      43. If Me.WindowState = FormWindowState.Maximized Then
      44. PictureBox1.Location = New Point(9, 6)
      45. ElseIf Me.WindowState = FormWindowState.Normal Then
      46. PictureBox1.Location = New Point(6, 1)
      47. End If
      48. End Sub
      49. End Class
      Hi zusammen

      Auf Wunsch von Darkronight habe ich ein Demoprojekt erstellt, welches euch eine Anwendung des Codes näherbringen soll.


      Gruss
      Pascal
      Dateien
      • Form2010_Demo.zip

        (365,34 kB, 677 mal heruntergeladen, zuletzt: )

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „PascalN“ ()