diff --git a/Source/OxyPlot.Avalonia/PlotBase.cs b/Source/OxyPlot.Avalonia/PlotBase.cs index 08d5b28..1241b6e 100644 --- a/Source/OxyPlot.Avalonia/PlotBase.cs +++ b/Source/OxyPlot.Avalonia/PlotBase.cs @@ -57,6 +57,11 @@ public abstract partial class PlotBase : TemplatedControl, IPlotView /// private Panel panel; + /// + /// Invalidation flag (0: no update, 1: update, 2: update date). + /// + private int isUpdateRequired; + /// /// Invalidation flag (0: no update, 1: update visual elements). /// @@ -224,21 +229,22 @@ public void ResetAllAxes() /// The update Data. public void InvalidatePlot(bool updateData = true) { - if (Width <= 0 || Height <= 0) - { - return; - } + // perform update on UI thread + var updateState = updateData ? 2 : 1; + int currentState = isUpdateRequired; - // TODO: legend on/off crash (issue with Legend hit-test implementation, really) - UpdateModel(updateData); - - if (Interlocked.CompareExchange(ref isPlotInvalidated, 1, 0) == 0) + while (currentState < updateState) { - // Invalidate the arrange state for the element. - // After the invalidation, the element will have its layout updated, - // which will occur asynchronously unless subsequently forced by UpdateLayout. - BeginInvoke(InvalidateArrange); - BeginInvoke(InvalidateVisual); + if (Interlocked.CompareExchange(ref isUpdateRequired, updateState, currentState) == currentState) + { + isUpdateRequired = updateState; + BeginInvoke(() => UpdateModel(updateData)); + break; + } + else + { + currentState = isUpdateRequired; + } } } @@ -386,10 +392,30 @@ protected override Size ArrangeOverride(Size finalSize) /// The update Data. protected void UpdateModel(bool updateData = true) { - if (ActualModel != null) + if (Width <= 0 || Height <= 0 || ActualModel == null) + { + return; + } + + lock (this.ActualModel.SyncRoot) { - ((IPlotModel)ActualModel).Update(updateData); + var updateState = (Interlocked.Exchange(ref isUpdateRequired, 0)); + + if (updateState > 0) + { + ((IPlotModel)ActualModel).Update(updateState == 2); + } + } + + if (Interlocked.CompareExchange(ref isPlotInvalidated, 1, 0) == 0) + { + // Invalidate the arrange state for the element. + // After the invalidation, the element will have its layout updated, + // which will occur asynchronously unless subsequently forced by UpdateLayout. + BeginInvoke(InvalidateArrange); + BeginInvoke(InvalidateVisual); } + } /// @@ -482,26 +508,36 @@ private void UpdateVisuals() if (ActualModel != null) { - if (DisconnectCanvasWhileUpdating) + lock (this.ActualModel.SyncRoot) { - // TODO: profile... not sure if this makes any difference - var idx = panel.Children.IndexOf(canvas); - if (idx != -1) + var updateState = (Interlocked.Exchange(ref isUpdateRequired, 0)); + + if (updateState > 0) { - panel.Children.RemoveAt(idx); + ((IPlotModel)ActualModel).Update(updateState == 2); } - ((IPlotModel)ActualModel).Render(renderContext, new OxyRect(0, 0, canvas.Bounds.Width, canvas.Bounds.Height)); - - // reinsert the canvas again - if (idx != -1) + if (DisconnectCanvasWhileUpdating) { - panel.Children.Insert(idx, canvas); + // TODO: profile... not sure if this makes any difference + var idx = panel.Children.IndexOf(canvas); + if (idx != -1) + { + panel.Children.RemoveAt(idx); + } + + ((IPlotModel)ActualModel).Render(renderContext, new OxyRect(0, 0, canvas.Bounds.Width, canvas.Bounds.Height)); + + // reinsert the canvas again + if (idx != -1) + { + panel.Children.Insert(idx, canvas); + } + } + else + { + ((IPlotModel)ActualModel).Render(renderContext, new OxyRect(0, 0, canvas.Bounds.Width, canvas.Bounds.Height)); } - } - else - { - ((IPlotModel)ActualModel).Render(renderContext, new OxyRect(0, 0, canvas.Bounds.Width, canvas.Bounds.Height)); } } } @@ -514,11 +550,11 @@ private static void BeginInvoke(Action action) { if (Dispatcher.UIThread.CheckAccess()) { - Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Loaded); + action?.Invoke(); } else { - action?.Invoke(); + Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Loaded); } } }