Skip to content

Commit f7b0a8f

Browse files
author
LoneWandererProductions
committed
bring back some fixes.
1 parent a31b65f commit f7b0a8f

5 files changed

Lines changed: 80 additions & 68 deletions

File tree

CommonControls/Thumbnails.xaml.cs

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,23 @@ public sealed partial class Thumbnails : IDisposable
5757
public static readonly DependencyProperty DependencyThumbHeight = DependencyProperty.Register(
5858
nameof(DependencyThumbHeight),
5959
typeof(int),
60-
typeof(Thumbnails), new PropertyMetadata(100));
60+
typeof(Thumbnails));
6161

6262
/// <summary>
6363
/// The Thumb Length (in lines)
6464
/// </summary>
6565
public static readonly DependencyProperty DependencyThumbWidth = DependencyProperty.Register(
6666
nameof(DependencyThumbWidth),
6767
typeof(int),
68-
typeof(Thumbnails), new PropertyMetadata(100));
68+
typeof(Thumbnails));
6969

7070
/// <summary>
7171
/// The Thumb Cell Size
7272
/// </summary>
7373
public static readonly DependencyProperty DependencyThumbCellSize = DependencyProperty.Register(
7474
nameof(DependencyThumbCellSize),
7575
typeof(int),
76-
typeof(Thumbnails), new PropertyMetadata(100));
76+
typeof(Thumbnails));
7777

7878
/// <summary>
7979
/// The dependency thumb grid
@@ -144,6 +144,11 @@ public sealed partial class Thumbnails : IDisposable
144144
/// </summary>
145145
private int _originalHeight;
146146

147+
/// <summary>
148+
/// The loaded
149+
/// </summary>
150+
private bool _loaded;
151+
147152
/// <summary>
148153
/// The original width
149154
/// </summary>
@@ -448,92 +453,102 @@ private async Task OnItemsSourceChanged()
448453
/// </summary>
449454
/// <param name="sender">The source of the event.</param>
450455
/// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
451-
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
456+
private void UserControl_Loaded(object sender, RoutedEventArgs e)
452457
{
453458
try
454459
{
460+
_loaded = true;
461+
462+
// Capture original width/height immediately
455463
_originalWidth = ThumbWidth;
456464
_originalHeight = ThumbHeight;
457465

458-
await LoadImages(); // safe to await here
466+
// Start loading images asynchronously
467+
if (ItemsSource != null)
468+
{
469+
_ = LoadItemsAsync();
470+
}
459471
}
460472
catch (Exception ex)
461473
{
462-
// Log or handle exceptions gracefully
463-
Trace.WriteLine($"Error loading images: {ex}");
474+
Trace.WriteLine($"Error in Loaded: {ex}");
464475
}
465476
}
466477

478+
/// <summary>
479+
/// Fire-and-forget wrapper to call your async method
480+
/// Loads the items asynchronous.
481+
/// </summary>
482+
private async Task LoadItemsAsync()
483+
{
484+
await OnItemsSourceChanged();
485+
}
486+
467487
/// <summary>
468488
/// Loads the images.
469489
/// </summary>
470490
private async Task LoadImages()
471491
{
472492
if (ItemsSource?.Any() != true) return;
473493

494+
_cancellationTokenSource?.Cancel();
474495
_cancellationTokenSource = new CancellationTokenSource();
475496
var token = _cancellationTokenSource.Token;
476497

498+
var timer = Stopwatch.StartNew();
477499

478-
479-
var timer = new Stopwatch();
480-
timer.Start();
481-
482-
// Initiate all values
483-
ExtendedGrid.CellSize = ThumbCellSize;
500+
// Make a local copy of images
484501
var pics = new Dictionary<int, string>(ItemsSource);
502+
503+
// Initialize dictionaries
485504
Keys = new ConcurrentDictionary<string, int>();
486505
ImageDct = new ConcurrentDictionary<string, Image>();
487506
Border = new ConcurrentDictionary<int, Border>();
488507
Selection = new ConcurrentDictionary<int, bool>();
489508
if (SelectBox) ChkBox = new ConcurrentDictionary<int, CheckBox>();
490509

510+
// Capture initial values from dependency properties
491511
int cellSize = await Application.Current.Dispatcher.InvokeAsync(() => ThumbCellSize);
492512
int thumbWidth = await Application.Current.Dispatcher.InvokeAsync(() => ThumbWidth);
493513
int thumbHeight = await Application.Current.Dispatcher.InvokeAsync(() => ThumbHeight);
514+
bool thumbGrid = await Application.Current.Dispatcher.InvokeAsync(() => ThumbGrid);
494515

495-
// Handle special cases
496-
if (cellSize == 0)
497-
{
498-
cellSize = 100;
499-
}
516+
// --- Handle special cases ---
517+
if (cellSize <= 0) cellSize = 100;
518+
if (thumbHeight <= 0 && thumbWidth <= 0) thumbHeight = 1;
500519

501-
if (thumbHeight == 0 && thumbWidth == 0)
520+
if (thumbHeight * thumbWidth < pics.Count)
502521
{
503-
thumbHeight = 1;
504-
}
505-
506-
if (ThumbHeight * thumbWidth < pics.Count)
507-
{
508-
if (thumbWidth == 1)
509-
{
510-
thumbHeight = pics.Count;
511-
}
512-
513-
if (thumbHeight == 1)
514-
{
515-
thumbWidth = pics.Count;
516-
}
522+
// Only one row or one column
523+
if (thumbWidth == 1) thumbHeight = pics.Count;
524+
if (thumbHeight == 1) thumbWidth = pics.Count;
517525

526+
// Neither is 1 → calculate width based on height
518527
if (thumbHeight != 1 && thumbWidth != 1 && pics.Count > 1)
519528
{
520529
var fraction = new Fraction(pics.Count, thumbHeight);
521530
thumbWidth = (int)Math.Ceiling(fraction.Decimal);
522531
}
523532
}
524533

525-
// Setup the grid layout
526-
var exGrid = ExtendedGrid.ExtendGrid(ThumbWidth, ThumbHeight, ThumbGrid);
534+
// Update dependency properties so other code/XAML sees correct sizes
535+
ThumbWidth = thumbWidth;
536+
ThumbHeight = thumbHeight;
537+
ThumbCellSize = cellSize;
527538

539+
// --- Setup grid layout ---
540+
var exGrid = ExtendedGrid.ExtendGrid(thumbWidth, thumbHeight, thumbGrid);
541+
Thb.Children.Clear();
528542
_ = Thb.Children.Add(exGrid);
529543

530-
var semaphore = new SemaphoreSlim(4); // limit concurrent image loads
544+
// --- Load images concurrently with semaphore ---
545+
var semaphore = new SemaphoreSlim(4);
531546
var tasks = pics.Select(async kv =>
532547
{
533548
await semaphore.WaitAsync(token);
534549
try
535550
{
536-
await LoadSingleImage(kv.Key, kv.Value, exGrid, token);
551+
await LoadSingleImage(kv.Key, kv.Value, exGrid, token, cellSize, thumbWidth);
537552
}
538553
finally
539554
{
@@ -543,29 +558,30 @@ private async Task LoadImages()
543558

544559
await Task.WhenAll(tasks);
545560

546-
// Wait for all remaining tasks
547-
await Task.WhenAll(tasks);
548-
549561
timer.Stop();
550-
Trace.WriteLine(string.Concat(ComCtlResources.DebugTimer, timer.Elapsed));
562+
Trace.WriteLine($"{ComCtlResources.DebugTimer}{timer.Elapsed}");
551563

552564
// Notify that loading is finished
553565
ImageLoaded?.Invoke();
554566
}
555567

556568
/// <summary>
557-
/// Loads the image asynchronous.
569+
/// Loads the image asynchronous.
558570
/// </summary>
559571
/// <param name="key">The key.</param>
560572
/// <param name="filePath">The file path.</param>
561573
/// <param name="exGrid">The ex grid.</param>
562-
/// <returns>Load all images async</returns>
563-
private async Task LoadSingleImage(int key, string filePath, Panel exGrid, CancellationToken token)
574+
/// <param name="token">The token.</param>
575+
/// <param name="cellSize">Size of the cell.</param>
576+
/// <param name="thumbWidth">Width of the thumb.</param>
577+
/// <returns>
578+
/// Load all images async
579+
/// </returns>
580+
private async Task LoadSingleImage(int key, string filePath, Panel exGrid, CancellationToken token, int cellSize, int thumbWidth)
564581
{
565582
if (token.IsCancellationRequested) return;
566583

567584
// Capture UI-thread values safely
568-
int cellSize = await Application.Current.Dispatcher.InvokeAsync(() => ThumbCellSize);
569585
bool isCheckBoxSelected = await Application.Current.Dispatcher.InvokeAsync(() => IsCheckBoxSelected);
570586

571587
// Load the bitmap off the UI thread
@@ -620,14 +636,14 @@ await Application.Current.Dispatcher.InvokeAsync(() =>
620636
Border.TryAdd(key, border);
621637

622638
// Grid placement
623-
Grid.SetRow(border, key / ThumbWidth);
624-
Grid.SetColumn(border, key % ThumbWidth);
639+
Grid.SetRow(border, key / thumbWidth);
640+
Grid.SetColumn(border, key % thumbWidth);
625641
_ = exGrid.Children.Add(border);
626642

627643
if (SelectBox && checkbox != null)
628644
{
629-
Grid.SetRow(checkbox, key / ThumbWidth);
630-
Grid.SetColumn(checkbox, key % ThumbWidth);
645+
Grid.SetRow(checkbox, key / thumbWidth);
646+
Grid.SetColumn(checkbox, key % thumbWidth);
631647
_ = exGrid.Children.Add(checkbox);
632648

633649
// Attach right-click event for context menu

ViewModel/AsyncDelegateCommand.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ public bool CanExecute(object parameter)
6868
return _canExecute?.Invoke((T)parameter) ?? true;
6969
}
7070

71-
7271
/// <summary>
7372
/// Raises the <see cref="CanExecuteChanged" /> event to force WPF to re-query CanExecute.
7473
/// </summary>
@@ -85,4 +84,4 @@ public event EventHandler CanExecuteChanged
8584
add => CommandManager.RequerySuggested += value;
8685
remove => CommandManager.RequerySuggested -= value;
8786
}
88-
}
87+
}

ViewModel/DelegateCommand.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public sealed class DelegateCommand<T> : ICommand
3232
/// </summary>
3333
private readonly Predicate<T>? _canExecute;
3434

35+
/// <summary>
36+
/// The execute command.
37+
/// </summary>
38+
private readonly Action<T?> _execute;
39+
3540
/// <summary>
3641
/// Initializes a new instance of the <see cref="DelegateCommand{T}" /> class.
3742
/// </summary>
@@ -41,9 +46,9 @@ public sealed class DelegateCommand<T> : ICommand
4146
/// executable.
4247
/// </param>
4348
/// <exception cref="ArgumentNullException">Thrown when the action is null.</exception>
44-
public DelegateCommand(Action<T> action, Predicate<T>? canExecute = null)
49+
public DelegateCommand(Action<T?> execute, Predicate<T?>? canExecute = null)
4550
{
46-
_action = action ?? throw new ArgumentNullException(nameof(action));
51+
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
4752
_canExecute = canExecute;
4853
}
4954

@@ -53,28 +58,22 @@ public DelegateCommand(Action<T> action, Predicate<T>? canExecute = null)
5358
/// </summary>
5459
/// <param name="parameter">The parameter for the action.</param>
5560
public void Execute(object? parameter)
56-
{
57-
_action((T)parameter);
58-
}
61+
=> _execute((parameter is T t) ? t : default);
5962

6063
/// <inheritdoc />
6164
/// <summary>
6265
/// Determines if the command can execute.
6366
/// </summary>
6467
/// <param name="parameter">The parameter for the predicate.</param>
6568
/// <returns>True if the command can execute, otherwise false.</returns>
66-
public bool CanExecute(object parameter)
67-
{
68-
return _canExecute?.Invoke((T)parameter) ?? true;
69-
}
69+
public bool CanExecute(object? parameter)
70+
=> _canExecute?.Invoke((parameter is T t) ? t : default) ?? true;
7071

7172
/// <summary>
7273
/// Raises the <see cref="CanExecuteChanged"/> event to force WPF to re-query CanExecute.
7374
/// </summary>
7475
public void RaiseCanExecuteChanged()
75-
{
76-
CommandManager.InvalidateRequerySuggested();
77-
}
76+
=> CommandManager.InvalidateRequerySuggested();
7877

7978
/// <inheritdoc />
8079
/// <summary>

ViewModel/ObservableObject.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,5 @@ public class ObservableObject : INotifyPropertyChanged
2828
/// </summary>
2929
/// <param name="propertyName">The name of the property that changed.</param>
3030
protected void RaisePropertyChangedEvent([CallerMemberName] string? propertyName = null)
31-
{
32-
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
33-
}
34-
}
31+
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
32+
}

ViewModel/RelayCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,4 @@ public RelayCommand(Action execute, Func<bool>? canExecute = null)
6767
public void RaiseCanExecuteChanged() =>
6868
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
6969
}
70-
}
70+
}

0 commit comments

Comments
 (0)