2

I’m having some problems with OxyPlot that I have not been able to resolve through their documentation or other searches. I’m working on a wpf application that will allow the user to open a .csv with a button-click event, then perform some math and report back some useful information. I’d like to plot some of the generated data hence OxyPlot. For some reason I cannot get the plot to populate, when the code that generates it, is within the button click event. To illustrate here is a smaller example: This code works (xaml):

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:oxy="http://oxyplot.org/wpf"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApplication1"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="20,20,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
    <Grid HorizontalAlignment="Left" Height="255" Margin="20,47,0,0" VerticalAlignment="Top" Width="477">
        <oxy:PlotView Model="{Binding ScatterModel}"/>
    </Grid>
</Grid>

with this:

public partial class MainWindow : Window
{
    public MainWindow()
    {
       InitializeComponent();
        DataContext = this;
        var tmp = new PlotModel { Title = "Scatter plot", Subtitle = "y = x" };
        var s2 = new LineSeries
        {
            StrokeThickness = 1,
            MarkerSize = 1,
            MarkerStroke = OxyColors.ForestGreen,
            MarkerType = MarkerType.Plus
        };

        for (int i = 0; i < 100; i++)
        {
            s2.Points.Add(new DataPoint(i, i));
        }
        tmp.Series.Add(s2);
        this.ScatterModel = tmp;
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {

    }
    public PlotModel ScatterModel { get; set; }

And produces this: Plot Working

But, without changing the xaml, if I copy/paste the code beneath the button click event:

public partial class MainWindow : Window
{
    public MainWindow()
    {
       InitializeComponent();

    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        DataContext = this;
        var tmp = new PlotModel { Title = "Scatter plot", Subtitle = "y = x" };
        var s2 = new LineSeries
        {
            StrokeThickness = 1,
            MarkerSize = 1,
            MarkerStroke = OxyColors.ForestGreen,
            MarkerType = MarkerType.Plus
        };

        for (int i = 0; i < 100; i++)
        {
            s2.Points.Add(new DataPoint(i, i));
        }
        tmp.Series.Add(s2);
        this.ScatterModel = tmp;
    }
    public PlotModel ScatterModel { get; set; }

The plot never generates: Not working:

I’ve tried moving DataContext = this; back up to public MainWindow(), and vice-versa with InitializeComponent(); no change. I’ve also tried defining

<Window.DataContext>
    <local:MainWindow/>
</Window.DataContext>

in the xaml but that throws an exception/infinite loop error during build. Something simple I fear I'm not getting about OxyPlot implementation?

Thanks! CSMDakota

1
  • It has nothing to do with the OxyPlot. You need to implement INotifyPropertyChanged, so your ScatterModel will get updated after the Button Click. The loop comes from the line <local:MainWindow/>. In MVVM it would be <local:ViewModel/>. Commented Nov 1, 2016 at 17:26

1 Answer 1

3

INotifyPropertyChanged keeps your view in sync with the program's state. One way to do this is by implementing a ViewModel (the MVVM pattern).

So let's create one. ViewModelBase introduces OnPropertyChanged(), the method that updates ScatterModel.

ViewModels.cs

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using OxyPlot;

namespace WpfApplication1
{
    public class ViewModel : ViewModelBase
    {
        private PlotModel _scatterModel;
        public PlotModel ScatterModel
        {
            get { return _scatterModel; }
            set
            {
                if (value != _scatterModel)
                {
                    _scatterModel = value;
                    OnPropertyChanged();
                }
            }
        }
    }

    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] String propName = null)
        {
            // C#6.O
            // PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
            if (PropertyChanged != null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    }
}

In MainWindow.xaml you can now add

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        var tmp = new PlotModel { Title = "Scatter plot", Subtitle = "y = x" };
        var s2 = new LineSeries
        {
            StrokeThickness = 1,
            MarkerSize = 1,
            MarkerStroke = OxyColors.ForestGreen,
            MarkerType = MarkerType.Plus
        };

        for (int i = 0; i < 100; i++)
        {
            s2.Points.Add(new DataPoint(i, i));
        }
        tmp.Series.Add(s2);
        ViewModel.ScatterModel = tmp;
    }

    // C#6.O
    // public ViewModel ViewModel => (ViewModel)DataContext;
    public ViewModel ViewModel
    {
        get { return (ViewModel)DataContext; }
    }
}

Note we're no longer setting DataContext = this, which is considered bad practice. In this case the ViewModel is small, but as a program grows this way of structuring pays off.

Sign up to request clarification or add additional context in comments.

1 Comment

Funk, Excellent! Thank you for the detailed answer, I followed your instructions and it is working now as intended. Before I go back over to my main program I will make sure I understand what you did here, I still need to study it carefully. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.