none
Grafiken mit VB2010 ? RRS feed

  • Frage

  • Ich werd noch wahnsinnig weil ich im Web nichts vernünftiges zu Grafiken in VB2010 finde. Mein Problem ist, dass eine auf die Form gezeichnete Grafik beim Me.Refresh() gelöscht wird bzw. auch wenn aus versch. SUBs auf die Form gezeichnet wird. Wie kann ich sowas wie AutoRedraw() bei VB6 unter VB2010 erreichen?

    Nicht funktionierender Beispielcode einer analogen Uhr, wo ich versucht habe die Grafik zu speichern:
    (Es ist nur eine normale Form samt einem Timer erforderlich).
    ---

    Public Class Form1

    Public x0, y0, r, rs, rm, rh As Integer ' Mittenkoordinaten, Radien
    Public g As Graphics
    Public m_Face As Bitmap

    Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown

    DrawFace()
    Timer1.Interval = 200
    Timer1.Enabled = True

    End Sub

    Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint

    Dim s, m, h, x1, y1 As Integer ' Zeitwerte, Zeigerkoordinaten

    If m_Face Is Nothing Then Exit Sub

    ' Ziffernblatt
    e.Graphics.DrawImage(m_Face, 0, 0)

    s = Int(Math.PI / 30 * Now.Second) ' Winkel s
    m = Int(Math.PI / 30 * Now.Minute) ' Winkel m
    h = Int(Math.PI / 6 * Now.Hour) ' Winkel h
    ' Textlabel mit Uhrzeit
    ' LabelTime.Text = Now.ToString
    ' Pens generieren
    Dim hour As New Pen(Color.Blue)
    Dim minute As New Pen(Color.Red)
    Dim second As New Pen(Color.Black)
    hour.Width = 8
    minute.Width = 4
    second.Width = 1
    ' alte Zeiger löschen
    Dim pinsel As New SolidBrush(Me.BackColor)
    g.FillEllipse(pinsel, x0 - rs, y0 - rs, rs * 2, rs * 2)
    ' Zeiger zeichnen
    x1 = Int(x0 + rh * Math.Sin(h) + 0.5)
    y1 = Int(y0 - rh * Math.Cos(h) + 0.5)
    g.DrawLine(hour, x0, y0, x1, y1)
    x1 = Int(x0 + rm * Math.Sin(m) + 0.5)
    y1 = Int(y0 - rm * Math.Cos(m) + 0.5)
    g.DrawLine(minute, x0, y0, x1, y1)
    x1 = Int(x0 + rs * Math.Sin(s) + 0.5)
    y1 = Int(y0 - rs * Math.Cos(s) + 0.5)
    g.DrawLine(second, x0, y0, x1, y1)

    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

    Me.Invalidate()

    End Sub

    Private Sub DrawFace()

    ' Ziffernblatt zeichnen
    m_Face = New Bitmap(Me.ClientRectangle.Width, Me.ClientRectangle.Height)
    Dim g As Graphics = Graphics.FromImage(m_Face)
    Dim stifth As New Pen(Color.Black)
    Dim i As Integer

    x0 = Int(Me.ClientRectangle.Width / 2)
    y0 = Int(Me.ClientRectangle.Height / 2)
    r = IIf(x0 < y0, x0 * 0.9, y0 * 0.9) ' radius
    rs = r * 0.9
    rm = r * 0.8
    rh = r * 0.6
    stifth.Width = 5
    'g = Me.CreateGraphics
    For i = 0 To 360 Step 6 ' Minuten
    g.DrawPie(Pens.Black, x0 - r, y0 - r, r * 2, r * 2, i, 360)
    Next
    For i = 0 To 360 Step 30 ' Stunden
    g.DrawPie(stifth, x0 - r, y0 - r, r * 2, r * 2, i, 360)
    Next
    Dim pinsel As New SolidBrush(Me.BackColor)
    g.FillEllipse(pinsel, x0 - rs, y0 - rs, rs * 2, rs * 2)
    ' Ziffernblatt anzeigen
    Me.BackgroundImage = m_Face

    End Sub

    End Class

    Mittwoch, 8. Dezember 2010 17:09

Antworten

  • Hallo,

    Du solltest Dich zunächst auseinandersetzen mit Grafik und Zeichnen in Windows Forms
    Und eine Grundregel dabei beherzigen:
    Alle Grafikobjekte (Pen, Brushes etc.) sollten direkt nach der Verwendung freigegeben werden.

    Will man wie hier (einen Teil) des Hintergrunds wiederholt zeichnen, so kann man dazu die
    doppelte Pufferung benutzen, beschrieben unter: Verwenden der doppelten Pufferung

    Abseits davon solltest Du Dir angewöhnen mit Option Strict On zu arbeiten, dass vermeidet einige Fehler.

    Ich habe Deinen Code (auf die Schnelle) mal entsprechend umgebaut.

    Option Explicit On
    Option Infer On
    Option Strict On
    
    Public Class UhrForm
      Private m_Face As BufferedGraphics
      Private WithEvents Timer1 As New Timer()
    
      Public Sub New()
        ' Neuzeichnen bei Größenänderung und alles im Paint Ereignis
        Me.SetStyle(ControlStyles.ResizeRedraw Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint, True)
        ' Mindestgröße 
        Me.MinimumSize = New Size(120, 120)
    
        InitializeComponent()
      End Sub
    
      Private Sub Form_Resize(ByVal sender As Object, ByVal e As EventArgs) _
        Handles MyBase.Resize
        ' Hintergrundgröße geändert
        Me.DrawFace()
      End Sub
    
      Private Sub Form_Load(ByVal sender As Object, ByVal e As EventArgs) _
        Handles MyBase.Load
        Me.DrawFace()
        Timer1.Interval = 500
        Timer1.Enabled = True
      End Sub
    
      Private Sub Form_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) _
        Handles MyBase.Paint
    
        Dim now = DateTime.Now
        Dim second = Math.PI / 30.0 * now.Second
        Dim minute = Math.PI / 30.0 * now.Minute
        Dim hour = Math.PI / 6.0 * now.Hour
        ' Console.WriteLine("Paint {0} - {1} {2} {3}", now, hour, minute, second)
    
        Dim centerX = CInt(Me.ClientRectangle.Width / 2)
        Dim centerY = CInt(Me.ClientRectangle.Height / 2)
    
        Dim radius = CInt(Math.Min(centerX, centerY) * 0.9)
        Dim radiusHour = CInt(radius * 0.6)
        Dim radiusMinute = CInt(radius * 0.8)
        Dim radiusSecond = CInt(radius * 0.9)
    
        ' alte Zeiger löschen
        If m_Face IsNot Nothing Then
          m_Face.Render(e.Graphics)
        End If
    
        'Using brush As New SolidBrush(Me.BackColor)
        '  e.Graphics.FillEllipse(Brush, centerX - radiusSecond, centerY - radiusSecond, radiusSecond * 2, radiusSecond * 2)
        'End Using
    
        ' Zeiger zeichnen
        Dim x1 = CInt(centerX + radiusHour * Math.Sin(hour) + 0.5)
        Dim y1 = CInt(centerY - radiusHour * Math.Cos(hour) + 0.5)
        Using hourPen = New Pen(Color.Blue, 8)
          e.Graphics.DrawLine(hourPen, centerX, centerY, x1, y1)
        End Using
    
        x1 = CInt(centerX + radiusMinute * Math.Sin(minute) + 0.5)
        y1 = CInt(centerY - radiusMinute * Math.Cos(minute) + 0.5)
        Using minutePen As New Pen(Color.Red, 4)
          e.Graphics.DrawLine(minutePen, centerX, centerY, x1, y1)
        End Using
    
        x1 = CInt(centerX + radiusSecond * Math.Sin(second) + 0.5)
        y1 = CInt(centerY - radiusSecond * Math.Cos(second) + 0.5)
        Using secondPen As New Pen(Color.Black, 1)
          e.Graphics.DrawLine(secondPen, centerX, centerY, x1, y1)
        End Using
      End Sub
    
      Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Me.Invalidate()
      End Sub
    
      Private Sub DrawFace()
        ' Re-create the graphics buffer for a new window size.
        BufferedGraphicsManager.Current.MaximumBuffer = Me.Size
        If m_Face IsNot Nothing Then
          m_Face.Dispose()
          m_Face = Nothing
        End If
    
        m_Face = BufferedGraphicsManager.Current.Allocate(Me.CreateGraphics(), Me.ClientRectangle)
    
        Dim centerX = CInt(Me.ClientRectangle.Width / 2)
        Dim centerY = CInt(Me.ClientRectangle.Height / 2)
        Dim radius = CInt(Math.Min(centerX, centerY) * 0.9)
        Dim radiusSecond = CInt(radius * 0.9)
    
        ' Hintergrund
        Using brush = New SolidBrush(Me.BackColor)
          m_Face.Graphics.FillRectangle(brush, 0, 0, Size.Width, Size.Height)
        End Using
    
        If radius > 0 Then
          ' Minuten
          For i = 0 To 360 Step 6
            m_Face.Graphics.DrawPie(Pens.Black,
                centerX - radius, centerY - radius,
                radius * 2, radius * 2, i, 360)
          Next
          ' Stunden
          Using Pen As New Pen(Color.Black, 5)
            For i = 0 To 360 Step 30
              m_Face.Graphics.DrawPie(Pen,
                centerX - radius, centerY - radius, radius * 2, radius * 2, i, 360)
            Next
          End Using
    
          ' Sekunden
          Using brush As New SolidBrush(Me.BackColor)
            m_Face.Graphics.FillEllipse(brush,
                centerX - radiusSecond, centerY - radiusSecond,
                radiusSecond * 2, radiusSecond * 2)
          End Using
        End If
      End Sub
    
    End Class
    
    Ergänzt ist ein Resize, damit die Uhr sich anpasst, wenn die Formulargröße sich ändert.
    Auch ist findet mehr Fliesskomma-Arithmetik statt - sonst lahmt der Sekundenzeiger ;-)

    Gruß Elmar

    Donnerstag, 9. Dezember 2010 09:46
    Beantworter