Skip to content

Commit eda1e06

Browse files
Switched to lazy loading the installed archive files due to long loading times for large install collections.
1 parent 872f56f commit eda1e06

5 files changed

Lines changed: 158 additions & 64 deletions

File tree

DazContentInstaller/Models/InstalledArchiveTree.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class InstalledArchiveTree : ObservableCollection<TreeNode>
1010
{
1111
public TreeNode LoadArchive(Archive archive)
1212
{
13-
var root = new TreeNode(archive.ArchiveName, archive.Id, null);
13+
var root = new TreeNode(archive.ArchiveName, archive.Id, null, isLazyLoad: false);
1414
Add(root);
1515

1616
foreach (var assetFile in archive.AssetFiles)
@@ -22,8 +22,35 @@ public TreeNode LoadArchive(Archive archive)
2222
}
2323

2424
SortTree(root);
25+
root.MarkChildrenLoaded();
2526
return root;
2627
}
28+
29+
public TreeNode LoadArchiveLazy(Archive archive)
30+
{
31+
var root = new TreeNode(archive.ArchiveName, archive.Id, null, isLazyLoad: true);
32+
Add(root);
33+
return root;
34+
}
35+
36+
public static void LoadArchiveFiles(TreeNode archiveNode, Archive archive)
37+
{
38+
if (!archiveNode.IsLazyLoad || archiveNode.HasLoadedChildren)
39+
return;
40+
41+
archiveNode.ClearPlaceholders();
42+
43+
foreach (var assetFile in archive.AssetFiles)
44+
{
45+
var parts = assetFile.FileName.Split(Path.DirectorySeparatorChar);
46+
var current = archiveNode;
47+
for (var index = 0; index < parts.Length; index++)
48+
current = current.GetOrAddChild(parts[index], index == parts.Length - 1 ? assetFile.Id : null);
49+
}
50+
51+
SortTree(archiveNode);
52+
archiveNode.MarkChildrenLoaded();
53+
}
2754

2855
private static void SortTree(TreeNode node)
2956
{

DazContentInstaller/Models/TreeNode.cs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,21 @@ public class TreeNode
1212
public TreeNode? Parent { get; }
1313
public string Title { get; }
1414
public Guid? DbId { get; }
15+
16+
public bool IsLazyLoad { get; set; }
17+
public bool IsExpanded { get; set; }
18+
public bool HasLoadedChildren { get; set; }
1519

16-
public TreeNode(string title, Guid? id, TreeNode? parent)
20+
public TreeNode(string title, Guid? id, TreeNode? parent, bool isLazyLoad = false)
1721
{
1822
Title = title;
1923
Parent = parent;
2024
DbId = id;
25+
IsLazyLoad = isLazyLoad;
26+
HasLoadedChildren = !isLazyLoad;
27+
28+
if (isLazyLoad)
29+
Children.Add(new TreeNode("Loading...", null, this));
2130
}
2231

2332
public TreeNode(string title, Guid? dbId, IEnumerable<TreeNode> children, TreeNode? parent) : this(title, dbId, parent)
@@ -28,12 +37,24 @@ public TreeNode(string title, Guid? dbId, IEnumerable<TreeNode> children, TreeNo
2837
public TreeNode GetOrAddChild(string name, Guid? dbId)
2938
{
3039
var child = Children.FirstOrDefault(c => c.Title.Equals(name, StringComparison.OrdinalIgnoreCase));
31-
if (child is null)
32-
{
33-
child = new TreeNode(name, dbId, this);
34-
Children.Add(child);
35-
}
40+
if (child is not null) return child;
41+
42+
child = new TreeNode(name, dbId, this);
43+
Children.Add(child);
3644

3745
return child;
3846
}
47+
48+
public void ClearPlaceholders()
49+
{
50+
if (IsLazyLoad && !HasLoadedChildren)
51+
{
52+
Children.Clear();
53+
}
54+
}
55+
56+
public void MarkChildrenLoaded()
57+
{
58+
HasLoadedChildren = true;
59+
}
3960
}

DazContentInstaller/ViewModels/MainWindowViewModel.cs

Lines changed: 77 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ await Task.Run(async () =>
170170
});
171171

172172
CurrentSelectedAssetLibrary = AssetLibraries.OrderByDescending(d => d.IsDefault).FirstOrDefault();
173+
AllowArchiveLoad = true;
173174
}
174175

175176
public async Task LoadInstalledArchivesAsync()
@@ -187,14 +188,79 @@ await Task.Run(async () =>
187188

188189
await foreach (var archive in archivesQuery.OrderBy(d => d.ArchiveName.ToLower()).AsAsyncEnumerable())
189190
{
190-
var files = await _dbContext.AssetFiles.Where(d => d.ArchiveId == archive.Id).ToListAsync();
191-
archive.AssetFiles = files;
192-
193-
var node = InstalledArchivesTree.LoadArchive(archive);
191+
var node = InstalledArchivesTree.LoadArchiveLazy(archive);
194192
DisplayedInstalledArchives.Add(node);
195-
InstalledAssetsCount = InstalledArchivesTree.Count;
193+
194+
InstalledAssetsCount++;
195+
}
196+
});
197+
}
198+
199+
public async Task LoadArchiveTreeFilesAsync(TreeNode archiveNode)
200+
{
201+
if (!archiveNode.IsLazyLoad || archiveNode.HasLoadedChildren || archiveNode.DbId == null)
202+
return;
203+
204+
try
205+
{
206+
StatusText = $"Loading files for {archiveNode.Title}...";
207+
StatusBarColor = Brushes.DodgerBlue;
208+
209+
await Task.Run(async () =>
210+
{
211+
var archive = await _dbContext.Archives
212+
.Include(a => a.AssetFiles)
213+
.FirstOrDefaultAsync(a => a.Id == archiveNode.DbId);
214+
215+
if (archive != null)
216+
{
217+
Dispatcher.UIThread.Post(() => { InstalledArchiveTree.LoadArchiveFiles(archiveNode, archive); });
218+
}
219+
});
220+
221+
StatusText = "Ready";
222+
StatusBarColor = Brushes.Green;
223+
}
224+
catch (Exception ex)
225+
{
226+
StatusText = $"Error loading files: {ex.Message}";
227+
StatusBarColor = Brushes.Red;
228+
}
229+
}
230+
231+
public async Task LoadArchiveFilesFromDiskAsync(List<string> filePaths)
232+
{
233+
StatusBarMax = 100 * filePaths.Count;
234+
StatusProgress = 0;
235+
StatusBarColor = Brushes.DodgerBlue;
236+
AllowArchiveLoad = false;
237+
238+
var index = 0;
239+
240+
await Task.Run(async () =>
241+
{
242+
foreach (var path in filePaths)
243+
{
244+
index++;
245+
var existingStatusProgress = index * 100;
246+
IProgress<string> messageProgress = new Progress<string>(s => StatusText = s);
247+
var percentageProgress = new Progress<int>(p => StatusProgress = existingStatusProgress + p);
248+
249+
using var loader = new DazArchiveLoader(path);
250+
var result = await loader.LoadArchiveAsync(messageProgress, percentageProgress);
251+
252+
Dispatcher.UIThread.Post(() =>
253+
{
254+
LoadedArchives.AddRange(result.Where(d => d.ContainedFiles.Count > 0));
255+
UpdateInstallButton();
256+
});
257+
messageProgress.Report($"Finished loading {Path.GetFileName(path)}");
196258
}
197259
});
260+
261+
StatusBarMax = 100;
262+
StatusBarColor = Brushes.Green;
263+
AllowArchiveLoad = true;
198264
}
199265

200266
private void RemoveLoadedArchive(LoadedArchive loadedArchiveOld)
@@ -256,11 +322,8 @@ await Task.Run(async () =>
256322
}
257323
});
258324

259-
await Task.Run(async () =>
260-
{
261-
await LoadInstalledArchivesAsync();
262-
});
263-
325+
await Task.Run(async () => { await LoadInstalledArchivesAsync(); });
326+
264327
messageProgress.Report($"Installed {archivesToInstall.Count} archives");
265328
percentageProgress.Report(100);
266329
StatusBarColor = Brushes.Green;
@@ -331,46 +394,8 @@ await Task.Run(async () =>
331394
StatusBarColor = Brushes.Green;
332395
});
333396

334-
await Task.Run(async () =>
335-
{
336-
await LoadInstalledArchivesAsync();
337-
});
338-
339-
AllowArchiveLoad = true;
340-
}
341-
342-
public async Task LoadArchiveFilesAsync(List<string> filePaths)
343-
{
344-
StatusBarMax = 100 * filePaths.Count;
345-
StatusProgress = 0;
346-
StatusBarColor = Brushes.DodgerBlue;
347-
AllowArchiveLoad = false;
348-
349-
var index = 0;
397+
await Task.Run(async () => { await LoadInstalledArchivesAsync(); });
350398

351-
await Task.Run(async () =>
352-
{
353-
foreach (var path in filePaths)
354-
{
355-
index++;
356-
var existingStatusProgress = index * 100;
357-
IProgress<string> messageProgress = new Progress<string>(s => StatusText = s);
358-
var percentageProgress = new Progress<int>(p => StatusProgress = existingStatusProgress + p);
359-
360-
using var loader = new DazArchiveLoader(path);
361-
var result = await loader.LoadArchiveAsync(messageProgress, percentageProgress);
362-
363-
Dispatcher.UIThread.Post(() =>
364-
{
365-
LoadedArchives.AddRange(result.Where(d => d.ContainedFiles.Count > 0));
366-
UpdateInstallButton();
367-
});
368-
messageProgress.Report($"Finished loading {Path.GetFileName(path)}");
369-
}
370-
});
371-
372-
StatusBarMax = 100;
373-
StatusBarColor = Brushes.Green;
374399
AllowArchiveLoad = true;
375400
}
376401

@@ -380,12 +405,13 @@ private void FilterInstalledAssetsTree(string? searchTerm)
380405
if (string.IsNullOrWhiteSpace(searchTerm))
381406
{
382407
DisplayedInstalledArchives.AddRange(InstalledArchivesTree);
383-
384408
return;
385409
}
386410

387411
var filtered = InstalledArchivesTree
388-
.Select(node => FilterTree(node, searchTerm))
412+
.Where(node => node.Title.Contains(searchTerm, StringComparison.OrdinalIgnoreCase))
413+
.Select(node =>
414+
node is { IsLazyLoad: true, HasLoadedChildren: false } ? node : FilterTree(node, searchTerm))
389415
.Where(node => node is not null)
390416
.Select(node => node!);
391417

@@ -394,14 +420,12 @@ private void FilterInstalledAssetsTree(string? searchTerm)
394420

395421
private static TreeNode? FilterTree(TreeNode node, string searchTerm)
396422
{
397-
// Filter children recursively
398423
var filteredChildren = node.Children
399424
.Select(child => FilterTree(child, searchTerm))
400425
.Where(child => child is not null)
401426
.Select(child => child!)
402427
.ToList();
403428

404-
// Check if this node matches or has any matching children
405429
var isMatch = node.Title.Contains(searchTerm, StringComparison.OrdinalIgnoreCase);
406430

407431
if (isMatch || filteredChildren.Count > 0)

DazContentInstaller/Views/MainWindow.axaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@
208208
</StackPanel>
209209

210210
<!-- Installed Assets List -->
211-
<TreeView Grid.Row="1"
211+
<TreeView Grid.Row="1" Name="InstalledArchivesTreeView"
212212
SelectionMode="Multiple" ItemsSource="{Binding DisplayedInstalledArchives}"
213213
SelectedItems="{Binding SelectedInstallNodes}"
214214
SelectionChanged="InstalledArchivesTreeView_OnSelectionChanged">

DazContentInstaller/Views/MainWindow.axaml.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ private async Task OnDrop(object? sender, DragEventArgs e)
5151

5252
if (zipFiles == null) return;
5353

54-
await ViewModel.LoadArchiveFilesAsync(zipFiles.Select(f => f.Path.LocalPath).ToList());
54+
await ViewModel.LoadArchiveFilesFromDiskAsync([.. zipFiles.Select(f => f.Path.LocalPath)]);
5555
}
5656
catch (Exception ex)
5757
{
@@ -83,14 +83,14 @@ private async void OnBrowseClick(object? sender, RoutedEventArgs e)
8383
FileTypeFilter =
8484
[
8585
new FilePickerFileType("ZIP Archives")
86-
{ Patterns = _allowedExtensions.Select(ae => $"*{ae}").ToArray() },
86+
{ Patterns = [.. _allowedExtensions.Select(ae => $"*{ae}")] },
8787
new FilePickerFileType("All Files") { Patterns = ["*"] }
8888
]
8989
});
9090

9191
if (files.Count < 1) return;
9292

93-
await ViewModel.LoadArchiveFilesAsync(files.Select(f => f.Path.LocalPath).ToList());
93+
await ViewModel.LoadArchiveFilesFromDiskAsync([.. files.Select(f => f.Path.LocalPath)]);
9494
}
9595
catch (Exception ex)
9696
{
@@ -109,6 +109,8 @@ private async void Control_OnLoaded(object? sender, RoutedEventArgs e)
109109
if (Application.Current?.ApplicationLifetime is null)
110110
return;
111111

112+
SetupTreeViewExpansionHandler();
113+
112114
await ViewModel.LoadAssetLibrariesAsync();
113115
}
114116
catch (Exception ex)
@@ -191,4 +193,24 @@ private async void InstalledArchivesTreeView_OnSelectionChanged(object? sender,
191193
await ShowErrorMessageBox(ex);
192194
}
193195
}
196+
197+
private void SetupTreeViewExpansionHandler() =>
198+
AddHandler(TreeViewItem.ExpandedEvent, OnTreeViewItemExpanded, RoutingStrategies.Bubble);
199+
200+
private async void OnTreeViewItemExpanded(object? sender, RoutedEventArgs e)
201+
{
202+
try
203+
{
204+
if (DataContext is not MainWindowViewModel vm || e.Source is not TreeViewItem item) return;
205+
206+
if (item.DataContext is TreeNode { IsLazyLoad: true, HasLoadedChildren: false } treeNode)
207+
{
208+
await vm.LoadArchiveTreeFilesAsync(treeNode);
209+
}
210+
}
211+
catch (Exception ex)
212+
{
213+
await ShowErrorMessageBox(ex);
214+
}
215+
}
194216
}

0 commit comments

Comments
 (0)