Back to Devexpress

How to: Bind Series to View Models Using the Item Template Selector

wpf-118933-controls-and-libraries-charts-suite-chart-control-examples-providing-data-how-to-bind-series-to-view-models-using-the-item-template-selector.md

latest19.0 KB
Original Source

How to: Bind Series to View Models Using the Item Template Selector

  • Jun 07, 2019
  • 8 minutes to read

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.

To bind series view models to a chart, use the Diagram.SeriesItemsSource property. To configure how the series view model is converted to a series on a chart, use Diagram.SeriesItemTemplate or Diagram.SeriesItemTemplateSelector. In this example, the Template Selector is used to convert the selected series type from Line to Range Area.

View Example

xaml
<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:&#10;Min: {V1}°C&#10;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>
vb
#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
vb
#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
csharp
#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
csharp
#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