I am using WPF, and .NET 6.0. My application is basically Josh Smith's MVVM Demo with the business logic replaced. The plot code is in a .NET 6.0 project separate from the application. The application has independent workspaces on separate tabs. I can open multiple tabs, some with plots, some without, but if I try to go back to a tab with plots, I see the error message above. When I try to go back to the tab with plots, I can see the views get instantiated again, just before the exception is thrown. I use this same code in a .NET Framework 4.7.2 application with no problems.
My XAML:
<UserControl x:Class="DataPlot.Views.BasicTrackView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DataPlot.Views"
xmlns:oxy="http://oxyplot.org/wpf"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<oxy:PlotView x:Name="Plot" Model="{Binding PlotModel}" Grid.Column="1" MinHeight="{Binding MinimumPlotHeight}">
<oxy:PlotView.DefaultTrackerTemplate>
<ControlTemplate>
<oxy:TrackerControl Position="{Binding Position}" LineExtents="{Binding PlotModel.PlotArea}">
<oxy:TrackerControl.Background>
<LinearGradientBrush EndPoint="0,1">
<GradientStop Color="#f0e0e0ff" />
<GradientStop Offset="1" Color="#f0ffffff" />
</LinearGradientBrush>
</oxy:TrackerControl.Background>
<oxy:TrackerControl.Content>
<TextBlock Text="{Binding}" Margin="7" />
</oxy:TrackerControl.Content>
</oxy:TrackerControl>
</ControlTemplate>
</oxy:PlotView.DefaultTrackerTemplate>
</oxy:PlotView>
</Grid>
</UserControl>
Code behind:
public partial class BasicTrackView : UserControl
{
public BasicTrackView()
{
InitializeComponent();
}
}
I have several plots like this in an ItemsControl:
<UserControl x:Class="DataPlot.Views.TrackContainerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DataPlot.Views"
xmlns:vm="clr-namespace:DataPlot.ViewModels"
xmlns:helpers="clr-namespace:DataPlot.Helpers"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate DataType="{x:Type vm:BasicTrackViewModel}">
<local:BasicTrackView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:GenericTrackViewModel}">
<local:GenericTrackView IsActive="{Binding IsActive}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:CollapsableTrackViewModel}">
<local:CollapsableTrackView IsActive="{Binding IsActive}"/>
</DataTemplate>
</UserControl.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Controls?" Width="60" Height="25" HorizontalAlignment="Right" Margin="0,2,20,2" Grid.Column="4"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Background="LightGray">
<Label.ToolTip>
<ToolTip>
<DockPanel Width="200" Height="75">
<TextBox Text="Zoom: Ctrl + Right Mouse, or
Zoom with mouse wheel
Pan: Right Mouse" AcceptsReturn="True"/>
</DockPanel>
</ToolTip>
</Label.ToolTip>
</Label>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ItemsControl Grid.Row="1" ItemsSource="{Binding TrackViewModels}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid helpers:GridHelpers.RowCount="{Binding TrackViewModels.Count}" helpers:GridHelpers.StarRows="All"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</ScrollViewer>
</Grid>
</UserControl>
Is there something I can do to keep the application from trying to recreate views that already exist?
I would like to credit bigcrazal (in his comment) for providing an easy solution with his link to code provided by edvinasz. Since links can go bad, I will put the code below. In my question I mentioned that I had no problem with this code in old .NET Framework 4.7.2 projects. I now believe this is because I was using static tabs in those past applications.
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 });
}
}
}