Customizing the bindings
First of all, keep up the good work, even if my poor C# skills make me look for hours for every little steps, Oxyplot seems to be the perfect fit for what I am trying to do.
I understand from the doc (http://docs.oxyplot.org/en/latest/controllers/index.html) that I can replace/delete the default mouse event and add new ones. My aim is to reaffect the Show 'Tracker' from left mouse to mous hover. The aim is to always see it as long as the pointer is on the graph.
So it seems pretty easy to unassign the left click with
myController.UnbindMouseDown(OxyMouseButton.Left);
But I can't get the syntax for AddHoverManipulator
myController.AddHoverManipulator(IView view, ManipulatorBase<OxyMouseEventArgs> manipulator, OxyMouseEventArgs args)
Basically i think I just get what view refers to (my PlotView control right? ==> this.PlotViewName)
Do I have to declare something before? What is a manipulator? What do I have to put for args?
Any help would be greatly appreciated.
Ciao
Switching PlotModel in RunTIme
After binding to a certain instance of PlotModel I'm trying to switch a plot model in run time.
In other words, I assign a new created PlotModel to the same variable that was used with the UI control. However, the graph control doesn't seems to adjust (and I do call InvalidatePlot(true)).
To be more specific,
<oxy:Plot Model="{Binding Models.MyGraphSwitcher1.MyPlotModel}" />
"MyPlotModel" above is a "OxyPlot.PlotModel" and it is changed by the ViewModel - "MyGraphSwitcher1". The problem is that when "MyPlotModel" is set to point to a new PlotModel instance the UI control is not updated. I believe it has to do with the bindings.
I wonder if there is some way to make this work?
Thanks,
Mike
Broken line chart
tongbong wrote at 2012-09-22 18:30:
Hi there,
first of all I am very glad to know about OxyPlot. It seams promising.
However, I do not see any example about broken line chart as in Visifire (see http://www.visifire.com/silverlight_line_charts_gallery.php).
Is there any plan to support that feature?
Cheers, Francois
objo wrote at 2012-09-24 10:37:
Currently, invalid data points (null / NaN) creates an "invisible" broken line (no dashed line is shown). I agree there could be an option to draw a dashed line. Vote on http://oxyplot.codeplex.com/workitem/10007
tongbong wrote at 2012-10-01 18:28:
For those who wonders how to code the invisible broken line, here is an example:
LineSeries series = new LineSeries("MySerie"); series.Points.Add(new DataPoint(5, 10)); series.Points.Add(new DataPoint(10, 10)); series.Points.Add(new DataPoint(Double.NaN, Double.NaN)); series.Points.Add(new DataPoint(20, 10)); series.Points.Add(new DataPoint(30, 10));
Angled Labels in CategoryAxis
ecascketta wrote at 2014-07-09 01:10:
So I posted this problem here as well.
Basically I have a column chart and it's cutting off the bottom axis labels when I export to a png. All of the labels have the potential to be quite long so I'm trying to angle them at 30 or 45 degrees but they keep getting cut off.
I ensured that PlotModel.PlotMargins.Bottom == NaN and I've fiddled with a bunch of properties on CategoryAxis and PlotModel but have had no luck.
It also seems like this problem has existed for a while?
Just like I posted above, I put details here.
Am I doing something wrong or is this a longstanding issue that can only be resolved with a dirty hack?
objo wrote at 2014-07-10 12:14:
Slow redraws with noisy data in WPF
tevo wrote at 2013-09-12 15:58:
tevo wrote at 2013-09-12 15:58:
I recently discovered a seemingly simple data set that slows down OxyPlot redraws to 1 or 2 FPS. I'm using OxyPlot.WPF 2013.1.67.1. I have a plot that takes up a full window and the window is nearly full screen (1680 x 1050). There is one LineSeries with 500 points generated like so:
const int n = 500;
var points = new List<IDataPoint>(n);
var rng = new System.Random();
for (int i = 0; i < n; i++)
points.Add(new DataPoint(i + 1, rng.NextDouble()));
As I adjust the window size, OxyPlot redraw performance is very slow. This was quite surprising since I've plotted LineSeries with 10,000 points before and had much better performance. My only guess as to why it degrades so much is that the noisy data requires
painting more pixels. Does anyone have a better understanding of why performance is so bad in this case? Even better, is there a way to fix my code or OxyPlot?
tevo wrote at 2013-09-12 15:58:
The code is added to the example library.
You can compare performance in WPF, SL and Windows Forms.
Also see the "Performance" examples using a zig-zag (sawtooth) curve.
Random data are difficult to optimize - all pixels must be drawn.
Are you binding to the data points using reflection?
tevo wrote at 2013-09-12 15:59:
I am not binding to the data points. My xaml just has this:
<oxy:Plot Model="{Binding PlotModel}" Title="OxyPlot" />
Then in C# I create the points and dolineSeries.Points = points;
Regarding optimization: Does OxyPlot already skip data points in some cases or are you suggesting something new?everytimer wrote at 2013-09-12 16:57:
tevo wrote at 2013-09-12 17:05:
everytimer wrote at 2013-09-13 21:15:
tevo wrote at 2013-09-16 17:07:
I profiled (sampling method) a very simple test case and found that the most expensive methods are:
- Canvas.Children.Add() (13%) - called in ShapesRenderContext.Add(), which is called most often from ShapesRenderContext.DrawText()
- Canvas.Children.Clear() (12%) - called in Plot.UpdateVisuals()
- TextBlock.Measure() (11%) - called in ShapesRenderContext.DrawText() and ShapesRenderContext.MeasureText()
There may be another way to address all of these problems at once but it would be a major change to OxyPlot. Instead of clearing the canvas and adding new objects for every update, existing objects could be updated and reused. This would eliminate calls to Canvas.Children.Clear(), reduce calls to Canvas.Children.Add() and TextBlock.Measure(), and reduce the number of objects that get created and destroyed. It would be a big change and I'm not even sure it would be a net win. I'd love to hear some thoughts on this idea, especially from objo.
For now, I'm going to seriously consider everytimer's suggestion to use WinForms. Performance is significantly better and probably acceptable for my app.
Lastly, I found some interesting links while looking into WPF performance. Maybe others will be interested as well...
objo wrote at 2013-09-17 00:03:
I have tried:
- using DrawingContext (not much performance gain and problems with aliasing/antialiasing if I remember correctly)
- disconnect Canvas from visual tree when updating (minor improvement, the DisconnectCanvasWhileUpdating property should be enabled in the Plot control)
- setting CacheMode on the Canvas (does not look good)
Avoiding Add and Clear of the Canvas.Children collection would make a big difference - but it is difficult to avoid this since the order of the elements is important. I think this improvement needs some logic to insert new and remove old elements. I guess this is also needed in the major refactoring (retained graphics mode) you suggest.
Measurement of text should be avoided in the DrawText method, it should be possible to solve this by applying horizontal/vertical alignment only.
FontFamily could be cached - but I am not sure if this is noticeable.
Here is another interesting link:
tevo wrote at 2013-09-17 20:35:
- Enabled DisconnectCanvasWhileUpdating. I couldn't see or measure any change in performance.
- Implemented a FontFamily cache (just like the brush cache). No change.
- Eliminate all calls to TextBlock.Measure() by commenting out the whole if{} block in DrawText(). Still no change!
- Turn off all ticks and tick labels on the axes. Still no change???
I think all of this means that optimizing the existing code just can't make that much of a difference. So what about switching to retained graphics mode? My tests don't give any data about that. Is there some way to test the concept without doing a major rewrite first?
a5r wrote at 2013-09-18 14:29:
http://msdn.microsoft.com/en-us/library/ee230083.aspx
and http://msdn.microsoft.com/en-us/library/ee230085.aspx
tevo wrote at 2013-09-18 16:15:
I also did another test where I let Plot.UpdateModelAndVisuals() run normally the first 10 times (just to get the plot drawn on screen), and after that always immediately returned. This is effectively a retained mode because the canvas and its children get reused for each frame. The plot was not changing at all but redraw performance was identical. Then I turned on the BitmapCache and redraws became nearly instant. The cache could do its job now because the canvas wasn't changing.
My conclusion is that WPF rendering is really slow in some cases, and any plot update requires WPF to render. I don't think there's any way to work around it.
Dang.
tevo wrote at 2013-09-19 16:03:
I couldn't help myself and kept on playing with my simple test plot of random data between 0 and 1. I varied the number of points to find out if performance gradually decreased or if there was a cliff somewhere. On my machine, performance is good up to about 50 points and then degrades rapidly. 200 points is pretty darn bad.
Then for some reason I decided to try two series of 50 points and performance was still good. I added two more series of 50 points and performance only degraded a little. These four series with 200 total points performed much, much better than one series of 200 points. Interesting.
That made me look into what was actually being used to draw these lines. ShapesRenderContext.DrawLine() uses a System.Windows.Shapes.Polyline. I thought that maybe the number of points in a polyline was the limiting factor, so I updated the method to make multiple polylines with a limited number of points. On a test case with 500 points, this change results in a huge speedup. It's at least 10x faster redraws! I experimented with the number of points to allow in a polyline, and I think there was improvement all the way down to about 16.
So for my test case this change is unquestionably a huge improvement. I tested "LineSeries, 100k points" from the ExampleBrowser and unfortunately my change degraded performance a bit. I think that's because 100k / 16 = 6250 polylines which is a lot. I tried to come up with a way to balance the number of points per polyline and the number of polylines and it seems to be working well now. Here's the updated DrawLine() method:
public void DrawLine(
IList<ScreenPoint> points,
OxyColor stroke,
double thickness,
double[] dashArray,
OxyPenLineJoin lineJoin,
bool aliased)
{
// balance the number of points per polyline and the number of polylines
var numPointsPerPolyline = Math.Max(points.Count / MaxPolylinesPerLine, MinPointsPerPolyline);
var polyline = new Polyline();
this.SetStroke(polyline, stroke, thickness, lineJoin, dashArray, aliased);
var pc = new PointCollection(numPointsPerPolyline);
foreach (var p in points)
{
pc.Add(p.ToPoint(aliased));
// use multiple polylines with limited number of points to improve WPF performance
if (pc.Count >= numPointsPerPolyline)
{
polyline.Points = pc;
this.Add(polyline);
// start a new polyline at last point so there is no gap
polyline = new Polyline();
this.SetStroke(polyline, stroke, thickness, lineJoin, dashArray, aliased);
var startPoint = pc.Last();
pc = new PointCollection(numPointsPerPolyline);
pc.Add(startPoint);
}
}
if (pc.Count > 1 || points.Count == 1)
{
polyline.Points = pc;
this.Add(polyline);
}
}
And of course you also need to define:/// <summary>
/// The maximum number of polylines per line.
/// </summary>
const int MaxPolylinesPerLine = 64;
/// <summary>
/// The minimum number of points per polyline.
/// </summary>
const int MinPointsPerPolyline = 16;
It was interesting to find that there is already a MaxFiguresPerGeometry variable. It is there there to "limit the number of figures, otherwise drawing errors...", but it seems like a similar thing.
Anyway, are there other things to think about and test or can this go straight into official OxyPlot?
objo wrote at 2013-09-19 23:20:
The only problem is dashed lines, I think. See the new example.
Can you calculate the correct dash offset?
objo wrote at 2013-09-19 23:55:
- not clear the canvas, instead set a child element index = 0
- when creating/adding: check if the canvas contains an element of the given type at the current index, reuse the element if type matches
- otherwise create new instance and replace or add to the canvas
- when finished, remove unused elements from canvas
tevo wrote at 2013-09-20 16:44:
I took a crack at calculating dash offset and it's trickier than I expected. Maybe that's because I'm not sure how Shape.StrokeDashOffset works. MSDN documentation doesn't give details but I did find a book that explains things more. Below is my ugly test code that doesn't quite work yet.
Note that this code does not even try to handle a dashArray with odd length. That new example you added should probably have two lines to verify that even and odd length dash arrays work properly.
public void DrawLine(
IList<ScreenPoint> points,
OxyColor stroke,
double thickness,
double[] dashArray,
OxyPenLineJoin lineJoin,
bool aliased)
{
// balance the number of points per polyline and the number of polylines
var numPointsPerPolyline = Math.Max(points.Count / MaxPolylinesPerLine, MinPointsPerPolyline);
double lineLength = 0;
double dashPatternLength = (dashArray != null) ? dashArray.Sum() : 0;
double[] dashArrayCumsum = null;
if (dashArray != null)
{
dashArrayCumsum = new double[dashArray.Length + 1];
for (int i = 0; i < dashArray.Length; i++)
{
dashArrayCumsum[i + 1] = dashArrayCumsum[i] + dashArray[i];
}
}
var polyline = this.CreateAndAdd<Polyline>();
this.SetStroke(polyline, stroke, thickness, lineJoin, dashArray, 0, aliased);
var pc = new PointCollection(numPointsPerPolyline);
var n = points.Count;
var last = new Point();
for (int i = 0; i < n; i++)
{
var p = points[i].ToPoint(aliased);
pc.Add(p);
if (dashArray != null)
{
if (i == 0)
{
last = p;
}
var delta = p - last;
var dist = Math.Sqrt((delta.X * delta.X) + (delta.Y * delta.Y));
lineLength += dist * thickness;
lineLength %= dashPatternLength;
last = p;
}
// use multiple polylines with limited number of points to improve WPF performance
if (pc.Count >= numPointsPerPolyline)
{
polyline.Points = pc;
if (i < n - 1)
{
// start a new polyline at last point so there is no gap
polyline = this.CreateAndAdd<Polyline>();
double dashOffset = 0;
if (dashArray != null)
{
for (int k = 1; k < dashArrayCumsum.Length; k++)
{
if (lineLength <= dashArrayCumsum[k])
{
dashOffset = (k - 1) + (lineLength - dashArrayCumsum[k - 1]) / (dashArrayCumsum[k] - dashArrayCumsum[k - 1]);
break;
}
}
}
this.SetStroke(polyline, stroke, thickness, lineJoin, dashArray, dashOffset, aliased);
pc = new PointCollection(numPointsPerPolyline) { pc.Last() };
}
}
}
if (pc.Count > 1 || n == 1)
{
polyline.Points = pc;
}
}
objo wrote at 2013-09-21 15:25:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Canvas> <Line X1="100" Y1="100" X2="800" Y2="100" Stroke="Black" StrokeThickness="2" StrokeDashArray="10,5,20,10,30,15"/> <Line X1="110" Y1="110" X2="800" Y2="110" Stroke="Black" StrokeThickness="2" StrokeDashArray="10,5,20,10,30,15" StrokeDashOffset="5"/> <Line X1="130" Y1="120" X2="800" Y2="120" Stroke="Black" StrokeThickness="2" StrokeDashArray="10,5,20,10,30,15" StrokeDashOffset="15"/> <Line X1="150" Y1="130" X2="800" Y2="130" Stroke="Black" StrokeThickness="2" StrokeDashArray="10,5,20,10,30,15" StrokeDashOffset="25"/> <Line X1="250" Y1="140" X2="800" Y2="140" Stroke="Black" StrokeThickness="2" StrokeDashArray="10,5,20,10,30,15" StrokeDashOffset="75"/> <Line X1="350" Y1="150" X2="800" Y2="150" Stroke="Black" StrokeThickness="2" StrokeDashArray="10,5,20,10,30,15" StrokeDashOffset="125"/> </Canvas> </Page>
tevo wrote at 2013-09-24 14:58:
http://i.imgur.com/IHc3PYh.png
Unfortunately, I also discovered a problem with using multiple Polylines to draw one line: LineJoins. The places where one Polyline ends and another begins can have artifacts. Here are some examples.
- http://i.imgur.com/fkV5Wxo.png (look in a vertical line under the red arrow)
- http://i.imgur.com/VHnSQkF.png (points highlighted in yellow)
My only idea to overcome this issue is for Polylines to overlap by two points. That way the end of a Polyline will be covered by the first LineJoin of the next line. This might lead to other problems, however. If the line color is semi-transparent, the overlapping parts would appear to be a different color.
Any other ideas about what to do here?
Here's my current code:
public void DrawLine(
IList<ScreenPoint> points,
OxyColor stroke,
double thickness,
double[] dashArray,
OxyPenLineJoin lineJoin,
bool aliased)
{
// balance the number of points per polyline and the number of polylines
var numPointsPerPolyline = Math.Max(points.Count / MaxPolylinesPerLine, MinPointsPerPolyline);
var polyline = this.CreateAndAdd<Polyline>();
this.SetStroke(polyline, stroke, thickness, lineJoin, dashArray, 0, aliased);
var pc = new PointCollection(numPointsPerPolyline);
var n = points.Count;
double lineLength = 0;
var dashPatternLength = (dashArray != null) ? dashArray.Sum() : 0;
var last = new Point();
for (int i = 0; i < n; i++)
{
var p = points[i].ToPoint(aliased);
pc.Add(p);
if (dashArray != null)
{
if (i > 0)
{
var delta = p - last;
var dist = Math.Sqrt((delta.X * delta.X) + (delta.Y * delta.Y));
lineLength += dist;
}
last = p;
}
// use multiple polylines with limited number of points to improve WPF performance
if (pc.Count >= numPointsPerPolyline)
{
polyline.Points = pc;
if (i < n - 1)
{
// start a new polyline at last point so there is no gap
polyline = this.CreateAndAdd<Polyline>();
var dashOffset = (dashPatternLength > 0) ? (lineLength / thickness) % dashPatternLength : 0;
this.SetStroke(polyline, stroke, thickness, lineJoin, dashArray, dashOffset, aliased);
pc = new PointCollection(numPointsPerPolyline) { pc.Last() };
}
}
}
if (pc.Count > 1 || n == 1)
{
polyline.Points = pc;
}
}
objo wrote at 2013-09-24 19:18:
I suggest we add a property on the Wpf Plot control: LineDrawingMode { HighQuality, HighSpeed }.
tevo wrote at 2013-09-25 19:46:
In my earlier tests I didn't see artifacts in narrow lines. Perhaps we should always use "HighSpeed" mode when thickness is less than some value, say 3.5.
objo wrote at 2013-09-25 21:51:
I added a "BalancedLineDrawingThicknessLimit" property. The default value is 3.5, use 0 to get old method.
Can you send the code for the "Dashed Line test" example?
tevo wrote at 2013-09-25 22:58:
Here's the dashed line test code:
[Example("Dashed line test")]
public static PlotModel DashedLineTest()
{
var model = new PlotModel("Dashed line test");
for (int y = 1; y <= 24; y++)
{
var line = new LineSeries()
{
StrokeThickness = y,
LineStyle = LineStyle.Dash,
Dashes = new double[] { 1, 2, 3 } // has no effect
};
for (int i = 0; i < 20; i++)
line.Points.Add(new DataPoint(i + 1, y));
model.Series.Add(line);
}
return model;
}
objo wrote at 2013-09-25 23:32:
The NoisyData example seems to be much faster (10x ?).
tevo wrote at 2013-09-26 15:14:
I want extend the scope of Oxyplot plotmodel for event handlers
I want that my plotview replies to more than one event handler, so I
declared the plotmodel outside the event handler bloc, and the result
was that it is out of scope of the event handler.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
// for plotting///////
using OxyPlot;
using OxyPlot.Series;
namespace test
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
var myModel = new PlotModel { Title = "Example 1" };
plot1.Invalidate(true);
myModel.InvalidatePlot(true);
}
void HScrollBar1Scroll(object sender, ScrollEventArgs e)
{
int hScrollBar1_Value = this.hScrollBar1.Value;
Func<double,double> func1 = (x) => Math.Cos(x)/(1+hScrollBar1_Value);
myModel.Series.Add(new FunctionSeries(func1, 0, 5, 0.1, "cos(x)"));
plot1.Model = myModel;
}
}
}
this way the vent handler has no effect and an error is generated. Is
there any other way to make the event handlers access the plotmodel?
Click Event on particular barchart stick
Jivraj wrote at 2014-03-06 10:04:
I wants to perform some task on clicking particular barchart(means its stick drawn) . Is it possible to make certain click event on clicking those barchart stick.If possible please suggest.
thanks in advance.
objo wrote at 2014-03-06 20:54:
MouseDown
event should be available on the BarSeries
. Note that we consider removing the events when the new
PlotController
is implemented:
https://oxyplot.codeplex.com/workitem/10132Jivraj wrote at 2014-03-07 10:22:
i tried your suggestion, as event fire i have just displayed a message it works on clicking the whole chart area but i wants only the event on clicking the bar chart only, but the message is displaying on clicking whole chart area .
Doing on this:
plotModel1.MouseDown += (s, e) =>
{
if (e.ChangedButton != OxyMouseButton.Left)
{
return;
}
e.Handled = false;
MessageDialog msgDialog = new MessageDialog("Welcome", "Alert!");
msgDialog.ShowAsync();
};
iOS PlotView Sealed?
benhysell wrote at 2014-03-13 01:43:
Why did you decide to seal PlotView?
I can easily unseal it, derive from it, and everything works as expected. The only oddity is I need to create a second
PlotModel
in my derived class, fully build the model, and then assign
this.Model = model
, else I get a null reference exception while trying to render the plot.objo wrote at 2014-03-13 07:00:
benhysell wrote at 2014-03-13 14:11:
PlotModel
issue?i.e. I need to have a second local variable to hold the
PlotModel
, build it up, then assign it to
this.PlotModel = localPlotModel;
else rendering fails down in the core OxyPlot project?iOS Adding an Axis to Model then Rendering Throws NullReferenceException
benhysell wrote at 2014-03-14 02:24:
With
PlotView
unsealed I can now do the following:public class GraphViewBad : PlotView
{
public GraphViewBad ()
{
}
public void BuildGraph()
{
Model = new PlotModel ("Bad Plot");
Model.TitleFontSize = 12;
Model.TitleFont = "Helvetica";
Model.TitleFontWeight = FontWeights.Normal;
Model.TitlePadding = 0;
Model.Padding = new OxyPlot.OxyThickness (3, 0, 0, 12);
Model.PlotAreaBackground = OxyColors.White;
Model.PlotAreaBorderThickness = 0;
Model.Background = OxyColors.Red;
//once an axis is added application fails with null ref
Model.Axes.Add (new LinearAxis (AxisPosition.Left) {
TickStyle = TickStyle.Outside,
AxislineStyle = LineStyle.Solid,
MajorStep = 1,
Minimum = 0,
Maximum = 10
});
}
}
Nothing fancy, a red background with a y-axis...enough to show the error.When I run this code I get a
System.NullReferenceException
in
PlotElement.cs
/// <summary>
/// Gets the actual font.
/// </summary>
protected internal string ActualFont
{
get
{
return this.Font ?? this.PlotModel.DefaultFont;
}
}
If I change my code to the following the view displays as expected:public class GraphViewGood : PlotView
{
PlotModel model;
public GraphViewGood ()
{
}
public void BuildGraph()
{
model = new PlotModel ("Good Plot");
model.TitleFontSize = 12;
model.TitleFont = "Helvetica";
model.TitleFontWeight = FontWeights.Normal;
model.TitlePadding = 0;
model.Padding = new OxyPlot.OxyThickness (3, 0, 0, 12);
model.PlotAreaBackground = OxyColors.White;
model.PlotAreaBorderThickness = 0;
model.Background = OxyColors.Red;
model.Axes.Add (new LinearAxis (AxisPosition.Left) {
TickStyle = TickStyle.Outside,
AxislineStyle = LineStyle.Solid,
MajorStep = 1,
Minimum = 0,
Maximum = 10
});
Model = model;
}
}
FileLoadException: Could not load file or assembly 'System.Core, Version=2.0.5.0 with the latest version of oxy plot
madhu_infos wrote at 2014-05-24 12:09:
Recentnly we observed there are few memory leaks in the views. To fix we shifted to latest version of the oxy plot. (i.e 2014.1.305.1)
The latest version of the oxy plot fixed the resolved the memory leak issue. We integreated this version.In devloper machines where silver light is installed oxy plot is working fine.
But in the test machine i get the below error
FileLoadException: Could not load file or assembly 'System.Core, Version=2.0.5.0 with the latest version of oxy plot
The application works on .Net 4. In the test machine visual studio is not present, .Net4 is installed but silverlight is not present.
I have no plans to put silverlight as a dependency. How this problem can be resolved what is the dependency required for this problem?
objo wrote at 2014-06-06 21:34:
http://support.microsoft.com/kb/2600211
If you want to run the library without patching you must build OxyPlot yourself - you can change the build target framework for the OxyPlot .NET 3.5 solution.
Служба підтримки клієнтів працює на UserEcho