Am Markt gibt es einige Pakete für die Datenvisualisierung mittels Diagrammen, die auch gewisse Animationen gestatten. Uns ist das Paket Microsoft Charts für WindowsForms und Web kostenlos gegeben. Und für WindowsForms-Lösungen habe ich die im folgenden beschriebenen Funktionen zur Animation von Datapoints mit "Bordmitteln" entwickelt. Es ist als ein erster Ansatz zu sehen, der mit etwas Fantasie eingesetzt und möglicherweise noch weiter entwickelt werden kann. Die gezeigte Prozedur benötigt folgende Prämissen: Für die YValues benötigt man zwei Arrays, eines mit den Endwerten und eines mit der gleichen Anzahl Null-Werten. Die Animation ist dann so gelöst, dass im Null-Werte-Array für jeden XPoint die Schrittweite addiert wird, bis der zugehörige Endwert erreicht wird. Aus dem XValue-Array und dem berechneten YValue-Array wird die neue Serie mitteles DataBindXY gebundenund die Chart upgedatet. Nach dem Update wird eine Minimalpause eingelegt. Dieser Prozess wird so lange ausgeführt, bis alle DataPoints die YValue-Endwerte erreicht haben. Die Prozedur stellt verschiedene Möglichkeiten der Animation zur Verfügung, was aus den Parameterangaben ersichtlich ist. Hier erst mal die Prozedur: ''' <summary> ''' Animation von Charts ''' </summary> ''' <param name="theChart">die Chart</param> ''' <param name="theSerie">die zu animierende Serie</param> ''' <param name="anzXValues">die Anzahl der XValues</param> ''' <param name="theSteps">die Schrittweite der YValues bei der Animation</param> ''' <param name="xvField">das Feld mit den XValues</param> ''' <param name="yvField">das Feld mit den YValues</param> ''' <param name="pSec">die Zeitspanne für die Pause während der Animation</param> ''' <param name="animArt">die Animationsart (0..einzelnes oder 1..gleichzeitiges Wachsen</param> ''' <param name="direction">die Richtung der Animation (0..von links, 1..von rechts</param> ''' <param name="clearing">sollen die Serienpunkte gelöscht werden, ja|nein</param> Public Sub AnimateCharts(theChart As Chart, theSerie As Object, anzXValues As Short, theSteps As Object, _ xvField As Object, yvField As Object, _ pSec As Single, Optional animArt As Short = 0, _ Optional direction As Short = 0, _ Optional clearing As Boolean = False) Dim w As Boolean Dim anfang, ende, schritt As Short anfang = 0 : schritt = 1 : ende = anzXValues - 1 If direction = 1 Then ' Animation von rechts anfang = anzXValues - 1 : ende = 0 : schritt = -1 End If ' ein Feld für alle Datapoint-Values = 0 Dim iniField(anzXValues - 1) As Object iniField.Initialize() ' ein Feld für die XValues, ein Feld für die YValues Dim xField(anzXValues - 1), yField(anzXValues - 1) As Object ' die Felder mit Daten aus Dataview füllen For j As Short = 0 To anzXValues - 1 xField(j) = xvField(j) yField(j) = yvField(j) Next With theChart If clearing Then ' Series.Points löschen .Series(theSerie).Points.Clear() ' Series.Points mit den aktuellen Xvalues und den YValues=0 verbinden .Series(theSerie).Points.DataBindXY(xField, iniField) ' Chart updaten .Update() End If ' Animation gemäß gewünschter Art Select Case animArt Case 0 ' die Animation ausführen für alle XValues ' Art: einen Datapoint nach dem anderen gemäß der XValues-Reihenfolge animieren ' (einzelnes Wachsen) For i As Short = anfang To ende Step schritt Do iniField(i) += theSteps ' Wert aus dem Null-YValues-Feld um Schrittweite erhöhen If theSteps < 0 Then w = (iniField(i) + theSteps <= yField(i)) Else w = (iniField(i) + theSteps >= yField(i)) End If ' Abfrage, ob aktueller YValue den endgültigen YValue erreicht hat If w Then iniField(i) = yField(i) ' den Null-YValue auf den endgültigen Wert setzen ' Series.Points nun mit Null-YValue-Feld verbinden .Series(theSerie).Points.DataBindXY(xField, iniField) ' Chart updaten .Update() ' Pause einlegen bis zur nächsten Wertberechnung Delay(pSec) If w Then Exit Do ' Schleife verlassen Loop Next Case 1 ' die Animation ausführen für alle XValues ' Art: alle Datapoints gleichzeitig animieren, bei 0 beginnend bis zum jeweiligen Endpunkt ' (gleichzeitiges Wachsen) Dim dmax(yField.Length - 1) As Short ' Feld für Registrieren, ob Endpunkte erreicht dmax.Initialize() ' Schleife für die Animation Do For i As Short = anfang To ende Step schritt If dmax(i) = 0 Then iniField(i) += theSteps If theSteps < 0 Then w = (iniField(i) <= yField(i)) Else w = (iniField(i) >= yField(i)) End If If w Then dmax(i) += 1 Else dmax(i) = 0 End If Next .Series(theSerie).Points.DataBindXY(xField, iniField) .Update() Delay(pSec) ' sind alle Endpunkte erreicht? Dim x As Boolean = Array.TrueForAll(dmax, AddressOf NoZeroValues) If x Then Exit Do ' alle Endpunkte erreicht Loop End Select End With End Sub Und gleich noch die benötigten Hilfsfunktionen: 1. Delay für das Erzeugen einer "Pseudo-Pause" ' eine Pause erzeugen Public Function Delay(ByVal seconds As Single) As Single Dim StartTime As DateTime = DateTime.Now Do ' Nichts tun. Loop While DateTime.Now.Subtract(StartTime).TotalSeconds < seconds Return seconds End Function 2. NoZeroValues als Predicate(Of T) für die Array-Funktion Array.TrueForAll Private Function NoZeroValues(v As Short) As Boolean Return (v > 0) End Function 3. MaxMinOfArray zum Ermitten des Maximums|Minimums der Werte eines Array ''' <summary> ''' Ermitteln von Maximum/Minimum der Werte eines Feldes ''' </summary> ''' <param name="theArray">das zu durchsuchende Feld</param> ''' <param name="flag">True für Maximum, False für Minimum</param> ''' <returns>den Wert (max oder min)</returns> Public Function MaxMinOfArray(ByVal theArray As Array, _ Optional ByVal flag As Boolean = True) As Object Dim max = theArray(0) For i As Integer = 0 To theArray.Length - 1 If flag Then If theArray(i) > max Then max = theArray(i) ' Maximum End If Else If theArray(i) < max Then max = theArray(i) ' Minimum End If End If Next i Return max End Function Anwendung der Prozedur: Dim delSec As Single = 0.02 Mein Beispiel hier geht von zwei Serien aus, die dargestellt werden sollen und zeige gleichzeitig, wie man die benötigten Felder aus DataViews erstellt. Dim xdv1(dv1.Count - 1), ydv1(dv1.Count - 1), ydv2(dv2.Count - 1) As Object For j As Short = 0 To dv1.Count - 1 xdv1(j) = dv1.Item(j)("Jahr") ydv1(j) = dv1.Item(j)("Betrag") ydv2(j) = dv2.Item(j)("Stunden") Next Dabei sind
und es werden die Felder erstellt
Für jede Serie ermittle ich eine passende Schrittweite. Dim stp1 As Single = MaxMinOfArray(ydv1) * delSec Dim stp2 As Single = MaxMinOfArray(ydv2) * delSec Und definiere zwei Felder für die anfänglichen Null-YValues je Serie: Dim in1(dv1.Count - 1) As Single Dim in2(dv2.Count - 1) As Short Jetzt erfolgt der Aufruf der Prozedur: AnimateCharts(Chart1, "Umsatz", dv1.Count, MaxMinOfArray(ydv1) * delSec, _ xdv1, ydv1, delSec, , CShort(Rnd())) AnimateCharts(Chart1, "Stunden", dv2.Count, CShort(MaxMinOfArray(ydv2) * delSec), _ xdv1, ydv2, delSec, , CShort(Rnd())) CShort(Rnd()) setze ich hier ein für eine zufällige Animationsart ein... Wie löst man die Animation aus? Nun wünsche ich Spaß beim Experimentieren und Ausprobieren! |