You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF .NET6中OxyPlot报错:PlotModel已被其他PlotView使用求助

问题描述

我基于Josh Smith的MVVM Demo,使用WPF和.NET 6.0开发应用,绘图代码放在独立的.NET 6.0项目中。应用通过标签页实现多工作区,可打开多个包含或不包含绘图的标签页,但切换回含绘图的标签页时,会抛出异常:OxyPlot - This PlotModel is already in use by some other PlotView control。切换标签时能观察到视图被重新实例化,且相同代码在.NET Framework 4.7.2环境下可正常运行。

相关代码

BasicTrackView的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>

BasicTrackView后台代码:

public partial class BasicTrackView : UserControl
{
    public BasicTrackView()
    {
        InitializeComponent();
    }
}

TrackContainerView的XAML:

<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&#x0a;Zoom with mouse wheel&#x0a;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>
解决方案

1. 视图卸载时解绑PlotModel

问题根源是TabControl切换时销毁旧视图、重新创建新视图,导致同一个PlotModel被多个PlotView绑定。可以在视图的Unloaded事件中解绑模型,Loaded事件中重新绑定:

修改BasicTrackView的后台代码:

public partial class BasicTrackView : UserControl
{
    public BasicTrackView()
    {
        InitializeComponent();
        Unloaded += BasicTrackView_Unloaded;
        Loaded += BasicTrackView_Loaded;
    }

    private void BasicTrackView_Unloaded(object sender, RoutedEventArgs e)
    {
        // 解绑PlotModel,避免被多个PlotView实例绑定
        Plot.Model = null;
    }

    private void BasicTrackView_Loaded(object sender, RoutedEventArgs e)
    {
        // 视图加载时重新绑定PlotModel
        if (DataContext is BasicTrackViewModel vm)
        {
            Plot.Model = vm.PlotModel;
        }
    }
}

2. 让TabControl保留已创建的视图

TabControl默认会卸载非选中标签的内容,可通过修改TabItem的样式,让内容视图保持存活:

<TabControl>
    <TabControl.ItemContainerStyle>
        <Style TargetType="TabItem">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <ContentPresenter Content="{Binding}" KeepAlive="True"/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </TabControl.ItemContainerStyle>
    <!-- 你的TabItem定义 -->
</TabControl>

KeepAlive="True"会让ContentPresenter保留视图实例,切换标签时不会销毁,也就不会重复创建视图导致模型冲突。

3. 为每个视图生成独立的PlotModel(可选)

如果业务场景允许,可以在ViewModel中为每个视图实例创建独立的PlotModel,确保每个PlotView绑定唯一的模型。这种方法需要调整ViewModel的逻辑,比如在视图创建时初始化新的PlotModel实例。


内容的提问来源于stack exchange,提问作者spainchaud

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 08:34:52