При повторном посещении вкладки создается новый набор представлений, что может привести к потенциальному переполнению стC#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 При повторном посещении вкладки создается новый набор представлений, что может привести к потенциальному переполнению ст

Сообщение Anonymous »


I have an application based on Josh Smith's MVVM Demo, with the business logic replaced. The user clicks links that dynamically generate tabs. Each tab has a view and viewmodel. The tab views contain child views one of which contains multiple OxyPlot PlotViews. The user is presented with multiple plots, and zooming on one plot will cause all the plots to zoom accordingly. This has worked well on my applications with hard-coded tabs.

The problem occurs when the user goes back to a previously selected tab. All the views in the hierarchy are recreated, but the old views are not deleted.

I have added static counters to my views, so I can keep track of what is happening with otherwise identical views. If the user zooms a plot on a revisited tab, I can see all the views and the copies getting zoomed. The more times a user switches tabs, the more copies that are created.

I actually had a stack overflow because the Oxyplot zoom was causing recursion and an infinite loop. I replaced the zoom with individual min/max settings, which solved that problem, but I still need to get rid of the extra view copies.

Here is the view that contains the PlotView:

Code behind

public partial class GenericTrackView : UserControl, ITrackView { #region Fields private TrackContainerView _parentView; private static int Count; #endregion #region Constructor public GenericTrackView() { InitializeComponent(); Count++; // for debugging, keep track of redundant views ID = Count; } #endregion #region Dependency Properties public static readonly DependencyProperty TrackProperty = DependencyProperty.Register("Track", typeof(Track), typeof(GenericTrackView), new FrameworkPropertyMetadata(null, OnTrackChanged)); public Track Track { get => (Track)GetValue(TrackProperty); set => SetValue(TrackProperty, value); } public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register("IsActive", typeof(bool), typeof(GenericTrackView), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public bool IsActive { get => (bool)GetValue(IsActiveProperty); set => SetValue(IsActiveProperty, value); } #endregion #region Properties public int ID { get; private set; } public Axis XAxis { get; private set; } public bool IsInternalChange { get; private set; } #endregion #region Private Methods private static void OnTrackChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var trackPanel = sender as GenericTrackView; if (trackPanel == null) return; var track = (Track)e.NewValue; } private void OnAxisChanged(object sender, AxisChangedEventArgs e) { // if true then stop any recursion if (IsInternalChange) return; var xMin = XAxis.ActualMinimum; var xMax = XAxis.ActualMaximum; foreach (var track in _parentView.TrackList) { if (track == this) continue; var genericTrack = track as GenericTrackView; genericTrack.IsInternalChange = true; // do not use zoom to set axis, it can lead to recursion genericTrack.XAxis.AbsoluteMinimum = xMin; genericTrack.XAxis.AbsoluteMaximum = xMax; genericTrack.Plot.InvalidatePlot(false); genericTrack.IsInternalChange = false; } } public override string ToString() { return $"{Name}: {ID}"; } #endregion #region Event Handlers private void Button_Click(object sender, RoutedEventArgs e) { var button = sender as Button; if (button == null) return; IsActive = !IsActive; Plot.Visibility = IsActive ? Visibility.Visible : Visibility.Collapsed; CheckBoxItems.Visibility = IsActive ? Visibility.Visible : Visibility.Collapsed; GridSplitter.Visibility = IsActive ? Visibility.Visible : Visibility.Collapsed; Filters.Visibility = IsActive ? ((GenericTrackViewModel)DataContext).FiltersVisible : Visibility.Collapsed; if (button.Content is TextBlock textBlock) { textBlock.Text = IsActive ? "-" : "+"; } else { button.Content = IsActive ? "-" : "+"; } // We need to toggle the Grid.RowDefinition.Height from "*" to "Auto" in order // for the row to collapse. // We know this control is in a ContentPresenter, which is a child of the Grid. // The Grid is the ItemsPanel of an ItemsControl. So first we find the Grid, // then we find and set the row that this TrackPanelView is in. var grid = WpfHelpers.FindParentControl(this); if (grid == null) return; var index = 0; foreach (var contentPresenter in grid.Children) { var dependencyObject = contentPresenter as DependencyObject; if (dependencyObject == null) continue; var panel = WpfHelpers.FindFirstVisualChild(dependencyObject); if (Equals(panel, this)) { grid.RowDefinitions[index].Height = IsActive ? new GridLength(1, GridUnitType.Star) : new GridLength(1, GridUnitType.Auto); break; } index++; } } private void GridSplitter_DragCompleted(object sender, DragCompletedEventArgs e) { var gridSplitter = sender as GridSplitter; if (gridSplitter == null) return; if (!(Math.Abs(e.HorizontalChange) > 0.0)) return; foreach (var trackView in _parentView.TrackList) { if (!Equals(trackView, this)) { ((GenericTrackView)trackView).OuterGrid.ColumnDefinitions[0].Width = OuterGrid.ColumnDefinitions[0].Width; } } } /// /// When the plot is loaded, find the x axis and link its AxisChanged event to the /// OnAxisChanged method. /// /// /// private void Plot_OnLoaded(object sender, RoutedEventArgs e) { foreach (var axis in Plot.ActualModel.Axes) { if (axis.Position != AxisPosition.Bottom) continue; XAxis = axis; break; } if (XAxis != null) XAxis.AxisChanged += OnAxisChanged; } private void GenericTrackView_OnLoaded(object sender, RoutedEventArgs e) { Name = ((GenericTrackViewModel)DataContext).TrackName; // find parent and add this track to its list _parentView = WpfHelpers.FindParentControl(this); _parentView.TrackList.Add(this); } #endregion } In my viewmodels, I am not using PlotModel, I am using ViewResolvingPlotModel as described in a previous post:

OxyPlot - This PlotModel is already in use by some other PlotView control

I don't know if the code I have shown is helpful, as I don't understand what the root cause of the problem is. All the connections between views and viewmodels are done through DataTemplates. I see no code in my MainWindowViewModel that is called when a user switches tabs.

The viewmodel is created as follows.

private void ShowPipelineTest() { var workspace = new TestPipelineViewModel(); Workspaces.Add(workspace); SetActiveWorkspace(workspace); } void SetActiveWorkspace(WorkspaceViewModel workspace) { Debug.Assert(Workspaces.Contains(workspace)); var collectionView = CollectionViewSource.GetDefaultView(Workspaces); collectionView?.MoveCurrentTo(workspace); } But this code is not called when the tab is revisited.

Edit: I found some code for testing. The original MVVM Demo can be found here:

https://github.com/djangojazz/JoshSmith_MVVMDemo

I had problems with the solution, but the project opened and worked. First open an All Customers tab, then a New Customer tab. When you click back on the first tab you will see the view get instantiated again. That is the problem. My application uses the same MainWindowView and the same MainWondowViewModel with my tab viewmodels replacing those in the demo.


Источник: https://stackoverflow.com/questions/780 ... -potential
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»