Search code examples
wpflivecharts

How to add new view when instantiating a new view model


New to WPF and not sure how to programmatically instantiate a new viewmodel that wraps a new chart and its data collection. Right now, it consists of the following, but not sure the optimal way to set it up.

class ChartViewModel
    {
        public ChartViewModel()
        {
            CartesianChart chart = new CartesianChart();
            chart.Series = new SeriesCollection
            {
                new GLineSeries
                {
                    Title = "Pressure",
                    Values = new GearedValues<double>(),
                },
                new GLineSeries
                {
                    Title = "Pulse",
                    Values = new GearedValues<int>(),
                }
            };  
        }
    }

And then, I need to add the new chart to the view. The CartesianChart object is the UIElement and it works as follows when I just test it in main window without this class.

stackPanel.Children.Add(chart);

But the class can't access the xaml it seems and I can't add the actual view model class since thats not a UIElement, only the chart is. Basically need to create a new chart instance every time the previous chart fills up with something like this:

ChartViewModel tempChart = new ChartViewModel();
chartRepo.Add(tempChart); //chart repo is a list of ChartViewModels

So it needs its own SeriesCollection and UIElement. Thanks for any recommendations.


Solution

  • If you want to dynamically add new charts you have to make use of DataTemplate to template the chart data.

    The DataTemplate which consists of the chart is bound to a ChartDataModel. We can use a ListView to display the charts (data templates). A view model ChartViewModel serves as the ListView.ItemsSource and holds a set of ChartData.
    Each ChartData maps to a new chart.

    Whenever you create a new ChartDataModel in the ChartViewModel and add it to ChartModels, the ListView will automatically create a new chart.

    The view:

    <ListView ItemsSource="{Binding ChartModels}">
      <ListView.DataContext>
        <ChartViewModel />
      </ListView.DataContext>
    
      <ListView.ItemTemplate>
        <DataTemplate DataType="ChartDataModel">
          <CartesianChart>
            <CartesianChart.Series>
              <LineSeries Title="Pressure" Values="{Binding PressureValues}" />
              <LineSeries Title="Pulse" Values="{Binding PulseValues}" />
            </CartesianChart.Series>
          </CartesianChart>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
    

    The model:

    class ChartDataModel
    {
      public ChartDataModel()
      {
        this.PressureValues = new ChartValues<double>();
        this.PulseValues = new ChartValues<double>();
      }
    
      public ChartValues<double> PressureValues { get; set; }
      public ChartValues<double> PulseValues { get; set; }
    }
    

    The view model:

    class ChartViewModel : INotifyPropertyChanged
    {
      public ChartViewModel()
      {
        this.ChartModels = new ObservableCollection<ChartDataModel>();
        CreateNewChart();
      }
    
      private void CreateNewChart()
      {
        var newChartDataModel = new ChartDataModel()
        {
          PressureDataValues = new ChartValues<double>()
          {
            10, 20, 30, 40, 50
          },
          PulseDataValues = new ChartValues<double>()
          {
            100, 200, 300, 400, 500
          }
        };
    
        this.ChartModels.Add(newChartDataModel);
      } 
    
      private ObservableCollection<ChartDataModel> chartModels;
      public ObservableCollection<ChartDataModel> ChartModels
      {
        get => this.chartModels;
        set
        {
          if (Equals(value, this.chartModels)) return;
          this.chartModels = value;
          OnPropertyChanged();
        }
      }
    
      public event PropertyChangedEventHandler PropertyChanged;
    
      [NotifyPropertyChangedInvocator]
      protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
      {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
    }