I am using xamdataChart with multiple datachart, scatterline/scattercontour etc. I would like to dynamically add series to the chart via MVVM.
I saw a solution for this but it is quite old, from 8 years ago. I modified it a little to accept ScatterContours but I am having a problem onValueMemberPath.
I nee to have 3 datachart,two off them add scatter line, and the last one add scatter contour.
Can someone notify the problem, or simple provide my a more recent solution to the problem?
Thank you!
ps: I couln'tupload my zip so hre is my code
Main window.xaml
<Window xmlns:ig="http://schemas.infragistics.com/xaml" x:Class="SeriesBinder.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SeriesBinder" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="50" /> </Grid.RowDefinitions> <ig:XamDataChart Margin="2" > <local:SeriesBinderBehavior.SeriesBinder> <local:SeriesBinderInfo SeriesSource="{Binding SeriesSource}" ItemsSourcePath="Source" XMemberPath="XPath" YMemberPath="YPath" TypePath="Type"/> </local:SeriesBinderBehavior.SeriesBinder> </ig:XamDataChart> <ig:XamDataChart Grid.Row="1" > <local:SeriesBinderBehavior.SeriesBinder> <local:SeriesBinderInfo SeriesSource="{Binding SeriesSource2}" ItemsSourcePath="Source" XMemberPath="XPath" YMemberPath="YPath" TypePath="Type"/> <!-- ValuMemberPathmissing --> </local:SeriesBinderBehavior.SeriesBinder> </ig:XamDataChart> <Button Content="Add Series" Command="{Binding AddSeries}" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="12,15,0,0" Name="button1" VerticalAlignment="Top" Width="75" /> <Button Content="Remove Series" Command="{Binding RemoveSeries}" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="93,15,0,0" Name="button2" VerticalAlignment="Top" Width="94" /> <Button Content="Clear Series" Command="{Binding ClearSeries}" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="193,15,0,0" Name="button3" VerticalAlignment="Top" Width="75" /> <Button Content="Reset Source" Command="{Binding ResetSeries}" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="274,15,0,0" Name="button4" VerticalAlignment="Top" Width="75" /> </Grid> </Window>
Main window.cs
using Infragistics.Controls.Charts; using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Windows; using System.Windows.Data; using System.Windows.Input; namespace SeriesBinder { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new MainViewModel(); } } #region Series Binder public class SeriesBinderBehavior : DependencyObject { public static readonly DependencyProperty SeriesBinderProperty = DependencyProperty.RegisterAttached( "SeriesBinder", typeof(SeriesBinderInfo), typeof(SeriesBinderBehavior), new PropertyMetadata(null, (o, e) => { OnSeriesBiderChaged(o as XamDataChart, e.OldValue as SeriesBinderInfo, e.NewValue as SeriesBinderInfo); })); public static SeriesBinderInfo GetSeriesBinder(DependencyObject target) { return target.GetValue(SeriesBinderProperty) as SeriesBinderInfo; } public static void SetSeriesBinder(DependencyObject target, object value) { target.SetValue(SeriesBinderProperty, value); } private static void OnSeriesBiderChaged( XamDataChart chart, SeriesBinderInfo oldVal, SeriesBinderInfo newVal) { if (chart == null) { return; } if (oldVal != null) { oldVal.DetachOwner(chart); } if (newVal != null) { newVal.AttachOwner(chart); } } } public class SeriesBinderInfo : FrameworkElement { #region Fields private XamDataChart _owner; Dictionary<object, Series> _seriesObjectMapper; #endregion #region Constructor public SeriesBinderInfo() { _seriesObjectMapper = new Dictionary<object, Series>(); } #endregion #region Initializing Methods public void AttachOwner(XamDataChart chart) { if (_owner != null) { DetachOwner(_owner); } _owner = chart; SetBinding(DataContextProperty, new Binding("DataContext") { Source = _owner }); } public void DetachOwner(XamDataChart _owner) { _owner = null; _seriesObjectMapper.Clear(); ClearValue(DataContextProperty); } #endregion #region Properties #region SeriesSource private const string SeriesSourcePropertyName = "SeriesSource"; public IEnumerable SeriesSource { get { return (IEnumerable)GetValue(SeriesSourceProperty); } set { SetValue(SeriesSourceProperty, value); } } public static readonly DependencyProperty SeriesSourceProperty = DependencyProperty.Register( SeriesSourcePropertyName, typeof(IEnumerable), typeof(SeriesBinderInfo), new PropertyMetadata(null)); #endregion #region ItemsSourcePath private const string ItemsSourcePathPropertyName = "ItemsSourcePath"; public string ItemsSourcePath { get { return (string)GetValue(ItemsSourcePathProperty); } set { SetValue(ItemsSourcePathProperty, value); } } public static readonly DependencyProperty ItemsSourcePathProperty = DependencyProperty.Register( ItemsSourcePathPropertyName, typeof(string), typeof(SeriesBinderInfo), new PropertyMetadata(null)); #endregion #region XMemberPath private const string XMemberPathPropertyName = "XMemberPath"; public string XMemberPath { get { return (string)GetValue(XMemberPathProperty); } set { SetValue(XMemberPathProperty, value); } } public static readonly DependencyProperty XMemberPathProperty = DependencyProperty.Register( XMemberPathPropertyName, typeof(string), typeof(SeriesBinderInfo), new PropertyMetadata(null)); #endregion #region YMemberPath private const string YMemberPathPropertyName = "YMemberPath"; public string YMemberPath { get { return (string)GetValue(YMemberPathProperty); } set { SetValue(YMemberPathProperty, value); } } public static readonly DependencyProperty YMemberPathProperty = DependencyProperty.Register( YMemberPathPropertyName, typeof(string), typeof(SeriesBinderInfo), new PropertyMetadata(null)); #endregion #region valueMemberPath private const string ValueMemberPathPropertyName = "ValueMemberPath"; public string ValueMemberPath { get { return (string)GetValue(ValueMemberPathProperty); } set { SetValue(ValueMemberPathProperty, value); } } public static readonly DependencyProperty ValueMemberPathProperty = DependencyProperty.Register( ValueMemberPathPropertyName, typeof(string), typeof(SeriesBinderInfo), new PropertyMetadata(null)); #endregion #region TypePath private const string TypePathPropertyName = "TypePath"; public string TypePath { get { return (string)GetValue(TypePathProperty); } set { SetValue(TypePathProperty, value); } } public static readonly DependencyProperty TypePathProperty = DependencyProperty.Register( TypePathPropertyName, typeof(string), typeof(SeriesBinderInfo), new PropertyMetadata(null)); #endregion #endregion #region Event Handlers void SeriesBinderInfo_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: { object seriesSource = e.NewItems[0]; AddSeries(seriesSource); } break; case NotifyCollectionChangedAction.Remove: { object seriesSource = e.OldItems[0]; RemoveSeries(seriesSource); } break; case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Reset: { GenerateSeries(); } break; default: break; } } #endregion #region Helper Methods private void RemoveSeries(object seriesSource) { Series s = _seriesObjectMapper[seriesSource]; SeriesType type = (SeriesType)seriesSource.GetType().GetProperty(TypePath).GetValue(seriesSource, null); UpdateSeriesProperties(s, type, seriesSource, true, true); _owner.Series.Remove(s); _seriesObjectMapper.Remove(seriesSource); } private void AddSeries(object seriesSource) { SeriesType type = (SeriesType)seriesSource.GetType().GetProperty(TypePath).GetValue(seriesSource, null); Series series = CreateSeries(type, seriesSource); _owner.Series.Add(series); _seriesObjectMapper.Add(seriesSource, series); } private void GenerateSeries() { _owner.Series.Clear(); _owner.Axes.Clear(); foreach (var item in SeriesSource) { AddSeries(item); } } private Series CreateSeries(SeriesType type, object seriesSource) { Series series = null; switch (type) { case SeriesType.LineSeries: { series = new LineSeries(); UpdateSeriesProperties(series, type, seriesSource, true, false); } break; case SeriesType.ColumnSeries: { series = new ColumnSeries(); UpdateSeriesProperties(series, type, seriesSource, true, false); } break; case SeriesType.ScatterSeries: { series = new ScatterSeries(); UpdateSeriesProperties(series, type, seriesSource, true, false); } break; case SeriesType.ScatterLineSeries: { series = new ScatterLineSeries(); UpdateSeriesProperties(series, type, seriesSource, true, false); } break; case SeriesType.ScatterContourSeries: { series = new ScatterContourSeries(); UpdateSeriesProperties(series, type, seriesSource, true, false); } break; default: break; } return series; } private void UpdateSeriesProperties(Series series, SeriesType type, object seriesSource, bool shouldRenewAxes, bool clearOnly) { if (series != null) { switch (type) { case SeriesType.LineSeries: case SeriesType.ColumnSeries: { HorizontalAnchoredCategorySeries category = series as HorizontalAnchoredCategorySeries; category.ClearValue(HorizontalAnchoredCategorySeries.ValueMemberPathProperty); category.ClearValue(HorizontalAnchoredCategorySeries.ItemsSourceProperty); if (!clearOnly) { category.SetBinding(HorizontalAnchoredCategorySeries.ValueMemberPathProperty, new Binding(YMemberPath) { Source = seriesSource }); category.SetBinding(HorizontalAnchoredCategorySeries.ItemsSourceProperty, new Binding(ItemsSourcePath) { Source = seriesSource }); } if (shouldRenewAxes) { if (category.XAxis != null) _owner.Axes.Remove(category.XAxis); if (category.YAxis != null) _owner.Axes.Remove(category.YAxis); if (!clearOnly) { CategoryXAxis xAxis = new CategoryXAxis(); xAxis.SetBinding(CategoryXAxis.ItemsSourceProperty, new Binding(ItemsSourcePath) { Source = seriesSource }); xAxis.Label = "{" + seriesSource.GetType().GetProperty(XMemberPath).GetValue(seriesSource, null) + "}"; NumericYAxis yAxis = new NumericYAxis(); _owner.Axes.Add(xAxis); _owner.Axes.Add(yAxis); category.XAxis = xAxis; category.YAxis = yAxis; } } } break; case SeriesType.ScatterSeries: case SeriesType.ScatterLineSeries: { ScatterBase scatter = series as ScatterBase; scatter.ClearValue(ScatterBase.XMemberPathProperty); scatter.ClearValue(ScatterBase.YMemberPathProperty); scatter.ClearValue(ScatterBase.ItemsSourceProperty); if (!clearOnly) { scatter.SetBinding(ScatterBase.XMemberPathProperty, new Binding(XMemberPath) { Source = seriesSource }); scatter.SetBinding(ScatterBase.YMemberPathProperty, new Binding(YMemberPath) { Source = seriesSource }); scatter.SetBinding(ScatterBase.ItemsSourceProperty, new Binding(ItemsSourcePath) { Source = seriesSource }); } if (shouldRenewAxes) { if (scatter.XAxis != null) _owner.Axes.Remove(scatter.XAxis); if (scatter.YAxis != null) _owner.Axes.Remove(scatter.YAxis); if (!clearOnly) { NumericXAxis xAxis = new NumericXAxis(); NumericYAxis yAxis = new NumericYAxis(); _owner.Axes.Add(xAxis); _owner.Axes.Add(yAxis); scatter.XAxis = xAxis; scatter.YAxis = yAxis; } } } break; case SeriesType.ScatterContourSeries: { ScatterContourSeries scatterContour = series as ScatterContourSeries; scatterContour.ClearValue(ScatterTriangulationSeries.XMemberPathProperty); scatterContour.ClearValue(ScatterTriangulationSeries.YMemberPathProperty); scatterContour.ClearValue(ScatterContourSeries.ValueMemberPathProperty); scatterContour.ClearValue(ScatterTriangulationSeries.ItemsSourceProperty); if (!clearOnly) { scatterContour.SetBinding(ScatterTriangulationSeries.XMemberPathProperty, new Binding(XMemberPath) { Source = seriesSource }); scatterContour.SetBinding(ScatterTriangulationSeries.YMemberPathProperty, new Binding(YMemberPath) { Source = seriesSource }); scatterContour.SetBinding(ScatterContourSeries.ValueMemberPathProperty, new Binding(ValueMemberPath) { Source = seriesSource }); scatterContour.SetBinding(ScatterTriangulationSeries.ItemsSourceProperty, new Binding(ItemsSourcePath) { Source = seriesSource }); } if (shouldRenewAxes) { if (scatterContour.XAxis != null) _owner.Axes.Remove(scatterContour.XAxis); if (scatterContour.YAxis != null) _owner.Axes.Remove(scatterContour.YAxis); if (!clearOnly) { NumericXAxis xAxis = new NumericXAxis(); NumericYAxis yAxis = new NumericYAxis(); _owner.Axes.Add(xAxis); _owner.Axes.Add(yAxis); scatterContour.XAxis = xAxis; scatterContour.YAxis = yAxis; // scatterContour.ValueMemberPath = } } ; } break; default: break; } } } #endregion #region Base Overrides protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { switch (e.Property.Name) { case SeriesSourcePropertyName: { if (e.OldValue != null && e.OldValue is INotifyCollectionChanged) (e.OldValue as INotifyCollectionChanged).CollectionChanged -= SeriesBinderInfo_CollectionChanged; if (e.NewValue != null && e.NewValue is INotifyCollectionChanged) (e.NewValue as INotifyCollectionChanged).CollectionChanged += SeriesBinderInfo_CollectionChanged; GenerateSeries(); } break; case XMemberPathPropertyName: case YMemberPathPropertyName: case ValueMemberPathPropertyName: case TypePathPropertyName: { if (SeriesSource != null) GenerateSeries(); } break; default: break; } base.OnPropertyChanged(e); } #endregion } #endregion #region View Model public class MainViewModel : PropertyChangedNotifier { private ObservableCollection<SeriesViewModel> seriesSource; private ObservableCollection<SeriesViewModel> seriesSource2; public MainViewModel() { SeriesSource = new ObservableCollection<SeriesViewModel> { new SeriesViewModel { Source = ChartData.GenerateData(10), Type = SeriesType.ColumnSeries, XPath = "Date", YPath = "Value", }, new SeriesViewModel { Source = ChartData.GenerateData(22), Type = SeriesType.LineSeries, XPath = "Label", YPath = "Value", }, new SeriesViewModel { Source = ChartData.GenerateData(20), Type = SeriesType.ScatterSeries, XPath = "Index", YPath = "Value", }, new SeriesViewModel { Source = ChartData.GenerateData(15), Type = SeriesType.ScatterLineSeries, XPath = "Index", YPath = "Value", }, }; SeriesSource2 = new ObservableCollection<SeriesViewModel>(); } public ObservableCollection<SeriesViewModel> SeriesSource { get { return seriesSource; } set { seriesSource = value; OnPropertyChagned("SeriesSource"); } } public ObservableCollection<SeriesViewModel> SeriesSource2 { get { return seriesSource2; } set { seriesSource2 = value; OnPropertyChagned("SeriesSource2"); } } public ICommand AddSeries { get { return new RelayCommand<object>( new Action<object>( (obj) => { SeriesSource2.Add( new SeriesViewModel { Source = ChartData.GenerateData(10), Type = SeriesType.ScatterContourSeries, XPath = "Index", YPath = "Index", ValueMemberPath = "Value" }); SeriesSource.Add( new SeriesViewModel { Source = ChartData.GenerateData(10), Type = SeriesType.ScatterLineSeries, XPath = "Index", YPath = "Value", ValueMemberPath = "Value" }); })); } } public ICommand RemoveSeries { get { return new RelayCommand<object>( new Action<object>( (obj) => { if (SeriesSource.Count > 0) SeriesSource.RemoveAt(0); })); } } public ICommand ClearSeries { get { return new RelayCommand<object>( new Action<object>( (obj) => { SeriesSource.Clear(); })); } } public ICommand ResetSeries { get { return new RelayCommand<object>( new Action<object>( (obj) => { SeriesSource = new ObservableCollection<SeriesViewModel> { new SeriesViewModel { Source = ChartData.GenerateData(10), Type = SeriesType.ColumnSeries, XPath = "Date", YPath = "Value", }, new SeriesViewModel { Source = ChartData.GenerateData(22), Type = SeriesType.LineSeries, XPath = "Label", YPath = "Value", }, new SeriesViewModel { Source = ChartData.GenerateData(20), Type = SeriesType.ScatterSeries, XPath = "Index", YPath = "Value", }, new SeriesViewModel { Source = ChartData.GenerateData(15), Type = SeriesType.ScatterLineSeries, XPath = "Index", YPath = "Value", }, }; })); } } } public class SeriesViewModel : PropertyChangedNotifier { private SeriesType type; public SeriesType Type { get { return type; } set { type = value; OnPropertyChagned("Type"); } } private IEnumerable source; public IEnumerable Source { get { return source; } set { source = value; OnPropertyChagned("Source"); } } private string xPath; public string XPath { get { return xPath; } set { xPath = value; OnPropertyChagned("XPath"); } } private string yPath; public string YPath { get { return yPath; } set { yPath = value; OnPropertyChagned("XPath"); } } private string valueMemberPath; public string ValueMemberPath { get { return valueMemberPath; } set { valueMemberPath = value; OnPropertyChagned("ValueMemberPath"); } } } public class ChartData { public DateTime Date { get; set; } public int Index { get; set; } public string Label { get; set; } public double Value { get; set; } public static ObservableCollection<ChartData> GenerateData(int count) { ObservableCollection<ChartData> data = new ObservableCollection<ChartData>(); Random r = new Random(); ChartData data0 = new ChartData { Date = DateTime.Now, Index = 0, Value = r.Next(20, 40), Label = "Item 0" }; data.Add(data0); for (int i = 1; i < count; i++) { ChartData dat = new ChartData { Date = DateTime.Now.AddDays(i), Index = i, Label = "Item " + i, }; if (r.NextDouble() > .5) dat.Value = data[i - 1].Value + r.NextDouble(); else dat.Value = data[i - 1].Value - r.NextDouble(); data.Add(dat); } return data; } } public class PropertyChangedNotifier : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChagned(string name) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name)); } } public enum SeriesType { LineSeries, ColumnSeries, ScatterSeries, ScatterLineSeries, ScatterContourSeries } public class RelayCommand<T> : ICommand { #region Declarations readonly Predicate<T> _canExecute; readonly Action<T> _execute; #endregion #region Constructors /// <summary> /// Initializes a new instance of the <see cref="RelayCommand<T>"/> class and the command can always be executed. /// </summary> /// <param name="execute">The execution logic.</param> public RelayCommand(Action<T> execute) : this(execute, null) { } /// <summary> /// Initializes a new instance of the <see cref="RelayCommand<T>"/> class. /// </summary> /// <param name="execute">The execution logic.</param> /// <param name="canExecute">The execution status logic.</param> public RelayCommand(Action<T> execute, Predicate<T> canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } #endregion #region ICommand Members public event EventHandler CanExecuteChanged { add { if (_canExecute != null) CommandManager.RequerySuggested += value; } remove { if (_canExecute != null) CommandManager.RequerySuggested -= value; } } [DebuggerStepThrough] public virtual Boolean CanExecute(Object parameter) { return _canExecute == null ? true : _canExecute((T)parameter); } public virtual void Execute(Object parameter) { _execute((T)parameter); } #endregion } #endregion }
Thank you , this is exactly what I want
https://www.infragistics.com/community/forums/f/ultimate-ui-for-wpf/120715/get-the-handle-of-marker-inside-scatter-series
can u please have a look and help me out to resolve the issue