Laden...

Seltsame grafische Artefakte bei eigenem virtualisiertem Canvas

Erstellt von Astrix vor 11 Jahren Letzter Beitrag vor 11 Jahren 769 Views
A
Astrix Themenstarter:in
12 Beiträge seit 2012
vor 11 Jahren
Seltsame grafische Artefakte bei eigenem virtualisiertem Canvas

Hallo zusammen,

ich habe mir für eine WPF-Anwendung unter dem .NET Framework 4.0 eine Klasse erstellt die von Canvas erbt.

Ich möchte auf einem Canvas eine (in gewissem Maße unbestimmte) Anzahl vom Anwender frei definierbarer "Plugins" darstellen können (jedes Plugin wiederum ist einfach ein WPF-Control welches von FrameworkElement ableitet).

Dabei stellt meine Canvas-Klasse eine gewisse Form von grafischer Virtualisierung bereit, denn es werden nur die Children-Elemente bei einem Mouse-Panning-Ereignis auf dem Canvas dargestellt die sich auch wirklich im sichtbaren Bereich befinden.

So klappt das eigentlich auch super, auf meinem Laptop funktioniert es mit weit über 250.000 Elementen immer noch recht flüssig (soviele habe ich in der Anwendung selbstverständlich nicht, da sind es eher so im "Worst-Case" 200 Elemente).

Mit einem gehaltenen Mausklick auf dem Canvas und Ziehen der Maus kann ich jetzt durch den gesamten "virtuellen" Canvas navigieren ("Panning").

Doch bewege ich die Maus dabei recht schnell kommt es zu seltsamen Artefakten in der Grafikdarstellung (und seltsamerweise in sämtlichen anderen Controls die ich in meiner Main-View definiere!).

Dazu habe ich hier einmal einen Screenshot angehängt. Bewege ich die Maus relativ langsam treten diese Bildfehler jedoch überhaupt nicht auf, es passiert erst wenn ich die Maus recht schnell hin- und herschiebe). Darum vermute ich, dass ich irgendwo in meiner Klasse einen Denkfehler habe, doch finde ich diesen einfach nicht.

Vielleicht hat ja jemand von euch einen Tipp was ich hier falsch mache?

PS: der Code ist in VisualBasic.Net programmiert und leider nicht in C# übersetzt, darum hoffe ich, dass dies auch okay ist für euch.

Imports MeinProjekt.Plugins
Imports MeinProjekt.ViewModels

Imports System.Windows.Input

Namespace Views.Controls

    ''' <summary>
    ''' Stellt Methoden für einen vereinfachten virtualisierten Canvas bereit.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class Desktop
        Inherits Canvas

        ''' <summary>
        ''' Liste der Plugins (ToDo: Dependency-Property).
        ''' </summary>
        ''' <remarks></remarks>
        Private ReadOnly _Plugins As New List(Of Plugin)

        ''' <summary>
        ''' Panning-Offset X für das Scrolling durch den Desktop.
        ''' </summary>
        ''' <remarks></remarks>
        Private _ViewPortOffsetX As Double = 0

        ''' <summary>
        ''' Panning-Offset Y für das Scrolling durch den Desktop.
        ''' </summary>
        ''' <remarks></remarks>
        Private _ViewPortOffsetY As Double = 0

        ''' <summary>
        ''' Gibt zurück ob der Panning-Modus aktiv ist oder legt dies fest.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Property IsPanning As Boolean
            Get
                Return _IsPanning
            End Get
            Set(value As Boolean)
                If value Then
                    Cursor = Cursors.SizeAll
                Else
                    Cursor = Cursors.Arrow
                End If
                _IsPanning = value
            End Set
        End Property
        Private _IsPanning As Boolean = False

        ''' <summary>
        ''' Enthält die letzte Mausposition beim Panning.
        ''' </summary>
        ''' <remarks></remarks>
        Private _MousePosOnPanningStart As New Point(0, 0)

        Protected Overrides Sub OnMouseDown(e As MouseButtonEventArgs)
            MyBase.OnMouseDown(e)
            If e IsNot Nothing Then
                If e.LeftButton = MouseButtonState.Pressed Then
                    If Not _IsPanning Then
                        Mouse.Capture(Me)
                    End If
                    _MousePosOnPanningStart = e.GetPosition(Me)
                End If
                IsPanning = True
            End If
        End Sub

        Protected Overrides Sub OnMouseUp(e As MouseButtonEventArgs)
            MyBase.OnMouseUp(e)
            If e IsNot Nothing Then
                If e.LeftButton = MouseButtonState.Released Then
                    If _IsPanning Then
                        ReleaseMouseCapture()
                    End If
                    Cursor = Cursors.Arrow
                    IsPanning = False
                End If
            End If
        End Sub

        Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
            MyBase.OnMouseMove(e)
            If e IsNot Nothing Then
                If _IsPanning Then
                    Dim pos As Point = e.GetPosition(Me)
                    _ViewPortOffsetX += (_MousePosOnPanningStart.X - pos.X)
                    ValueLimiter.Limit(_ViewPortOffsetX, Double.MinValue, Double.MaxValue)
                    _ViewPortOffsetY += (_MousePosOnPanningStart.Y - pos.Y)
                    ValueLimiter.Limit(_ViewPortOffsetY, Double.MinValue, Double.MaxValue)
                    _MousePosOnPanningStart = e.GetPosition(Me)
                    AddViewPortChildren()
                End If
            End If
        End Sub

        Protected Overrides Sub OnRenderSizeChanged(sizeInfo As System.Windows.SizeChangedInfo)
            MyBase.OnRenderSizeChanged(sizeInfo)
            AddViewPortChildren()
        End Sub

        Private Sub AddViewPortChildren()
            Children.Clear()
            ' Nur die Plugins im Canvas darstellen die sich im sichtbaren Bereich befinden.
            ' ToDo: Abfragen zusammenfassen, hier nur zum testen separiert.
            For Each p As Plugin In _Plugins
                If p.Top + p.Height >= _ViewPortOffsetY Then
                    If p.Top <= _ViewPortOffsetY + RenderSize.Height Then
                        If p.Left + p.Width >= _ViewPortOffsetX Then
                            If p.Left <= _ViewPortOffsetX + RenderSize.Width Then
                                Children.Add(p)
                                SetLeft(p, p.Left - _ViewPortOffsetX)
                                SetTop(p, p.Top - _ViewPortOffsetY)
                            End If
                        End If
                    End If
                End If
            Next
        End Sub

        Public Sub New()
            Background = CType(FindResource("DesktopLightningY"), Brush)
            ' Mangels Dependency-Properties werden hier einfach zum Testen der Klasse 1000 Testplugins erstellt.
            For i As Integer = 0 To 999
                Dim p As New Plugin
                p.Width = 200
                p.Height = 150
                p.Left = 0 + i * 255
                p.Top = 0 + i * 55
                _Plugins.Add(p)
            Next i
            AddViewPortChildren()
        End Sub

    End Class

End Namespace

Im Screenshot stellt jedes blaue Rechteck ein Element von "Plugin" dar. Die rot-umrandeten Regionen zeigen die Bildfehler wenn ich die Maus beim "Panning" sehr schnell bewege.

Vielen lieben Dank für alle Antworten im voraus. Wenn ich mehr Infos bereitstellen soll sagt mir bitte bescheid.

Viele Grüße
Astrix

925 Beiträge seit 2004
vor 11 Jahren

Hmm... könnte sowas sein wie die Artefakte bei Games, die auftreten, wenn man VSync deaktiviert hat und schnelle Bewegungen ausführt... ist aber nur eine sehr unbegründete Vermutung.