This example demonstrates how to bind series view models to a chart. Note that you can bind secondary axes and custom labels using the same approach.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MvvmChart"
xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts" x:Class="MvvmChart.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="399" Width="656">
<Grid>
<!--region ChartView-->
<dxc:ChartControl x:Name="chart"
SelectionMode="Extended"
SeriesSelectionMode="Series"
SelectedItem="{Binding Mode=TwoWay, Path=SelectedWeather}">
<dxc:XYDiagram2D SeriesItemsSource="{Binding Weather}">
<dxc:XYDiagram2D.SeriesItemTemplateSelector>
<local:WeatherTemplateSelector>
<local:WeatherTemplateSelector.MinMaxSeriesTemplate>
<DataTemplate>
<dxc:RangeAreaSeries2D DataSource="{Binding Data}"
ArgumentDataMember="Date"
ValueDataMember="MinValue"
Value2DataMember="MaxValue"
Marker1Visible="False"
Marker2Visible="False"
Transparency="0.7"
CrosshairLabelPattern="{}{S} Day Temperature: Min: {V1}°C Max: {V2}°C">
<dxc:RangeAreaSeries2D.SeriesAnimation>
<dxc:Area2DStretchFromNearAnimation Duration="0:0:1.200"/>
</dxc:RangeAreaSeries2D.SeriesAnimation>
</dxc:RangeAreaSeries2D>
</DataTemplate>
</local:WeatherTemplateSelector.MinMaxSeriesTemplate>
<local:WeatherTemplateSelector.AvgSeriesTemplate>
<DataTemplate>
<dxc:LineSeries2D DataSource="{Binding Data}"
ArgumentDataMember="Date"
ValueDataMember="AvgValue"
MarkerVisible="True"
CrosshairLabelPattern="{}{S} Day Temperature: Avg: {V1}°C">
</dxc:LineSeries2D>
</DataTemplate>
</local:WeatherTemplateSelector.AvgSeriesTemplate>
</local:WeatherTemplateSelector>
</dxc:XYDiagram2D.SeriesItemTemplateSelector>
</dxc:XYDiagram2D>
</dxc:ChartControl>
<!--region ChartView-->
</Grid>
</Window>
#Region "#ChartViewCodeBehind"
Imports System.ComponentModel
Imports System.Windows
Imports System.Windows.Controls
Namespace MvvmChart
Partial Public Class MainWindow
Inherits Window
Private privateViewModel As DailyWeatherViewModel
Public Property ViewModel() As DailyWeatherViewModel
Get
Return privateViewModel
End Get
Private Set(ByVal value As DailyWeatherViewModel)
privateViewModel = value
End Set
End Property
Public Sub New()
InitializeComponent()
ViewModel = New DailyWeatherViewModel()
DataContext = ViewModel
AddHandler ViewModel.PropertyChanged, AddressOf OnViewModelPropertyChanged
End Sub
Private Sub OnViewModelPropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
DataContext = Nothing
DataContext = ViewModel
End Sub
End Class
Friend Class WeatherTemplateSelector
Inherits DataTemplateSelector
Public Property AvgSeriesTemplate() As DataTemplate
Public Property MinMaxSeriesTemplate() As DataTemplate
Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate
Dim weatherItem As WeatherItem = TryCast(item, WeatherItem)
If weatherItem Is Nothing Then
Return Nothing
End If
Return If(weatherItem.IsSelected, MinMaxSeriesTemplate, AvgSeriesTemplate)
End Function
End Class
End Namespace
#End Region ' #ChartViewCodeBehind
#Region "#ViewModelAndModel"
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Globalization
Imports System.Windows.Media
Imports System.Xml.Linq
Namespace MvvmChart
Public Class DailyWeatherViewModel
Implements INotifyPropertyChanged
Private Const vostokStationName As String = "Vostok Station"
Private Const deathValleyName As String = "Death Valley, NV"
Private Shared ReadOnly coldColor As Color = Color.FromArgb(255, 0, 0, 255)
Private Shared ReadOnly hotColor As Color = Color.FromArgb(255, 255, 0, 0)
Private ReadOnly weather_Renamed As List(Of WeatherItem)
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public ReadOnly Property Weather() As List(Of WeatherItem)
Get
Return weather_Renamed
End Get
End Property
Private selectedItem As WeatherItem
Public Property SelectedWeather() As WeatherItem
Get
Return selectedItem
End Get
Set(ByVal value As WeatherItem)
If selectedItem Is value Then
Return
End If
If selectedItem IsNot Nothing Then
selectedItem.IsSelected = False
End If
selectedItem = value
If value IsNot Nothing Then
value.IsSelected = True
End If
RaisePropertyChangedEvent("SelectedWeather")
End Set
End Property
Public Sub New()
Dim valleyData As List(Of WeatherRecord) = LoadWeatherData("DeathValley.xml")
Dim vostokData As List(Of WeatherRecord) = LoadWeatherData("VostokStation.xml")
weather_Renamed = New List(Of WeatherItem)() From { _
New WeatherItem(valleyData, hotColor, deathValleyName), _
New WeatherItem(vostokData, coldColor, vostokStationName) _
}
End Sub
Private Function LoadWeatherData(ByVal fileName As String) As List(Of WeatherRecord)
Dim items As New List(Of WeatherRecord)()
Dim weatherDocument As XDocument = XDocument.Load(String.Format("../../Data/{0}", fileName))
For Each element As XElement In weatherDocument.Root.Elements("Weather")
items.Add(WeatherRecord.Load(element))
Next element
Return items
End Function
Private Sub RaisePropertyChangedEvent(ByVal propertyName As String)
Dim handler = PropertyChangedEvent
If handler IsNot Nothing Then
handler.Invoke(Me, New PropertyChangedEventArgs(propertyName))
End If
End Sub
End Class
Public Class WeatherItem
Implements INotifyPropertyChanged
Private averageLineThickness_Renamed As Integer = 2
Private isSelected_Renamed As Boolean = False
Public Property AverageLineThickness() As Integer
Get
Return averageLineThickness_Renamed
End Get
Set(ByVal value As Integer)
averageLineThickness_Renamed = value
RaisePropertyChanged("AverageLineThickness")
End Set
End Property
Public Property IsSelected() As Boolean
Get
Return isSelected_Renamed
End Get
Set(ByVal value As Boolean)
If isSelected_Renamed = value Then
Return
End If
isSelected_Renamed = value
RaisePropertyChanged("IsSelected")
End Set
End Property
Private privateData As List(Of WeatherRecord)
Public Property Data() As List(Of WeatherRecord)
Get
Return privateData
End Get
Private Set(ByVal value As List(Of WeatherRecord))
privateData = value
End Set
End Property
Private privateColor As Color
Public Property Color() As Color
Get
Return privateColor
End Get
Private Set(ByVal value As Color)
privateColor = value
End Set
End Property
Private privateName As String
Public Property Name() As String
Get
Return privateName
End Get
Private Set(ByVal value As String)
privateName = value
End Set
End Property
Public Sub New(ByVal data As List(Of WeatherRecord), ByVal color As Color, ByVal name As String)
Me.Data = data
Me.Color = color
Me.Name = name
End Sub
#Region "INotifyPropertyChanged Members"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub RaisePropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
#End Region
End Class
Public Class WeatherRecord
Public Shared Function Load(ByVal element As XElement) As WeatherRecord
Dim culture As CultureInfo = CultureInfo.InvariantCulture
Dim date_Renamed As Date = Date.Parse(element.Attribute("Date").Value, culture)
Dim min As Double = Double.Parse(element.Attribute("Min").Value, culture)
Dim max As Double = Double.Parse(element.Attribute("Max").Value, culture)
Dim avg As Double = Double.Parse(element.Attribute("Avg").Value, culture)
Return New WeatherRecord(date_Renamed, max, avg, min)
End Function
Private privateMinValue As Double
Public Property MinValue() As Double
Get
Return privateMinValue
End Get
Private Set(ByVal value As Double)
privateMinValue = value
End Set
End Property
Private privateMaxValue As Double
Public Property MaxValue() As Double
Get
Return privateMaxValue
End Get
Private Set(ByVal value As Double)
privateMaxValue = value
End Set
End Property
Private privateAvgValue As Double
Public Property AvgValue() As Double
Get
Return privateAvgValue
End Get
Private Set(ByVal value As Double)
privateAvgValue = value
End Set
End Property
Private privateDate As Date
Public Property [Date]() As Date
Get
Return privateDate
End Get
Private Set(ByVal value As Date)
privateDate = value
End Set
End Property
Private Sub New(ByVal [date] As Date, ByVal maxValue As Double, ByVal avgValue As Double, ByVal minValue As Double)
Me.Date = [date]
Me.MaxValue = maxValue
Me.AvgValue = avgValue
Me.MinValue = minValue
End Sub
End Class
End Namespace
#End Region ' #ViewModelAndModel
#region #ChartViewCodeBehind
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace MvvmChart {
public partial class MainWindow : Window {
public DailyWeatherViewModel ViewModel { get; private set; }
public MainWindow() {
InitializeComponent();
ViewModel = new DailyWeatherViewModel();
DataContext = ViewModel;
ViewModel.PropertyChanged += OnViewModelPropertyChanged;
}
private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e) {
DataContext = null;
DataContext = ViewModel;
}
}
class WeatherTemplateSelector : DataTemplateSelector {
public DataTemplate AvgSeriesTemplate { get; set; }
public DataTemplate MinMaxSeriesTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
WeatherItem weatherItem = item as WeatherItem;
if(weatherItem == null) return null;
return weatherItem.IsSelected ? MinMaxSeriesTemplate : AvgSeriesTemplate;
}
}
}
#endregion #ChartViewCodeBehind
#region #ViewModelAndModel
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Media;
using System.Xml.Linq;
namespace MvvmChart {
public class DailyWeatherViewModel : INotifyPropertyChanged {
const string vostokStationName = "Vostok Station";
const string deathValleyName = "Death Valley, NV";
static readonly Color coldColor = Color.FromArgb(255, 0, 0, 255);
static readonly Color hotColor = Color.FromArgb(255, 255, 0, 0);
readonly List<WeatherItem> weather;
public event PropertyChangedEventHandler PropertyChanged;
public List<WeatherItem> Weather { get { return weather; } }
WeatherItem selectedItem;
public WeatherItem SelectedWeather {
get { return selectedItem; }
set {
if(selectedItem == value) return;
if(selectedItem != null)
selectedItem.IsSelected = false;
selectedItem = value;
if (value != null)
value.IsSelected = true;
RaisePropertyChangedEvent("SelectedWeather");
} }
public DailyWeatherViewModel() {
List<WeatherRecord> valleyData = LoadWeatherData("DeathValley.xml");
List<WeatherRecord> vostokData = LoadWeatherData("VostokStation.xml");
weather = new List<WeatherItem>() {
new WeatherItem(valleyData, hotColor, deathValleyName),
new WeatherItem(vostokData, coldColor, vostokStationName),
};
}
List<WeatherRecord> LoadWeatherData(string fileName) {
List<WeatherRecord> items = new List<WeatherRecord>();
XDocument weatherDocument = XDocument.Load(String.Format("../../Data/{0}", fileName));
foreach(XElement element in weatherDocument.Root.Elements("Weather")) {
items.Add(WeatherRecord.Load(element));
}
return items;
}
void RaisePropertyChangedEvent(string propertyName) {
var handler = PropertyChanged;
if (handler != null)
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class WeatherItem : INotifyPropertyChanged {
int averageLineThickness = 2;
bool isSelected = false;
public int AverageLineThickness {
get { return averageLineThickness; }
set {
averageLineThickness = value;
RaisePropertyChanged("AverageLineThickness");
}
}
public bool IsSelected {
get { return isSelected; }
set {
if(isSelected == value) return;
isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
public List<WeatherRecord> Data { get; private set; }
public Color Color { get; private set; }
public string Name { get; private set; }
public WeatherItem(List<WeatherRecord> data, Color color, string name) {
this.Data = data;
this.Color = color;
this.Name = name;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propertyName) {
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public class WeatherRecord {
public static WeatherRecord Load(XElement element) {
CultureInfo culture = CultureInfo.InvariantCulture;
DateTime date = DateTime.Parse(element.Attribute("Date").Value, culture);
double min = Double.Parse(element.Attribute("Min").Value, culture);
double max = Double.Parse(element.Attribute("Max").Value, culture);
double avg = Double.Parse(element.Attribute("Avg").Value, culture);
return new WeatherRecord(date, max, avg, min);
}
public double MinValue { get; private set; }
public double MaxValue { get; private set; }
public double AvgValue { get; private set; }
public DateTime Date { get; private set; }
WeatherRecord(DateTime date, double maxValue, double avgValue, double minValue) {
this.Date = date;
this.MaxValue = maxValue;
this.AvgValue = avgValue;
this.MinValue = minValue;
}
}
}
#endregion #ViewModelAndModel