6

When setting up an OxyPlot plot view, you can either define the plot explicitly through various controls, or set it up through a binding to a PlotModel.

As such, in the first case, the XAML for a plot of two LineSeries objects could look something like

<oxy:Plot Title="Some plot">
    <oxy:Plot.Axes>
        <oxy:LinearAxis Position="Left" />
        <oxy:LinearAxis Position="Bottom" />
    </oxy:Plot.Axes>
    <oxy:Plot.Series>
        <oxy:LineSeries ItemsSource="{Binding ActualSeriesData1}" DataFieldX="X" DataFieldY="Y"/>
        <oxy:LineSeries ItemsSource="{Binding ActualSeriesData2}" DataFieldX="X" DataFieldY="Y"/>
    </oxy:Plot.Series>
</oxy:Plot>

with a very thin view-model. On the other hand, in the second case, I would simply have something like

<oxy:PlotView Model="{Binding SomePlotModel}" />

and construct the actual plot in the view-model. There are pros and cons to both setups, but I find that the first approach generally works better when I know beforehand what I actually want to plot, whereas the second approach allows for dynamic changes to the plot contents.

My issue is the following: For the first case I know how to add general styling to all plots. If, for instance, I want them to make them look Seaborn-like, I would just add something like

<x:Array Type="Color" x:Key="SeabornColors">
    <Color>#4c72b0</Color>
    <Color>#55a868</Color>
    <Color>#c44e52</Color>
    <Color>#8172b2</Color>
    <Color>#ccb974</Color>
    <Color>#64b5cd</Color>
</x:Array>
<Style TargetType="oxy:Plot">
    <Setter Property="PlotAreaBackground" Value="#EBEBF2" />
    <Setter Property="PlotAreaBorderThickness" Value="0" />
    <Setter Property="TitleFont" Value="Segoe UI" />
    <Setter Property="TitleFontWeight" Value="Normal" />
    <Setter Property="DefaultColors" Value="{StaticResource SeabornColors}"/>
</Style>
<Style TargetType="oxy:LinearAxis">
    <Setter Property="TicklineColor" Value="White" />
    <Setter Property="MajorGridlineColor" Value="White" />
    <Setter Property="MinorGridlineColor" Value="White" />
    <Setter Property="ExtraGridlineColor" Value="White" />
    <Setter Property="AxislineColor" Value="White" />
    <Setter Property="TitleColor" Value="Black" />
    <Setter Property="TextColor" Value="Black" />
    <Setter Property="Font" Value="Segoe UI" />
    <Setter Property="TitleFont" Value="Segoe UI" />
    <Setter Property="TickStyle" Value="None" />
    <Setter Property="MajorGridlineStyle" Value="Solid" />
</Style>

to my ResourceDictionary. How can I achieve the same effect in the second case, i.e. using an oxy:PlotView? I can set some of the general styling properties using a <Style TargetType="oxy:PlotView" />, but how would I style, say, all LineSeries contained within a Series within a Model within a PlotView?

fuglede
  • 17,388
  • 2
  • 54
  • 99
  • I've had the same problem, and the only way I was able to solve it is by using code behind to set the properties on the PlotModels. It still separates the design from the logic, but unfortunately not using XAML or styles. – Herman Mar 27 '17 at 20:19
  • @Herman: Is there a straightforward way of doing that so that it works for all PlotModels in the application? That's one of the nice things about just setting it in a style. – fuglede Mar 27 '17 at 20:25
  • 1
    The best way I could come up with is creating a method for each style that takes a PlotModel as argument, then iterate over all plot models and call the method for each PlotModel. Theoretically, you could find all PlotViews in a Window and extract the PlotModel but in my case my ViewModel already has an array of the plot models so that is simpler. – Herman Mar 28 '17 at 13:22
  • I think your question is not specific enough. Whenever using a `PlotView` / `PlotModel` / `Plot`, nothing is ever stopping you from re-using your XAML resources, either as a `StaticResource` or through `Application.Current.Resources["SomeResourceKey"]` in code-behind. Bear in mind though, that there might be incompatible types, for instance, between `Color` and `OxyColor`. And also between `OxyPlot.Wpf.LinearAxis` and `OxyPlot.Axes.LinearAxis` ...... – jsanalytics Apr 07 '17 at 12:50
  • ...... just don't expect things to be immediately and _**automagically**_ interchangeable in all possible scenarios...:O) Pick a particular scenario and be specific about what you're not being able to do. – jsanalytics Apr 07 '17 at 12:50
  • @jstreet: The issue is that if I add a XAML resource to be used for all LinearAxes, they will not be applied to those axes that are within a PlotView, but only to those that are defined "by" hand as in the first block of code. – fuglede Apr 23 '17 at 09:02
  • @fuglede : understood... but for your second case, you showed no attempt to reuse your XAML resources in your `SomePlotModel`. – jsanalytics Apr 23 '17 at 15:29
  • @jstreet: The attempt was to have what is done in the first case work in the second case as well; that is define a style with `TargetType="oxy:LinearAxis"` in the hopes that that would style the LinearAxes in a `PlotModel` (and the issue is that it does not). Is there any way to set up a piece of XAML to make this happen? – fuglede Apr 23 '17 at 15:56
  • @fuglede : Can you post your **code** attempt of doing that? – jsanalytics Apr 23 '17 at 17:05
  • @jstreet: The style for `LinearAxis` is what is contained in the final block of XAML code, and what I would have expected to work. – fuglede Apr 23 '17 at 17:27
  • @fuglede : Ok, but post the **code** where you create your `SomePlotModel`. – jsanalytics Apr 23 '17 at 17:39
  • @jstreet: The question is about how to achieve the styling using XAML, though. Here, `SomePlotModel` could be any `PlotModel`, with a couple of `LinearAxis` added to it through `PlotModel.Axes.Add`; something similar to https://stackoverflow.com/questions/24938689/how-to-plot-multiple-lineseries-on-an-oxyplot-chart#24938963, say. Note, also, that the styling should be applied to the relevant elements in all PlotModels in the application. – fuglede Apr 23 '17 at 17:47
  • Ok, I'm trying to guide you to the answer but you're not cooperating with me...:O) – jsanalytics Apr 23 '17 at 17:50
  • I'll say this though: `OxyPlot` gives you two options: 1) `MVVM` approach using `` 2) Code-behind approach using `PlotView` and `PlotModel`. You can pick either one of those and go with it. If you try to mix up aspects from one approach into the other... well, it's not gonna work and there's no reason it should. That's why there are 2 approaches...:O) – jsanalytics Apr 23 '17 at 17:59
  • In this example code, what is 'oxy' defined as in the namespaces at the top? I have xmlns:oxy="clr-namespace:OxyPlot.Xamarin.Forms;assembly=OxyPlot.Xamarin.Forms" But I get the error "Type Plot not found in xmlns:oxy="clr-namespace:OxyPlot.Xamarin.Forms;assembly=OxyPlot.Xamarin.Forms" – Rob L Jul 31 '19 at 05:13
  • 1
    @RobL Depends on the type of project; since this one was about WPF in particular, we'd do `xmlns:oxy="clr-namespace:OxyPlot.Wpf;assembly=OxyPlot.Wpf"`. I don't know what the relevant types are called in `Xamarin.Forms`, but if it doesn't have a `Plot`, I guess you'd just use whatever you use in `Xamarin.Forms` instead. – fuglede Jul 31 '19 at 06:48

1 Answers1

0

I didn't get through all the comments but some of them seems to already contain links to the solution. I have an example where I'm using MVVM approach and changing those setting. It should help:

In MainWindow.xaml:

<oxy:PlotView Name="TestView" Model="{Binding plotModel}" Grid.Row="0" Grid.Column="1" />

In my MainWindowViewModel.cs:

private PlotModel m_plotModel;
public PlotModel plotModel
{
    get { return m_plotModel; }
    set => SetAndRaisePropertyChanged(ref m_plotModel, value);
}

public UserControlOscilloViewModel()
{
    var signalAxis = new CategoryAxis()
    {
        Position = AxisPosition.Left,
        Minimum = 0,
        MinimumMinorStep = 1,
        AbsoluteMinimum = 0,
        TicklineColor = OxyColors.Transparent,
    };
    var timeAxis = new LinearAxis()
    {
        Position = AxisPosition.Bottom,
        Minimum = 0,
        MinimumMajorStep = 1,
        AbsoluteMinimum = 0,
    };

    plotModel = new PlotModel();            
    plotModel.Axes.Add(signalAxis);
    plotModel.Axes.Add(timeAxis);
}

Then when you are refreshing your model, you can access your axis like this if needed:

CategoryAxis signalAxis = (CategoryAxis)plotModel.Axes.ElementAt(0);
LinearAxis timeAxis = (LinearAxis)plotModel.Axes.ElementAt(1);

and add series like this:

var series = new LineSeries
{
    Color = OxyColors.SeaShell,
    LineStyle = LineStyle.Dash
};
foreach (DataPoint p in ListOfPoints)
{
    series.Points.Add(p);
}
plotModel.Series.Add(series);
plotModel.InvalidatePlot(true);

I'm not addressing exactly what you asked, but I think you have access to all needed objects/members by doing this. I also simplified my code to improve clarity, I hope I didn't break anything when doing so...

Mikitori
  • 589
  • 9
  • 21
  • Hm, yeah, I'm not sure I follow; the main issue I had was the styling of all `PlotModel`s across an application; styling an individual one is straightforward, and ultimately I opted for what's more or less the solution suggested by @Herman, creating a method to style a given `PlotModel` and making sure that that's called for each of the relevant ones. Not ideal but it does the job. – fuglede Jun 23 '18 at 21:12