optimize<*>: use custom view locator instead of ContentControl.DataTemplates to avoid memory leak.

This commit is contained in:
leo 2024-03-02 22:45:14 +08:00
parent 27d4dd5f64
commit 60e664ab26
9 changed files with 77 additions and 62 deletions

View file

@ -160,26 +160,23 @@ namespace SourceGit.ViewModels {
} else {
page = ActivePage;
page.Node = node;
page.View = new Views.Repository() { DataContext = repo };
page.Data = repo;
}
} else {
page.Node = node;
page.View = new Views.Repository() { DataContext = repo };
page.Data = repo;
}
ActivePage = page;
}
private void CloseRepositoryInTab(LauncherPage page) {
if (page.Node.IsRepository) {
var repo = Preference.FindRepository(page.Node.Id);
if (repo != null) {
Commands.AutoFetch.RemoveRepository(repo.FullPath);
repo.Close();
}
if (page.Data is Repository repo) {
Commands.AutoFetch.RemoveRepository(repo.FullPath);
repo.Close();
}
page.View = null;
page.Data = null;
}
private LauncherPage _activePage = null;

View file

@ -8,9 +8,9 @@ namespace SourceGit.ViewModels {
set => SetProperty(ref _node, value);
}
public object View {
get => _view;
set => SetProperty(ref _view, value);
public object Data {
get => _data;
set => SetProperty(ref _data, value);
}
public AvaloniaList<Models.Notification> Notifications {
@ -25,12 +25,12 @@ namespace SourceGit.ViewModels {
Bookmark = 0,
IsRepository = false,
};
_view = new Views.Welcome() { DataContext = new Welcome() };
_data = new Welcome();
}
public LauncherPage(RepositoryNode node, Repository repo) {
_node = node;
_view = new Views.Repository() { DataContext = repo };
_data = repo;
}
public override string GetId() {
@ -48,6 +48,6 @@ namespace SourceGit.ViewModels {
}
private RepositoryNode _node = null;
private object _view = null;
private object _data = null;
}
}

View file

@ -1,7 +1,6 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.VisualTree;
using System;
@ -208,11 +207,6 @@ namespace SourceGit.Views {
InitializeComponent();
}
protected override void OnUnloaded(RoutedEventArgs e) {
base.OnUnloaded(e);
GC.Collect();
}
private void OnCommitDataGridLayoutUpdated(object sender, EventArgs e) {
commitGraph.InvalidateVisual();
}

View file

@ -223,8 +223,8 @@
</Border>
</Grid>
<!-- Page container -->
<ContentControl Grid.Row="1" Background="{DynamicResource Brush.ToolBar}" Content="{Binding ActivePage.View}"/>
<!-- Page body -->
<v:LauncherBody Grid.Row="1" Background="{DynamicResource Brush.ToolBar}" Data="{Binding ActivePage.Data}"/>
<!-- Popup container -->
<Grid Grid.Row="1" x:Name="popupContainer" Margin="0,36,0,0" ClipToBounds="True" IsVisible="{Binding ActivePage.Popup, Converter={x:Static ObjectConverters.IsNotNull}}">

View file

@ -23,6 +23,34 @@ namespace SourceGit.Views {
}
}
public class LauncherBody : Border {
public static readonly StyledProperty<object> DataProperty =
AvaloniaProperty.Register<LauncherBody, object>(nameof(Data), false);
public object Data {
get => GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
protected override Type StyleKeyOverride => typeof(Border);
static LauncherBody() {
DataProperty.Changed.AddClassHandler<LauncherBody>((body, ev) => {
var data = body.Data;
if (data == null) {
body.Child = null;
} else if (data is ViewModels.Welcome) {
body.Child = new Welcome { DataContext = data };
} else if (data is ViewModels.Repository) {
body.Child = new Repository { DataContext = data };
} else {
body.Child = null;
}
});
}
}
public partial class Launcher : Window, Models.INotificationReceiver {
public Launcher() {
DataContext = new ViewModels.Launcher();

View file

@ -447,25 +447,7 @@
<Button Grid.Column="3" Classes="flat" FontWeight="Regular" Content="{DynamicResource Text.Repository.Abort}" Height="20" Padding="8,0" Margin="4,0" Command="{Binding AbortMerge}"/>
</Grid>
<ContentControl Grid.Row="1" Content="{Binding SelectedView}">
<ContentControl.DataTemplates>
<DataTemplate DataType="vm:Histories">
<v:Histories/>
</DataTemplate>
<DataTemplate DataType="vm:WorkingCopy">
<v:WorkingCopy/>
</DataTemplate>
<DataTemplate DataType="vm:StashesPage">
<v:StashesPage/>
</DataTemplate>
<DataTemplate DataType="x:Double">
<Border/>
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
<v:RepositorySubView Grid.Row="1" Data="{Binding SelectedView}"/>
</Grid>
</Grid>
</Grid>

View file

@ -1,11 +1,45 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using SourceGit.ViewModels;
using System;
using System.Threading.Tasks;
namespace SourceGit.Views {
public class RepositorySubView : Border {
public static readonly StyledProperty<object> DataProperty =
AvaloniaProperty.Register<RepositorySubView, object>(nameof(Data), false);
public object Data {
get => GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
protected override Type StyleKeyOverride => typeof(Border);
static RepositorySubView() {
DataProperty.Changed.AddClassHandler<RepositorySubView>((view, ev) => {
var data = view.Data;
if (data == null) {
view.Child = null;
} else if (data is ViewModels.Histories) {
view.Child = new Histories { DataContext = data };
} else if (data is ViewModels.WorkingCopy) {
view.Child = new WorkingCopy { DataContext = data };
} else if (data is ViewModels.StashesPage) {
view.Child = new StashesPage { DataContext = data };
} else {
view.Child = null;
}
GC.Collect();
});
}
}
public partial class Repository : UserControl {
public Repository() {
InitializeComponent();

View file

@ -1,16 +1,9 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using System;
namespace SourceGit.Views {
public partial class StashesPage : UserControl {
public StashesPage() {
InitializeComponent();
}
protected override void OnUnloaded(RoutedEventArgs e) {
base.OnUnloaded(e);
GC.Collect();
}
}
}

View file

@ -2,7 +2,6 @@ using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
using System;
using System.Collections.Generic;
namespace SourceGit.Views {
@ -11,18 +10,6 @@ namespace SourceGit.Views {
InitializeComponent();
}
protected override void OnUnloaded(RoutedEventArgs e) {
var vm = DataContext as ViewModels.WorkingCopy;
vm.SelectedStagedChange = null;
vm.SelectedStagedTreeNode = null;
vm.SelectedUnstagedChange = null;
vm.SelectedUnstagedTreeNode = null;
vm.SetDetail(null, false);
base.OnUnloaded(e);
GC.Collect();
}
private void ViewAssumeUnchanged(object sender, RoutedEventArgs e) {
var repoPage = this.FindAncestorOfType<Repository>();
if (repoPage != null) {