Il presente articolo è stato tradotto automaticamente.
ExtremeUI
Grafici a linee con i modelli di dati
Charles Petzold
Scaricare il codice di esempio
Nonostante le molte avanzate manifestazioni del file di grafica questi giorni (incluso animazioni 3D e viceversa), ho il sospetto che il più importante per sempre sarà la rappresentazione grafica di base dei dati nei grafici tradizionali creati da barre, torte e righe.
Una tabella di dati è possibile che venga visualizzato come una confusione di numeri casuali, ma qualsiasi informazione interessante nascosti all'interno dell'indice delle figure o tendenze diventano molto più comprensibile quando visualizzati in un grafico.
Con Windows Presentation Foundation (WPF), ovvero e relativo offshoot basate sul Web, Silverlight, abbiamo abbiamo scoperto i vantaggi derivanti dalla definizione di elementi visivi graphical nel markup anziché il codice. XAML (Extensible Application Markup Language) è più facile da alterare rispetto al codice più semplice di sperimentare e più toolable rispetto al codice, consentendo di noi per definire in modo interattivo nostro elementi visivi e sperimentare approcci alternativi.
Infatti, è vantaggioso in modo che i programmatori WPF verranno trascorrono molte ore la scrittura di codice in modo specifico per consentire più potente e flessibile XAML definisce gli effetti visivi interamente in XAML. Questo è un fenomeno che viene chiamata “ codifica per XAML ” ed è uno dei modi che WPF ha modificato l'approccio allo sviluppo di applicazioni.
Molte delle tecniche più potente in WPF prevedono ItemsControl, ovvero il controllo di base per visualizzare gli insiemi di elementi, in genere dello stesso tipo. (Un controllo familiare che deriva da ItemsControl è ListBox, che consente lo spostamento e selezione, nonché visualizzazione).
È possibile compilare un ItemsControl con oggetti di qualsiasi tipo, anche gli oggetti business che non dispongono di alcuna rappresentazione testuale o visivo intrinseco. Bacchetta ingrediente è un DataTemplate, ovvero quasi sempre definito in XAML, che fornisce una rappresentazione visiva in base alle proprietà ’ oggetti tali oggetti di business.
Marzo problema ho illustrato come utilizzare ItemsControls e DataTemplates per definire i grafici a barre e grafici a torta in XAML. Era originariamente verrà per includere i grafici a linee in tale articolo, ma l'importanza e difficoltà di riga grafici mandates che una colonna intera essere dedicato al soggetto.
Problemi di linea grafico
I grafici a linee sono effettivamente forme di vengono tracciati a dispersione xy, ovvero un sistema di coordinate cartesiano con una variabile sull'asse orizzontale e un'altra sull'asse verticale. Una grande differenza con il grafico a linee è che i valori orizzontalmente graphed in genere sono ordinati. Molto spesso questi valori sono le date o ore, ovvero il grafico a linee Visualizza spesso la modifica in una variabile nel tempo.
La grande differenza è che spesso sono connesse le singole coordinate con una linea. Anche se questa riga è ovviamente una base parte gli elementi visivi grafico a linee, genera effettivamente una chiave inglese monkey grande nel processo di realizzazione del grafico in XAML. DataTemplate descrive la modalità di rendering di ogni elemento in ItemsControl; ma le voci di connessione richiede l'accesso a più punti, in teoria un PointCollection che può quindi essere utilizzato con un elemento Polyline. La necessità di generare questo PointCollection era il primo suggerimento che una classe personalizzata sono necessari per eseguire la pre-elaborazione sui dati grafico a linee.
Più di altri grafici, grafici riga imponga che una maggiore attenzione da pagare per gli assi. Infatti, è utile per gli assi orizzontali e verticali per essere ItemsControls aggiuntive! DataTemplates aggiuntive per questi due altri ItemsControls consente quindi di definire la formattazione dei segni di graduazione dell'asse ed etichette interamente in XAML.
In sintesi, cosa iniziare con è un insieme di elementi di dati con due proprietà: una proprietà corrispondente per l'asse orizzontale e l'altro per l'asse verticale. Per realizzare un grafico in XAML, è necessario ottenere determinati elementi da questi dati. Innanzitutto, è necessario scegliere gli oggetti per ogni elemento di dati (eseguire il rendering di ogni punto di dati). È inoltre necessario un PointCollection di tutti gli elementi di dati (per la linea che collega i punti) e li contrassegna due raccolte aggiuntive contenenti informazioni sufficienti per eseguire il rendering di assi orizzontali e verticali in XAML, inclusi i dati per le etichette e gli offset per posizionare le etichette e i segni di graduazione.
Il calcolo di tali oggetti Point e offset richiede ovviamente alcune informazioni: la larghezza e l'altezza dei valori di grafico e il minimo e massimo dei dati graphed sugli assi orizzontali e verticali.
Ma che non è abbastanza insufficiente. Si supponga che il valore minimo per l'asse verticale è 127 e il valore massimo è 232. In tal caso, è consigliabile l'asse verticale effettivamente estendere da 100 a 250 con segni di graduazione ogni 25 unità. O per questo grafico specifico, è consigliabile includere sempre 0, in modo che l'asse verticale è compreso tra 0 e 250. O forse si desidera che il valore massimo, per essere sempre un multiplo di 100, in modo che va da 0 a 300. Se i valori compresi tra-125 e 237, forse da 0 per essere centrato, in modo che l'asse potrebbe variare da-300 a 300.
Esistono potenzialmente molte strategie differenti per determinare i valori da visualizzare gli assi, che quindi controllano il calcolo dei valori di punti associati a ogni elemento di dati. Queste strategie possono pertanto variare che è opportuno offrire un'opzione “ plug-in ” per definire le strategie di asse aggiuntive come necessario per un particolare grafico.
Il primo tentativo
Gli errori di programmazione sono talvolta istruttivo come programmazione successi. Il primo tentativo di creare una classe per grafici riga accessibile da XAML non era esattamente un errore al termine, ma è stato certamente a frecce nella direzione desiderata.
Sapevo che per generare l'insieme di oggetti Point verrebbe ovviamente devo accesso all'insieme di elementi in ItemsControl, nonché il ActualWidth e ActualHeight del controllo. Per questi motivi sembravano logico derivare una classe da ItemsControl che chiamato LineChartItemsControl.
LineChartItemsControl definite diverse nuove proprietà di lettura/scrittura: I nomi di proprietà ’ gli elementi che dovrebbe essere graphed forniti HorizontalAxisPropertyName e VerticalAxisPropertyName. Altri quattro nuove proprietà fornito LineChartItemsControl con i valori minimo e massimo per gli assi orizzontali e verticali. (Questo era un approccio molto semplice per la gestione degli assi sapevo che doveva essere migliorata in un secondo momento).
Il controllo personalizzato definito inoltre tre proprietà di dipendenza di sola lettura per l'associazione dati in XAML: una proprietà denominata Points di tipo PointCollection e due proprietà denominata HorizontalAxisInfo e VerticalAxisInfo per il rendering di assi.
LineChartItemsControl overrode i metodi OnItemsSourceChanged e OnItemsChanged per essere informato ogni volta che sono stati che si verificano modifiche nell'insieme di elementi e installato un gestore per l'evento SizeChanged. È quindi abbastanza semplice mettere insieme tutte le informazioni disponibili per calcolare le tre proprietà di dipendenza di sola lettura.
Utilizzare effettivamente LineChartItemsControl in XAML, tuttavia, era un disordine. La parte più semplice è stato eseguito il rendering di linea collegati. Che è stata eseguita con un elemento Polyline con la relativa proprietà Points associato alla proprietà Points di LineChartItemsControl. Ma non è molto difficile definire un DataTemplate sarebbe posizionare i singoli dati. Il modello di dati ha solo accesso alle proprietà di un particolare elemento di dati. Tramite associazioni, DataTemplate può accedere ItemsControl stesso, ma come ottenere accesso alle informazioni che corrisponde all'elemento dati specifico di posizionamento?
La soluzione è coinvolto un set RenderTransform da un MultiBinding che conteneva un'associazione di RelativeSource e a cui fa riferimento un BindingConverter. Era talmente complesso che il giorno dopo, avevo codificato è couldn't piuttosto determinare come funzionava!
La complessità di questa soluzione non è una chiara indicazione che è stato necessario un approccio completamente diverso.
Generatore di grafici Line in pratica
La soluzione reconceived era una classe che ho chiamato LineChartGenerator quanto genera tutte le materie prime necessarie per definire gli elementi visivi di un grafico completamente in XAML. Passa un insieme (gli oggetti business effettivi) e vengono forniti quattro insiemi, ovvero uno per i dati di punta, uno per il disegno di linea collegati e altre due per gli assi orizzontali e verticali. In questo modo è possibile creare un grafico in XAML contiene più ItemsControls (generalmente disposti in una griglia four-by-four o superiore se si desidera includere titoli e altre etichette), ciascuno con un proprio DataTemplate per visualizzare questi insiemi.
Vedere Let’s il funzionamento in pratica. (Tutto il codice sorgente scaricabile è contenuto in un singolo progetto di Visual Studio denominato LineChartsWithDataTemplates. Questa soluzione ha un progetto DLL denominato LineChartLib e tre programmi di dimostrazione).
Il progetto PopulationLineChart contiene una struttura denominata CensusDatum definisce due proprietà di tipo int denominata Year e popolamento. La classe CensusData deriva da ObservableCollection di tipo CensusDatum e riempie l'insieme con u. S. dati censimenti decennial da anni 1790 (quando la compilazione è stata 3,929,214) tramite 2000 (281,421,906). Nella figura 1 Mostra il grafico risultante.
Figura 1 Il Display PopulationLineChart
Tutto il codice XAML per il grafico si trova nel file Window1.XAML nel progetto PopulationLineChart. Nella figura 2 viene illustrata la sezione Resources di questo file. LineChartGenerator ha una propria proprietà ItemsSource; in questo esempio viene impostato per l'oggetto CensusData. È inoltre necessario impostare le proprietà Width e Height qui. (È possibile realizzare questa operazione non è una posizione ottimale per questi valori e non abbastanza favoriscono per il metodo preferito di layout in WPF, ma è impossibile scoprire una soluzione migliore). Questi valori indicano le dimensioni interne della escludendo gli assi orizzontali e verticali del grafico.
Nella figura 2 la sezione Resources di PopulationLineChart
<Window.Resources>
<src:CensusData x:Key="censusData" />
<charts:LineChartGenerator
x:Key="generator"
ItemsSource="{Binding Source={StaticResource censusData}}"
Width="300"
Height="200">
<charts:LineChartGenerator.HorizontalAxis>
<charts:AutoAxis PropertyName="Year" />
</charts:LineChartGenerator.HorizontalAxis>
<charts:LineChartGenerator.VerticalAxis>
<charts:IncrementAxis PropertyName="Population"
Increment="50000000"
IsFlipped="True" />
</charts:LineChartGenerator.VerticalAxis>
</charts:LineChartGenerator>
</Window.Resources>
LineChartGenerator presenta inoltre due proprietà di tipo denominato HorizontalAxis e VerticalAxis AxisStrategy. AxisStrategy è una classe astratta che definisce diverse proprietà, tra cui PropertyName in cui si indica che la proprietà degli oggetti dati che si desidera che nel grafico di questo asse. In conformità con il sistema di coordinate WPF, valori crescenti andare da sinistra a destra e dall'alto verso il basso. Quasi sempre è opportuno impostare IsFlipped proprietà sull'asse verticale a true così l'aumento di valori passare dal basso verso l'alto.
Una delle classi deriva da AxisStrategy è IncrementAxis, definisce una proprietà denominata Increment. Con la strategia IncrementAxis, è possibile specificare quale incremento desiderato tra i segni di graduazione. Il valore minimo e massimo vengono impostati come multipli dell'incremento. Ho utilizzato IncrementAxis per la scala di popolazione.
Un'altra classe che deriva da AxisStrategy è AutoAxis, non definisce nessun ulteriore proprietà proprie. Ho utilizzato quello per l'asse orizzontale: Tutto ciò avviene è utilizzare i valori effettivi per l'asse. (Un'altra derivazione AxisStrategy ovvio che sono stati scritto è ExplicitAxis inserendo un elenco di valori visualizzato sull'asse.)
La classe LineChartGenerator definisce due proprietà di dipendenza di sola lettura. Il primo è denominato punti di tipo PointCollection, questa proprietà viene utilizzata per disegnare la linea che collega i punti:
<Polyline Points="{Binding Source={StaticResource generator},
Path=Points}"
Stroke="Blue" />
La seconda proprietà LineChartGenerator è denominata ItemPoints di tipo ItemPointCollection. Un ItemPoint presenta due proprietà, denominata Item e Point. L'elemento è l'oggetto originale nell'insieme, ovvero in questo esempio specifico elemento è un oggetto di tipo CensusDatum. Punto è il punto in cui tale elemento verrà visualizzato nel grafico.
Nella figura 3 Mostra ItemsControl che visualizza il corpo principale del grafico. Si noti che relativo ItemsSource è associata alla proprietà del LineChartGenerator ItemPoints. Modello ItemsPanel è un elemento Grid e ItemTemplate è un percorso con un EllipseGeometry e una descrizione comandi. La proprietà Center di EllipseGeometry il è associata alla proprietà dell'oggetto ItemPoint Point mentre la descrizione comandi accede alle proprietà Year e popolazione della proprietà Item.
Nella figura 3 ItemsControl The Main per PopulationLineChart
<ItemsControl ItemsSource="{Binding Source={StaticResource generator},
Path=ItemPoints}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Path Fill="Red" RenderTransform="2 0 0 2 0 0">
<Path.Data>
<EllipseGeometry Center="{Binding Point}"
RadiusX="4"
RadiusY="4"
Transform="0.5 0 0 0.5 0 0" />
</Path.Data>
<Path.ToolTip>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Item.Year}" />
<TextBlock Text="{Binding Item.Population,
StringFormat=’: {0:N0}’}" />
</StackPanel>
</Path.ToolTip>
</Path>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Si chiederà sulla trasformazione impostata sull'oggetto EllipseGeometry sfalsato di una proprietà RenderTransform impostato sull'elemento Path. Questo è un kludge: Senza di essa, dell'ellisse all'estrema destra è stato parzialmente troncato e couldn't correggere con ClipToBounds.
La polilinea e questo ItemsControl principale condividono la stessa griglia a cella singola con larghezza e altezza viene associata ai valori da LineChartGenerator:
<Grid Width="{Binding Source=
{StaticResource generator}, Path=Width}"
Height="{Binding Source=
{StaticResource generator}, Path=Height}">
La polilinea è di sotto di ItemsControl in questo esempio.
La classe AxisStrategy definisce la proprietà di dipendenza di sola lettura denominata AxisItems, un insieme di oggetti di tipo AxisItem, dispone di due proprietà denominata Item e offset. Questo è l'insieme utilizzato per ItemsControl per ogni asse. Anche se la proprietà Item è definita di tipo object, sarà effettivamente lo stesso tipo della proprietà associata a quell'asse. Offset è una distanza dalla parte superiore o sinistra.
Nella figura 4 Mostra ItemsControl per l'asse orizzontale, l'asse verticale è simile. La proprietà ItemsSource di ItemsControl è associata alla proprietà AxisItems della proprietà HorizontalAxis del LineChartGenerator. ItemsControl viene pertanto compilato con gli oggetti di tipo AxisItem. La proprietà Text di TextBlock è associata alla proprietà Items e viene utilizzata la proprietà offset per tradurre il Mark di graduazione e il testo lungo l'asse.
Nella figura 4 markup per l'asse orizzontale di PopulationLineChart
<ItemsControl Grid.Row="2"
Grid.Column="1"
Margin="4 0"
ItemsSource="{Binding Source={StaticResource generator},
Path=HorizontalAxis.AxisItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Line Y2="10" Stroke="Black" />
<TextBlock Text="{Binding Item}"
FontSize="8"
LayoutTransform="0 -1 1 0 0 0"
RenderTransform="1 0 0 1 -6 1"/>
<StackPanel.RenderTransform>
<TranslateTransform X="{Binding Offset}" />
</StackPanel.RenderTransform>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Poiché questi tre ItemsControls sono semplicemente seduta in tre celle di una griglia, è responsabilità dell'utente di progettazione del layout in XAML per assicurarsi che vengano allineati correttamente.Tutti i bordi o i margini o spaziatura interna applicate a tali controlli devono essere coerente.ItemsControl in Figura 4 è un margine orizzontale di 4, ItemsControl per l'asse verticale sono un margine verticale di 4.Si è scelto di tali valori corrispondono ai BorderThickness e riempimento di un bordo che circonda la griglia a cella singola contenente il grafico stesso e la polilinea:
<Border Grid.Row="1"
Grid.Column="1"
Background="Yellow"
BorderBrush="Black"
BorderThickness="1"
Padding="3">
Consistenza di tipo di dati
La classe LineChartGenerator stessa non è molto interessante. Si presuppone che l'insieme ItemsSource già ordinato e è prevalentemente dedicato a verificare che tutto ciò che viene aggiornata quando viene modificata la proprietà ItemsSource. Se l'insieme è impostata sulla ItemsSource implementa ICollectionChanged, il grafico viene aggiornato anche quando gli elementi vengono aggiunti o rimossi dall'insieme. Se gli elementi dell'insieme implementano INotifyPropertyChanged, il grafico viene aggiornato quando modificano elementi stessi.
La maggior parte delle reale utilizzare effettivamente va classi AxisStrategy e derived. Tali derivazioni AxisStrategy sono le classi impostato su LineChartGenerator proprietà HorizontalAxis e VerticalAxis.
AxisStrategy stesso definisce la proprietà PropertyName importante indica quali proprietà degli oggetti da inserire nel grafico è associato a quell'asse. AxisStrategy utilizza la reflection per accedere a quel particolare proprietà di oggetti nell'insieme. Ma solo l'accesso a tale proprietà non è sufficiente. AxisStrategy (e relativi derivati) necessario eseguire calcoli sui valori di questa proprietà per ottenere oggetti Point e segni di graduazione offset. Questi calcoli comprendono la moltiplicazione e divisione.
La necessità di calcoli fortemente significa che le proprietà da graphed devono essere tipi numerici, ovvero valori integer o a virgola mobile. Ancora un tipo di dati estremamente comuni sull'asse orizzontale dei grafici a linee in genere utilizzato non è un numero, ma una data o un'ora. In Microsoft .NET Framework, stiamo parlando di un oggetto di tipo DateTime.
Quale operazione tutti i tipi di dati numerici e DateTime necessario in comune? Ma tutti implementano l'interfaccia IConvertible, che significa che tutte contengono una serie di metodi convertirli in un altro, e sono tutti utilizzabili con i metodi della classe Convert statico denominato stesso. Di conseguenza, sembravano ragionevole per richiedere che le proprietà da inserire nel grafico implementano IConvertible. AxisStrategy (e relativi derivati) possono quindi semplicemente convertire i valori delle proprietà dati Double per eseguire i calcoli necessari.
Tuttavia, ho subito scoperto che le proprietà di tipo DateTime effettivamente Impossibile essere convertito in raddoppia utilizzando il metodo ToDouble o il metodo statico Convert.ToDouble. Ciò significava che le proprietà di tipo DateTime realmente doveva essere gestiti con una logica speciale, Fortunatamente è disattivata da una grande quantità. La proprietà Ticks definita da DateTime è un valore integer a 64 bit, che può essere convertito in double, double può essere convertito in un DateTime innanzitutto convertirlo in un valore integer a 64 bit e passando quindi tale valore a un costruttore DateTime. Un po' sperimentazione rilevato che la conversione di andata e ritorno è approssimata al millisecondo.
AxisStrategy dispone di un metodo Ricalcola che scorre in ciclo tutti gli elementi nell'insieme ItemsSource del relativo padre, converte la proprietà specificata di ogni oggetto in un valore double e determina i valori minimi e massimo. AxisStrategy definisce tre proprietà che influiscono sulla potenzialmente questi due valori: Margine (che consente il minimo e massimo per essere un po' oltre l'intervallo dei valori effettivi), IncludeZero (in modo che l'asse include sempre il valore zero anche se tutti i valori sono maggiori di zero o inferiore a zero) e IsSymmetricAroundZero, significa che l'asse massimo deve essere positivo e il valore minimo deve essere negativo, ma essi deve avere gli stessi valori assoluti.
Dopo le rettifiche AxisStrategy chiama il metodo astratto CalculateAxisItems:
protected abstract void CalculateAxisItems(Type propertyType, ref double minValue, ref double maxValue);
Il primo argomento è il tipo di proprietà corrispondente a tale asse. Qualsiasi classe che deriva da AxisStrategy deve implementare questo metodo e utilizzare l'opportunità di definire gli elementi e gli offset che costituiscono il relativo insieme AxisItems.
È molto probabile che CalculateAxisItems imposterà anche valori nuovo minimo e massimo. Quando restituisce CalculateAxisItems, questi valori con la larghezza e l'altezza del grafico AxisStrategy quindi utilizzato per calcolare i valori di punto di tutti gli elementi.
Origini dati XML
Oltre a gestire le proprietà numeriche e le proprietà di tipo DateTime, AxisStategy deve gestire anche la distinzione tra maiuscole e minuscole quando gli elementi dell'insieme ItemsSource sono di tipo XmlNode. Questo è ciò che l'insieme contiene quando ItemsSource è associata a XmlDataProvider riferimento un'isola di dati XML nel file XAML o un file XML esterno.
AxisStrategy utilizza le stesse convenzioni DataTemplates: Un nome di per sé fa riferimento a un elemento XML e un nome preceduto da un simbolo @ è un attributo XML. AxisStrategy consente di ottenere questi valori come stringhe. Nel caso siano effettivamente le date o ore, la AxisStrategy tenta innanzitutto di convertire le stringhe in oggetti DateTime prima di convertirli in valori Double. DateTime.TryParseExact viene utilizzato per questo processo e solo per le specifiche di formattazione della lingua invariante dei “ R ”, “ s ”, “ u ” e “ o ”.
Viene illustrato il progetto SalesByMonth grafici XML dei dati e alcune altre funzionalità. Il file Window1.XAML contiene XmlDataProvider con i dati per i 12 mesi di vendite fittizie denominata penne e gomme due prodotti:
<XmlDataProvider x:Key="sales"
XPath="YearSales">
<x:XData>
<YearSales >
<MonthSales Date="2009-01-01T00:00:00">
<Widgets>13</Widgets>
<Doodads>285</Doodads>
</MonthSales>
...
<MonthSales Date="2009-12-01T00:00:00">
<Widgets>29</Widgets>
<Doodads>160</Doodads>
</MonthSales>
</YearSales>
</x:XData>
</XmlDataProvider>
La sezione risorse contiene inoltre due oggetti LineChartGenerator molto simili per i due prodotti.Di seguito è quello per le penne
<charts:LineChartGenerator
x:Key="widgetsGenerator"
ItemsSource=
"{Binding Source={StaticResource sales},
XPath=MonthSales}"
Width="250" Height="150">
<charts:LineChartGenerator.HorizontalAxis>
<charts:AutoAxis PropertyName="@Date" />
</charts:LineChartGenerator.HorizontalAxis>
<charts:LineChartGenerator.VerticalAxis>
<charts:AdaptableIncrementAxis
PropertyName="Widgets"
IncludeZero="True"
IsFlipped="True" />
</charts:LineChartGenerator.VerticalAxis>
</charts:LineChartGenerator>
Si noti che l'asse orizzontale è associato l'attributo XML Data.L'asse verticale è di tipo AdaptableIncrementAxis che deriva da AxisStrategy e definisce due proprietà aggiuntive:
• Incrementi di tipo DoubleCollection
• MaximumItems di tipo int
L'insieme incrementa ha i valori predefiniti 1, 2 e 5 e la proprietà MaximumItems ha un valore predefinito di 10.Il progetto SalesByMonth utilizza semplicemente le impostazioni predefinite.AdaptableIncrementAxis determina l'incremento tra i segni di graduazione in modo che il numero di elementi dell'asse non superi MaximumItems ottimale.Con le impostazioni predefinite i valori di incremento di 1, 2 e 5 e quindi 0, 20 e 50 e quindi 100, 200 e 500 viene verificato e così via.Passerà anche nella direzione opposta: test con incrementi di 0,5, 0,2, 0,1 e così via.
È possibile compilare la proprietà incrementi di AdaptableIncrementAxis ovviamente con altri valori.Se si desidera che l'incremento da sempre essere un multiplo di 10, è sufficiente utilizzare il valore 1.In alternativa a 1, 2 e 5 che potrebbero essere più appropriato per alcune situazioni è 1, 2.5 e 5.
AdaptableIncrementAxis (o qualcosa di simile a esso la propria creazione) è probabilmente la scelta migliore quando i valori numerici di un asse sono imprevedibili, in particolare quando il grafico contiene i dati in modo dinamico la modifica o aumentando la dimensione complessiva.Poiché la proprietà incrementi di AdaptableIncrementAxis è di tipo DoubleCollection, è adatta per valori DateTime.Descriverò un'alternativa per DateTime più avanti in questa colonna.
Il file XAML nel progetto SalesByMonth definisce due oggetti LineChartGenerator due prodotti che consente quindi un grafico composto come illustrato in Figura 5.
Nella figura 5 Il Display SalesByMonth
Questa opzione di creazione di un grafico composto non richiedeva alcuna speciale nelle classi che costituiscono LineChartLib.Tutto il codice non è generare flessibile gli insiemi che possono quindi essere gestiti in XAML.
Per supportare tutte le etichette e gli assi, l'intero grafico viene realizzato in una griglia di quattro righe e cinque colonne contenente cinque ItemsControls, due per due insiemi di dati degli elementi del grafico stesso, due per le scale asse sulla sinistra e a destra e maggiore di uno per l'asse orizzontale.
L'assegnazione dei colori per distinguere i due prodotti è semplice da implementare in XAML.Ma si noti inoltre che i due prodotti vengono ulteriormente distinti da coordinate triangolare e lo stile Incorniciato.Gli elementi triangolari sono stato eseguito il rendering da questo modello di dati:
<DataTemplate>
<Path Fill="Blue"
Data="M 0 -4 L 4 4 -4 4Z">
<Path.RenderTransform>
<TranslateTransform X="{Binding Point.X}"
Y="{Binding Point.Y}" />
</Path.RenderTransform>
</Path>
</DataTemplate>
È possibile utilizzare forme effettivamente associate due prodotti o persino piccola bitmap in un esempio reale.
La riga che collega i punti in questo esempio non è un elemento Polyline standard, ma invece un derivato di Shape personalizzato denominato CanonicalSpline. (Spline canonica, noto anche come le spline di tipo cardinal, ovvero fa parte di Windows Form ma non hanno apportato in di WPF. Ogni coppia di punti è collegata tramite una curva algorithmically dipende da due punti aggiuntivi che circonda la coppia di punti.) È inoltre possibile scrivere altre classi personalizzate a tale scopo, ad esempio uno che esegue l'interpolazione dei minimi quadrati nei punti e viene visualizzato il risultato.
HorizontalAxis.AxisItems proprietà del LineChartChartGenerator è un ObservableCollection di tipo DateTime, significa che gli elementi possono essere formattati utilizzando la funzionalità StringFormat della classe di associazione e la data/ora standard le stringhe di formattazione.
DataTemplate per l'asse orizzontale utilizza “ MMMM ” stringa di formattazione per visualizzare i nomi dei mesi intero:
<DataTemplate>
<StackPanel HorizontalAlignment="Left">
<Line Y2="10" Stroke="Black" />
<TextBlock Text="{Binding Item, StringFormat=MMMM}"
RenderTransform="1 0 0 1 -4 -4">
<TextBlock.LayoutTransform>
<RotateTransform Angle="45" />
</TextBlock.LayoutTransform>
</TextBlock>
<StackPanel.RenderTransform>
<TranslateTransform X="{Binding Offset}" />
</StackPanel.RenderTransform>
</StackPanel>
</DataTemplate>
Le date e ore
L'utilizzo di oggetti DateTime sull'asse orizzontale di un grafico a linee è così comune che vale la pena codifica un AxisStrategy sforzo spesa per gestire in modo specifico con questi oggetti. Alcuni grafici a linee che si accumulino dati quali quotazioni dei titoli o i valori letti ambientali, ad esempio aggiungendo un nuovo elemento di ogni ora, e sarebbe bello avere un AxisStrategy si adatta automaticamente in base ai valori DateTime di intervallo tra gli elementi nel grafico.
Mio consente a una classe è calledAdaptableDateTimeAxis ed è destinato a contenere dati DateTime su una vasta gamma di secondi per anni.
AdaptableDateTimeAxis dispone di una proprietà MaximumItems (con impostazione predefinita di 10) e sei insiemi denominati SecondIncrements MinuteIncrements, HourIncrements, DayIncrements, MonthIncrements e YearIncrements. La classe sistematicamente tenterà di trovare un incremento tra segni di graduazione punti in modo che il numero di elementi non superi MaximumItems. Con le impostazioni predefinite AdaptableDateTimeAxis verrà testato con incrementi di 1 secondo, di 2 secondi, 5, 15 e 30 secondi, quindi 1, 2, 5, 15 e 30 minuti, 1, 2, 4, 6 e 12 ore, 1, 2, 5 e 10 giorni e 1, 2, 4 e 6 mesi. Una volta ottiene agli anni, tenta di 1, 2 e 5 anni, quindi 10, 20 e 50, e così via.
AdaptableDateTimeAxis definisce inoltre una proprietà di dipendenza di sola lettura denominata DateTimeInterval, anche il nome di un'enumerazione con membri di secondo, minuto, ora e così via, che indica le unità di incremento asse determinato dalla classe. Questa proprietà consente di DataTriggers essere definito in XAML che modificano la formattazione di DateTime in base a un incremento. Nella figura 6 viene illustrato un esempio di DataTemplate che esegue tale selezione formattazione.
Nella figura 6 il DataTemplate per l'asse orizzontale di TemperatureHistory
<DataTemplate>
<StackPanel HorizontalAlignment="Left">
<Line Y2="10" Stroke="Black" />
<TextBlock Name="txtblk"
RenderTransform="1 0 0 1 -4 -4">
<TextBlock.LayoutTransform>
<RotateTransform Angle="45" />
</TextBlock.LayoutTransform>
</TextBlock>
<StackPanel.RenderTransform>
<TranslateTransform X="{Binding Offset}" />
</StackPanel.RenderTransform>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource generator},
Path=HorizontalAxis.
DateTimeInterval}"
Value="Second">
<Setter TargetName="txtblk" Property="Text"
Value="{Binding Item, StringFormat=h:mm:ss d MMM yy}" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={StaticResource generator},
Path=HorizontalAxis.
DateTimeInterval}"
Value="Minute">
<Setter TargetName="txtblk" Property="Text"
Value="{Binding Item, StringFormat=h:mm d MMM yy}" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={StaticResource generator},
Path=HorizontalAxis.
DateTimeInterval}"
Value="Hour">
<Setter TargetName="txtblk" Property="Text"
Value="{Binding Item, StringFormat=’h tt, d MMM yy’}" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={StaticResource generator},
Path=HorizontalAxis.
DateTimeInterval}"
Value="Day">
<Setter TargetName="txtblk" Property="Text"
Value="{Binding Item, StringFormat=d}" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={StaticResource generator},
Path=HorizontalAxis.
DateTimeInterval}"
Value="Month">
<Setter TargetName="txtblk" Property="Text"
Value="{Binding Item, StringFormat=MMM yy}" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={StaticResource generator},
Path=HorizontalAxis.
DateTimeInterval}"
Value="Year">
<Setter TargetName="txtblk" Property="Text"
Value="{Binding Item, StringFormat=MMMM}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Tale modello è il progetto TemperatureHistory, che accede al sito Web del servizio Weather nazionale per ottenere ogni ora i valori letti temperatura in Park centrale a New York.Nella figura 7 Mostra visualizzazione TemperatureHistory dopo viene eseguito il programma per diverse ore; Figura 8 Mostra dopo numerosi giorni.
Nella figura 7 Il Display TemperatureHistory con ore
Nella figura 8 Il Display TemperatureHistory con giorni
Naturalmente, classi di grafici in linea non sono completamente flessibile, ad esempio, attualmente non è possibile disegnare in modo indipendente i segni di graduazione non associati a etichette di testo, ma penso che illustrano un approccio praticabile e potente per fornire informazioni sufficienti per definire elementi visivi grafico a linee interamente in XAML.
Charles Petzold* è consulente redattore esperti di* MSDN Magazine*.*Il suo libro più recente è The Annotated Turing: Una presentazione tramite carta storici di Alan Turing Computability e il di Turing Machine guidata (Wiley, 2008). Il suo sito Web è charlespetzold.com .
Grazie all'esperto tecnica seguente per la revisione di questo articolo: David Teitelbaum