oxyplot: This PlotModel is already in use by some other PlotView control.

This issue occures at line 165 in PlotViewEx.cs. As my thought, reattaching same plotmodel to the same plotview should not throw this InvalidOperationException showing on title. Then a possible solution is to avoid throwing InvalidOperationException and reattaching plotmodel when this.currentModel.PlotView equals current plotview object(this).

Code modification is here:

private void OnModelChanged()
{
    lock (this.modelLock)
    {
        if (this.currentModel != null)
        {
            ((IPlotModel)this.currentModel).AttachPlotView(null);
        }

        this.currentModel = this.Model;

        if (this.currentModel != null && !Equals(this.currentModel.PlotView, this))
        {
            if (this.currentModel.PlotView != null)
            {
                throw new InvalidOperationException(
                    "This PlotModel is already in use by some other PlotView control.");
            }

            ((IPlotModel)this.ActualModel).AttachPlotView(this);
        }
    }

    this.InvalidatePlot();
}

About this issue

  • Original URL
  • State: open
  • Created 9 years ago
  • Comments: 45 (13 by maintainers)

Commits related to this issue

Most upvoted comments

I know this is old and done, but until proper refactoring mentioned above is complete. Using PlotModel in a ViewModels and dynamically generating Views (ex. TabControls with DataTemplates) is not possible with out having the exception thrown once in a while.

So I though I would share a “simple” hack work around.

Instantiate this type instead of PlotModel in your VM and the most recent DataTemplate View will be auto attached.

    using System;
    using System.Linq;
    using System.Reflection;
    using OxyPlot;

    /// <summary>
    /// Use this sub implementation of the <see cref="PlotModel"/> if the view will be declared using data template.
    /// Because views will be automatically generated, and new view will be different this causes current version to throw an error.
    /// </summary>
    public class ViewResolvingPlotModel : PlotModel, IPlotModel
    {
        private static readonly Type BaseType = typeof(ViewResolvingPlotModel).BaseType;
        private static readonly MethodInfo BaseAttachMethod = BaseType
            .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
            .Where(methodInfo => methodInfo.IsFinal && methodInfo.IsPrivate)
            .FirstOrDefault(methodInfo => methodInfo.Name.EndsWith(nameof(IPlotModel.AttachPlotView)));

        void IPlotModel.AttachPlotView(IPlotView plotView)
        {
            //because of issue https://github.com/oxyplot/oxyplot/issues/497 
            //only one view can ever be attached to one plotmodel
            //we have to force detach previous view and then attach new one
            if (plotView != null && PlotView != null && !Equals(plotView, PlotView))
            {
                BaseAttachMethod.Invoke(this, new object[] { null });
                BaseAttachMethod.Invoke(this, new object[] { plotView });
            }
            else
            {
                BaseAttachMethod.Invoke(this, new object[] { plotView });
            }
        }
    }

I really like the solution offered by @edvinasz but I had a case where I couldn’t use a derived class from PlotModel so I created this extension class:

public static class PlotModelExtensions
	{
	public static void AttachToView(this PlotModel plotModel, IPlotView plotView)
		{
		// Because of issue https://github.com/oxyplot/oxyplot/issues/497 
		// only one view can ever be attached to one plotmodel.
		// We have to force detach from any previous view and then attach to the new one.
		MethodInfo BaseAttachMethod = plotModel.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
							.Where(methodInfo => methodInfo.IsFinal && methodInfo.IsPrivate)
							.FirstOrDefault(methodInfo => methodInfo.Name.EndsWith(nameof(IPlotModel.AttachPlotView)));

		if (plotView != null && plotModel.PlotView != null && !Equals(plotView, plotModel.PlotView))
			{
			BaseAttachMethod.Invoke(plotModel, new object[] { null });
			}
		BaseAttachMethod.Invoke(plotModel, new object[] { plotView });
		}
	}

Use it simply like this:

    plotModel.AttachToView(plotView);

The InvalidateFlag is changed in VM whenever it needs to redraw the entire thing, such as when new points are added to the series. Similar to calling Invalidate methods on the plot model itself.