Diagramme für Windows Phone 7 mit der Silverlight-Charting-Komponente

Charts mobil
Kommentare

Eigentlich ist es unglaublich, aber trotzdem wahr. Das Lesen einfacher Charts stellt die Mehrheit der Aufnahmekandidaten der oberösterreichischen IT-Ausbildungsstätte FH Hagenberg vor unlösbare Probleme – ein Fünftel des Aufnahmetests besteht nur aus solchen Aufgaben, über die jeder Praktiker lacht.

Leider ist das kein spezifisch hagenbergisches Problem – in vielen Ländern bereitet das Verstehen von grafischen Visualisierungen Probleme. Das ist insofern bedauerlich, als dass Charts oftmals die effizienteste Form der Datenvisualisierung darstellen – die Übersichtlichkeit einer Datentabelle steigt sofort um einige Zehnerpotenzen, wenn man sie grafisch aufbereitet. Früher entstanden derartige Charts im Mobilbereich von Hand: Man schnappte sich einen GDI Handle und renderte mit mehr oder minder primitiver Mathematik munter drauflos.

Charts für Faule

Die Logik besagt, dass eine immer wieder zu lösende Problemstellung irgendwann in Form einer Bibliothek kommerzialisiert wird. Mittlerweile buhlt eine Vielzahl von kommerziellen Libraries um die Gunst der Entwickler. Das Silverlight Toolkit von Microsoft ist an sich als Erweiterung für die Browser-Runtime gedacht. Die in der Version für Silverlight 4.0 enthaltene Charting-Komponente lässt sich mit einigen Tricks auch auf dem Windows Phone verwenden.

Installatives

Da die Silverlight Runtime des Windows Phone 7 auf Silverlight 3.0 basiert, müssen die Assemblies aus dem Toolkit ein wenig angepasst werden, bevor man sie am Telefon verwenden kann. Erfreulicherweise hat uns der MSDN-Blogger David Anson diese lästige Arbeit abgenommen – er bietet unter [1] eine Datei namens DataVisualizationOnWindowsPhone.zip an, die die beiden Assemblies schon fertig adaptiert enthält. Danach erstellen wir uns eine neue Silverlight Application for Windows Phone 7. Auf der CD heißt das Beispiel MTChart2. Nachdem der Wizard seine Arbeit getan hat, müssen wir die zwei aus der .zip-Datei entnommenen Bibliotheken hinzufügen. Die von der IDE daraufhin angezeigte Warnung wird mit einem Klick auf YES quittiert, da sie für uns irrelevant ist. Damit ist die Integration auch schon erledigt. Die Steuerelemente des Toolkits erscheinen in der aktuellen Version von Visual Studio für Windows Phone 7 übrigens nicht mehr in der Toolbar des Designers – die IDE hält sie nämlich für inkompatibel, was im Prinzip ja auch richtig ist. 

Aufmacherbild: Color Swatch Isolated with Clipping Paths von Shutterstock / Urheberrecht: Dario Sabljak

[ header = Seite 2: Erste Schritte in Chart-Land ]

Erste Schritte in Chart-Land

Doch damit erst einmal genug der Einleitung – das erste Diagramm will erstellt sein. Dazu öffnen wir die .xaml-Datei des Hauptformulars und erweitern sie um einen Namespace:

xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"

Das ist insofern notwendig, als es uns das Referenzieren des Chart-Steuerelements wesentlich erleichtert. Im nächsten Schritt erweitern wir das Main Form um ein leeres Chart-Fenster, indem wir es in das vom Wizard erstellte Content-Grid einschreiben:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <charting:Chart Name="myChart" LegendTitle="Legende">
    </charting:Chart>
</Grid> 

Um ein Chart zu erstellen, brauchen wir logischerweise Daten. Beim absolute Werte darstellenden Tortendiagramm benötigen wir die Mächtigkeit des dem Element zugeordneten Anteils der Gesamtmenge und den Namen des Elements. Zur „Datenerhaltung“ verwendet das Beispiel eine eher simple Klasse (Listing 1).

public class PieInfo {
    public string Name { get; set; }
    public int Count { get; set; }
}
// Im Konstruktor des Hauptformulars beleben wir unser Diagramm
public MainPage() {
    InitializeComponent();
    
    List<PieInfo> myPieInfoList=new List<PieInfo>(0);
    myPieInfoList.Clear();
    PieInfo p=new PieInfo();
    p.Name="A";
    p.Count=25;
    myPieInfoList.Add(p);
    p=new PieInfo();
    p.Name="B";
    p.Count=50;
    myPieInfoList.Add(p);

    PieSeries myPieSeries=new PieSeries();
    myPieSeries.ItemsSource = myPieInfoList;
    myPieSeries.DependentValuePath = "Count";
    myPieSeries.IndependentValuePath = "Name";
    myChart.Series.Add(myPieSeries);
      
} 

Im ersten Teil von Listing 1 erstellen wir uns zwei Datenpunkte, die gemeinsam das Diagramm realisieren. Im zweiten Teil beginnt die eigentliche Arbeit mit dem Erstellen eines Objekts vom Typ PieSeries. Im Silverlight Toolkit baut man Charts aus einzelnen „Datenserien“ zusammen, die jeweils einen Datenstrom darstellen. Da Tortendiagramme nur aus einer Torte bestehen können, brauchen wir auch nur ein PieSeries-Objekt. Dieses bekommt als Data Source die im ersten Teil von Hand erstellte Liste zugewiesen. Die Eigenschaften DependentValuePath und IndependentValuePath legen dabei fest, wie die zu befragenden Eigenschaften des Datenelements heißen. Zuletzt fügen wir das Item zum Diagramm hinzu. Ausgeführt sieht das Programm wie in Abbildung 1 und 2 aus.

Abb. 1: MTCharts2 in einem dunklen...

Abb. 2: ...und in einem hellen Theme ausgeführt

[ header = Seite 3: Style mich! ]

Style mich!

Hätten alle Windows-Phone-7-Geräte nur das helle Theme eingebaut, wäre dieser Artikel hier zu Ende. Leider verbauen sowohl Samsung als auch Nokia sehr gerne AMOLED-Bildschirme, die bei dunklem Hintergrund weniger Strom verbrauchen und deshalb meist in dieser Einstellung betrieben werden.

Unser Chart-Steuerelement kommt vom Desktop, wo OLED-Technologie aufgrund diverser Probleme (Knappheit, Burn-in) noch nicht wirklich Fuß fassen konnte. Da Röhrenmonitore seit Jahren ausgestorben sind, rechnen die Entwickler des Controls nicht mit einer „Low-key-Palette“. Die Lösung für dieses Problem besteht im Definieren eigener Templates, die die Anzeige der Widgets anpassen. Dazu sollte man – insbesondere bei kleineren Anpassungen – zu Expression Blend greifen. Im schlimmsten Fall verliert man damit im Vergleich zum „wilden Kopieren“ von XAML einige Minuten. Geht die Kopierorgie hingegen wie in den meisten Fällen schief, landet man am Ende sowieso wieder bei der kostenlosen IDE-Beigabe von Microsoft.

Nach dem Start von Expression Blend öffnen wir das Projekt MTChart3 und navigieren danach in die Datei MainPage.xaml. Sie erscheint im Editorfenster. Unten rechts wird ihre Struktur in der Rubrik Objects and Timelines eingeblendet. Im dort erscheinenden Bayum expandieren wir nacheinander die Elemente LayoutRoot und Content Panel. Danach klicken wir das Objekt myChart rechts an und wählen im Kontextmenü EDIT ADDITIONAL TEMPLATES | EDIT LEGEND STYLE | EDIT A COPY. Im nächsten Schritt zeigt Expression Blend den Zielwahldialog an. In ihm können wir festlegen, wohin die von der IDE generierten XAML-Codeblöcke gespeichert werden sollen. Da das Programm in der Regel sehr lange Suaden generiert, empfiehlt sich die Auslagerung aller generieren XAML-Konstrukte in eine eigene Datei. Dazu klicken wir im Dialog auf die Schaltfläche NEW, geben einen Dateinamen ein und klicken auf OK. Im ursprünglichen Dialog angekommen, klicken wir ebenfalls auf OK und erfreuen uns am in Abbildung 3 gezeigten Style.

Abb. 3: Die „Default-Einstellungen“ sind Style geworden

Im nächsten Schritt selektieren wir unten links im Baum das Element HeaderContent. Im Fenster rechts können wir dann in der Rubrik Brushes festlegen, welche Pinsel zum Rendern der angegebenen Bereiche verwendet werden sollen. Es empfiehlt sich, für Foreground den Brush PhoneForegroundColor zu wählen. Dabei handelt es sich um eine Metavariable, die – je nach aktiviertem Theme – anders belebt wird. Ändert der Benutzer die Vordergrundfarbe in der Settings-Anwendung, ändert sich auch der Wert von PhoneForegroundColor. Zum Zeichnen von Background eignet sich am besten PhoneBackgroundColor. Beim Einstellen ist Abbildung 4 zu beachten. Auch die Zeile von nebeneinanderliegenden Grafiken ist wichtig, legt sie doch die Art der Füllung (einfarbig, Gradient etc.) fest.

Abb. 4: Die vertikale Zeile legt den Füllmodus fest

Nachdem das HeaderControl richtig eingestellt wurde, verfahren wir analog mit dem ScrollViewer. Danach klicken wir auf SAVE ALL, schließen Expression Blend und öffnen das Projekt abermals mit Visual Studio. Die Änderungen in MainPage.xaml sind minimal (Listing 2),

<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
    <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
    <TextBlock x:Name="PageTitle" Text="MTCharts3" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel> 

Die volle Bescherung finden wir in der neu erstellten Datei, die uns aber Gott sei Dank nicht wirklich interessiert. Nur aus Kuriositätsgründen folgt hier ein winziger Ausschnitt, der das Verwenden von Metavariablen demonstriert:

<visualizationToolkit:Title.Foreground>
    <SolidColorBrush Color="{StaticResource PhoneForegroundColor}"/>
</visualizationToolkit:Title.Foreground> 

Das Resultat der Programmausführung zeigt Abbildung 5.

Abb. 5: Die Legende sieht nun auch im dunklen Design gesittet aus

Wer möchte, kann auch die Hintergrundfarbe der Chartbox anpassen. Dazu öffnet man MainPage.xaml nochmals in Expression Blend, klickt nach dem Rechtsklick auf myChart diesmal statt auf Edit Additional Templates auf Edit Template, und wählt dort Edit a Copy. Das Grid, das das Diagramm enthält, ist leider sehr gut versteckt – Abbildung 6 zeigt den Klickpfad.

Abb. 6: Das Diagramm-Grid liegt tief in der Control-Struktur

[ header = Seite 4: Dokumentation ]

Dokumentation?

Leider findet man zu in Silverlight Toolkit enthaltenen Steuerelementen in der MSDN keinerlei Informationen. Möchte man Dokumentation haben, muss man das komplette Silverlight Toolkit auf der eigenen Maschine installieren. Es kann unter [2] heruntergeladen werden. Da die für Windows Phone 7 adaptierten Assemblies aus der im April 2010 ausgelieferten Version stammen, sollte man auch diese installieren. Die Dokumentation findet sich danach in einer im Ordner C:Program FilesMicrosoft SDKsSilverlightv4.0ToolkitApr10 liegenden .chm-Datei. Der Vollständigkeit halber nochmals die Warnung, dass die in diesem Ordner liegenden Assemblies am Windows Phone 7 NICHT funktionieren.

Liniendiagramm erbeten

Obwohl Tortendiagramme durchaus attraktive „Brainteaser“ abgeben, kann das Silverlight Toolkit auch eine Vielzahl anderer Diagramme erstellen. Das wollen wir uns in MTCharts4 näher ansehen. Der Autor hat das Beispiel aus MTCharts3 abgeleitet, weshalb die Ressourcendatei wieder mit von der Partie ist. Als Erstes wollen wir ein Blasendiagramm erstellen. Dabei handelt es sich um eine Microsoft-spezifische Darstellungsart, die zusätzlich zu den in einem Liniendiagramm findbaren Koordinateninformationen auch das „Gewicht“ des jeweiligen Datensatzes darstellt. Logischerweise brauchen wir dazu als Erstes eine neue Datenklasse, die statt zwei Elementen nun drei kennt:

public class BubbleInfo
{
    public int X { get; set; }
    public int Y { get; set; }
    public int Weight { get; set; }
} 

Theoretisch könnte man auch –wie im vorigen Beispiel – einen String als IndependentValue übergeben. Das Diagramm würde die Elemente dann äquidistant nebeneinander anzeigen und den String als Label auf der X-Achse verwenden. Im Konstruktor des Formulars beleben wir das Formular nach dem vorigen Schema:

...
BubbleSeries myPieSeries = new BubbleSeries();
myPieSeries.ItemsSource = myBubbleInfoList;
myPieSeries.DependentValuePath = "Y";
myPieSeries.IndependentValuePath = "X";
myPieSeries.SizeValuePath = "Weight";
myChart.Series.Add(myPieSeries); 

Der Unterschied zum vorigen Beispiel besteht darin, dass wir diesmal statt einer PieSeries eine BubbleSeries erstellen. Da die BubbleSeries insgesamt drei Werte braucht, müssen wir auch einen SizeValuePath festlegen. Das Resultat der neuen Routine zeigt Abbildung 7.

Abb. 7: Das Blasendiagramm ist fertig

Zwei in einem

Als Nächstes wollen wir uns ein Liniendiagramm erstellen. Da auch die „Default-Strings“ in der Legende nerven, ist es an der Zeit, hier Abhilfe zu schaffen. Das Line Chart kann dabei entweder mit X- und Y-Werten oder aber mit einem Zahlenwert und einem String versorgt werden. Wir verwenden diesmal wieder die Kombination Zahlenwert und String. Um dem Beispiel ein wenig „Pfiff“ zu geben, soll das Diagramm zwei Datenreihen darstellen. So etwas würde man beispielsweise brauchen, um die Passagierzahlen von zwei Luftfahrzeugen zu visualisieren. Die Erstellung der Daten erfolgt wie auch schon in den vorhergehenden Beispielen in zwei Listen. Wichtig ist wiederum nur der zweite Teil des Konstruktors, der sich im Beispiel mit dem Liniendiagramm wie in Listing 3 zu sehen präsentiert.

LineSeries myPieSeries = new LineSeries();
myPieSeries.ItemsSource = tuList;
myPieSeries.DependentValuePath = "Count";
myPieSeries.IndependentValuePath = "Name";
myPieSeries.Title = "TU144";
myChart.Series.Add(myPieSeries);

myPieSeries = new LineSeries();
myPieSeries.ItemsSource = conList;
myPieSeries.DependentValuePath = "Count";
myPieSeries.IndependentValuePath = "Name";
myPieSeries.Title = "BAC";
myChart.Series.Add(myPieSeries); 

Will man anstatt einer Datenzeile zwei Datenzeilen haben, fügt man einfach zwei Series-Objekte zur Series-Collection des Diagramms hinzu. Das Beeinflussen der Legende erfolgt über die Eigenschaft Title, die einen String entgegennimmt. Auch hier ein Screenshot der Programmausführung. Man erkennt in Abbildung 8 klar, dass das Diagramm zwei Datengruppen darstellt und die Legende angepasst wird.

Abb. 8: TU144 vs. Concorde (Daten: Tupolev PSC, Übersetzung Dr. D. M. Kohrs)

[ header = Seite 5: Achsen und Farben ]

Achsen und Farben

Obwohl das obige Diagramm schon sehr gefällig aussieht, bleiben noch zwei Ärgernisse. Erstens verwendet Silverlight die vom Desktop bekannten Diagrammfarben, die gegenüber den am Windows Phone üblichen knalligen Highlight-Schmuckfarben eher matt wirken. Zweitens haben wir noch keine Möglichkeit gefunden, um die Achsen mit Labels zu versehen. Für das erste Problem brauchen wir eine Farbliste, die im Control-Template festgelegt wird. Da uns Expression Blend ja im Rahmen des Anpassens des Hintergrunds schon so ein Template angelegt hat, haben wir diesmal eher wenig Arbeit. Als Erstes wollen wir unser ResourceDictionary in eine neue .xaml-Datei auslagern. Leider erlaubt die Express Edition von Visual Studio nicht, eine neue leere XAML-Datei zum Projekt hinzuzufügen. Aus diesem Grund klicken wir das Projekt im Projekt-Explorer rechts an und öffnen es in Expression Blend. Dort quittieren wir die höchst lästige Sicherheitswarnung und wählen im Menü PROJECT | ADD NEW ITEM aus. Im daraufhin aufpoppenden Dialog wählen wir Resource Dictionary und quittieren den Dialog mit einem Klick auf OK. Expression Blend erstellt die Datei daraufhin automatisch und integriert sie auch ins Projekt und in App.xaml. Nach einem Klick auf SAVE ALL schließen wir Expression Blend und kehren in Visual Studio zurück. Dort öffnen wir die neue .xaml-Datei im Editor und kopieren den Inhalt von ResourceDictionary1.xaml 1:1 hinein. Die Definition des LegendStyles wird entfernt, der ChartStyle von ChartStyle1 auf ChartStyleWPColors umbenannt:

<Style x:Key="ChartStyleWPColors" TargetType="chartingToolkit:Chart"> 

Danach suchen wir uns den Teil des Styles, der dem in Listing 4 am ehesten entspricht:

<Setter Property="Palette">
    <Setter.Value>
      <visualizationToolkit:ResourceDictionaryCollection>
      <ResourceDictionary>
      <RadialGradientBrush x:Key="Background" Center="0.075,0.015" GradientOrigin="-0.1,-0.1" RadiusY="0.9" RadiusX="1.05">
         <GradientStop Color="#FFB9D6F7"/>
         <GradientStop Color="#FF284B70" Offset="1"/>
      </RadialGradientBrush> 

Dort sehen wir eine Liste von immer gleichen Strukturen vom Typ ResourceDictionary – sie realisieren den Stil von je einer Serie im Diagramm und werden beim Rendering von unten nach oben durchlaufen. Theoretisch kann man dort einfach die Farbwerte des RadialGradientBrush anpassen. Da am Windows Phone 7 aber konstante Schmuckfarben en vogue sind, löschen wir alle ResourceDictionaries bis auf die ersten zwei aus der Datei, und ersetzen die RadialGradientBrushes durch Zeilen nach dem folgenden Schema:

<SolidColorBrush x:Key="Background" Color="#FFA53B3B"/> 

An sich passiert hier nicht allzu viel: Wir weisen dem Background-Element des Charts statt dem RadialGradientBrush einen SolidColorBrush zu. Das Formular CustomLinePage unterscheidet sich von der LinePage nur dadurch, dass dem Chart-Element ein anderer Style zugewiesen wird:

<charting:Chart Name="myChart" LegendTitle="Legende" LegendStyle="{StaticResource LegendStyle1}" Style="{StaticResource ChartStyleWPColors}">
</charting:Chart> 

Der restliche Code ist 1:1 von LinePage.xaml übernommen. Damit kann auch dieses Beispiel ausgeführt werden. Die Linien erscheinen nun in „voller Farbe“. Wer die echten – also von Microsoft definierten – Schmuckfarben verwenden will, findet in der MSDN unter [3] ein Codestück, das die Farbwerte enthält. Zu beachten ist, dass Operator bzw. Hersteller eine eigene Farbe definieren dürfen – man kommt also nicht immer 100 % an das vorgegebene Design heran. Damit bleibt nur mehr das Anpassen der Achsen. Deshalb passen wir den Konstruktor wie in Listing 5 zu sehen an.

LinearAxis myAxis = new LinearAxis();
myAxis.Minimum = 0;
myAxis.Interval = 25;
myAxis.Maximum = 200;
myAxis.Orientation = AxisOrientation.Y;

LineSeries myPieSeries = new LineSeries();
myPieSeries.ItemsSource = tuList;
myPieSeries.DependentValuePath = "Count";
myPieSeries.IndependentValuePath = "Name";
myPieSeries.Title = "TU144";
myPieSeries.DependentRangeAxis = myAxis;
myChart.Series.Add(myPieSeries);

myPieSeries = new LineSeries();
myPieSeries.ItemsSource = conList;
myPieSeries.DependentValuePath = "Count";
myPieSeries.IndependentValuePath = "Name";
myPieSeries.Title = "BAC";
myPieSeries.DependentRangeAxis = myAxis;
myChart.Series.Add(myPieSeries); 

Der Unterschied zum vorigen Konstruktor liegt darin, dass wir den Datenserien nun eine Achse zuweisen. Das Silverlight Toolkit lässt dem Entwickler beim Erstellen der Achse sehr viel Spielraum. Wir verwenden eine LinearAxis, die lineare Werte abbildet. Die Achse braucht zusätzlich noch einige Informationen. Minimum und Maximum legen den minimalen und den maximalen Wert fest. Interval bestimmt, in welchem Abstand die einzelnen „Koordinaten“ angezeigt werden. Orientation bestimmt, ob die Achse in X- oder in Y-Richtung läuft. Das ist hilfreich, da man damit das Diagramm „umdrehen“ kann.

Zu guter Letzt wird die Achse der DependantRange-Wertereihe zugewiesen. Das finale Resultat zeigt Abbildung 9. Offensichtlich funktioniert unser Beispiel auf dem Samsung Omnia 7 ohne jede Macke. Leider ist das Initialisieren der Charts zeitweise etwas langsam.

Abb. 9: Die App ist los

Fortgeschrittene Spielereien

Mit den hier gezeigten Methoden lassen sich zwar schon sehr attraktive Diagramme rendern, das Silverlight Toolkit kann jedoch noch viel mehr. Die Struktur der Anzeige (Wo ist die Legende etc.?) kann über Templates beeinflusst werden. Da die Erklärung der Vorgehensweise den Rahmen dieses Artikels sprengen würde, verweist der Autor auf das Codebeispiel von [1]. David Anson hat auch hier schon vorgearbeitet und zwei fertige Styles samt etwas Sample-Code bereitgestellt. Man kann sogar eigene Charttypen (z. B. die im Edelmetallbereich sehr populären Candlestick-Charts) realisieren. Die Vorgehensweise dazu ist in [4] beschrieben. Man muss den 2009 erstellten Beispielcode aber an die aktuellere Version anpassen.

Fazit

Obwohl die Charting-Funktionen des Silverlight Toolkit bis heute nicht offiziell für Windows Phone 7 angeboten werden, kann man mit dem quelloffenen Werkzeugkasten schon jetzt ohne allzu viel Aufwand attraktive Diagramme und Datenvisualisierungen zusammenhacken. Gerade in Zeiten von immer billiger werdenden Apps macht es Sinn, Entwicklungszeit einzusparen. Außerdem: Wenn in jeder App ein Diagramm vorkommt, kann selbst der dümmste iPhone-User in einigen Jahren Diagramme lesen. In diesem Sinne: Machen Sie’s gut!

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -