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
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.