diff --git a/Source/LibationWinForms/ProcessQueue/ProcessBookControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessBookControl.cs
index 89bcf31c..3045d2e2 100644
--- a/Source/LibationWinForms/ProcessQueue/ProcessBookControl.cs
+++ b/Source/LibationWinForms/ProcessQueue/ProcessBookControl.cs
@@ -22,6 +22,49 @@ namespace LibationWinForms.ProcessQueue
public static Color QueuedColor = SystemColors.Control;
public static Color SuccessColor = Color.PaleGreen;
+ private ProcessBookViewModelBase m_Context;
+ public ProcessBookViewModelBase Context
+ {
+ get => m_Context;
+ set
+ {
+ if (m_Context != value)
+ {
+ OnContextChanging();
+ m_Context = value;
+ OnContextChanged();
+ }
+ }
+ }
+
+ private void OnContextChanging()
+ {
+ if (Context is not null)
+ Context.PropertyChanged -= Context_PropertyChanged;
+ }
+
+ private void OnContextChanged()
+ {
+ Context.PropertyChanged += Context_PropertyChanged;
+ Context_PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(null));
+ }
+
+ private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ SuspendLayout();
+ if (e.PropertyName is null or nameof(Context.Cover))
+ SetCover(Context.Cover as Image);
+ if (e.PropertyName is null or nameof(Context.Title) or nameof(Context.Author) or nameof(Context.Narrator))
+ SetBookInfo($"{Context.Title}\r\nBy {Context.Author}\r\nNarrated by {Context.Narrator}");
+ if (e.PropertyName is null or nameof(Context.Status) or nameof(Context.StatusText))
+ SetStatus(Context.Status, Context.StatusText);
+ if (e.PropertyName is null or nameof(Context.Progress))
+ SetProgress(Context.Progress);
+ if (e.PropertyName is null or nameof(Context.TimeRemaining))
+ SetRemainingTime(Context.TimeRemaining);
+ ResumeLayout();
+ }
+
public ProcessBookControl()
{
InitializeComponent();
diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.Designer.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.Designer.cs
index 477c8a39..85fdb856 100644
--- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.Designer.cs
+++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.Designer.cs
@@ -162,7 +162,6 @@
this.virtualFlowControl2.Name = "virtualFlowControl2";
this.virtualFlowControl2.Size = new System.Drawing.Size(390, 424);
this.virtualFlowControl2.TabIndex = 3;
- this.virtualFlowControl2.VirtualControlCount = 0;
//
// panel1
//
diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs
index 50bc57c9..7a67d31f 100644
--- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs
+++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs
@@ -1,9 +1,6 @@
using LibationFileManager;
using LibationUiBase;
-using LibationUiBase.ProcessQueue;
using System;
-using System.Collections;
-using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
@@ -34,12 +31,11 @@ internal partial class ProcessQueueControl : UserControl
numericUpDown1.Value = speedLimitMBps > numericUpDown1.Maximum || speedLimitMBps < numericUpDown1.Minimum ? 0 : speedLimitMBps;
statusStrip1.Items.Add(PopoutButton);
- virtualFlowControl2.RequestData += VirtualFlowControl1_RequestData;
virtualFlowControl2.ButtonClicked += VirtualFlowControl2_ButtonClicked;
ViewModel.LogWritten += (_, text) => WriteLine(text);
ViewModel.PropertyChanged += ProcessQueue_PropertyChanged;
- ViewModel.BookPropertyChanged += ProcessBook_PropertyChanged;
+ virtualFlowControl2.Items = ViewModel.Items;
Load += ProcessQueueControl_Load;
}
@@ -60,15 +56,13 @@ internal partial class ProcessQueueControl : UserControl
ViewModel.Queue.ClearQueue();
if (ViewModel.Queue.Current is not null)
await ViewModel.Queue.Current.CancelAsync();
- virtualFlowControl2.VirtualControlCount = ViewModel.Queue.Count;
- UpdateAllControls();
+ virtualFlowControl2.RefreshDisplay();
}
private void btnClearFinished_Click(object? sender, EventArgs e)
{
ViewModel.Queue.ClearCompleted();
- virtualFlowControl2.VirtualControlCount = ViewModel.Queue.Count;
- UpdateAllControls();
+ virtualFlowControl2.RefreshDisplay();
if (!ViewModel.Running)
runningTimeLbl.Text = string.Empty;
@@ -92,22 +86,13 @@ internal partial class ProcessQueueControl : UserControl
#region View-Model update event handling
- private void ProcessBook_PropertyChanged(object? sender, PropertyChangedEventArgs e)
- {
- if (sender is not ProcessBookViewModel pbvm)
- return;
-
- int index = ViewModel.Queue.IndexOf(pbvm);
- UpdateControl(index, e.PropertyName);
- }
-
private void ProcessQueue_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName is null or nameof(ViewModel.QueuedCount))
{
queueNumberLbl.Text = ViewModel.QueuedCount.ToString();
queueNumberLbl.Visible = ViewModel.QueuedCount > 0;
- virtualFlowControl2.VirtualControlCount = ViewModel.Queue.Count;
+ virtualFlowControl2.RefreshDisplay();
}
if (e.PropertyName is null or nameof(ViewModel.ErrorCount))
{
@@ -134,107 +119,41 @@ internal partial class ProcessQueueControl : UserControl
}
}
- ///
- /// Index of the first visible in the
- ///
- private int FirstVisible = 0;
- ///
- /// Number of visible in the
- ///
- private int NumVisible = 0;
- ///
- /// Controls displaying the state, starting with
- ///
- private IReadOnlyList? Panels;
-
- ///
- /// Updates the display of a single at within
- ///
- /// index of the within the
- /// The nme of the property that needs updating. If null, all properties are updated.
- private void UpdateControl(int queueIndex, string? propertyName = null)
- {
- try
- {
- int i = queueIndex - FirstVisible;
-
- if (Panels is null || i > NumVisible || i < 0) return;
-
- var proc = ViewModel.Queue[queueIndex];
-
- Invoke(() =>
- {
- Panels[i].SuspendLayout();
- if (propertyName is null or nameof(proc.Cover))
- Panels[i].SetCover(proc.Cover as Image);
- if (propertyName is null or nameof(proc.Title) or nameof(proc.Author) or nameof(proc.Narrator))
- Panels[i].SetBookInfo($"{proc.Title}\r\nBy {proc.Author}\r\nNarrated by {proc.Narrator}");
- if (propertyName is null or nameof(proc.Status) or nameof(proc.StatusText))
- Panels[i].SetStatus(proc.Status, proc.StatusText);
- if (propertyName is null or nameof(proc.Progress))
- Panels[i].SetProgress(proc.Progress);
- if (propertyName is null or nameof(proc.TimeRemaining))
- Panels[i].SetRemainingTime(proc.TimeRemaining);
- Panels[i].ResumeLayout();
- });
- }
- catch (Exception ex)
- {
- Serilog.Log.Logger.Error(ex, "Error updating the queued item's display.");
- }
- }
-
- private void UpdateAllControls()
- {
- int numToShow = Math.Min(NumVisible, ViewModel.Queue.Count - FirstVisible);
-
- for (int i = 0; i < numToShow; i++)
- UpdateControl(FirstVisible + i);
- }
-
///
/// View notified the model that a botton was clicked
///
- /// index of the within
- /// The clicked control to update
- private async void VirtualFlowControl2_ButtonClicked(int queueIndex, string buttonName, ProcessBookControl panelClicked)
+ /// the whose button was clicked
+ /// The name of the button clicked
+ private async void VirtualFlowControl2_ButtonClicked(object? sender, string buttonName)
{
+ if (sender is not ProcessBookControl control || control.Context is not ProcessBookViewModel item)
+ return;
+
try
{
- var item = ViewModel.Queue[queueIndex];
- if (buttonName == nameof(panelClicked.cancelBtn))
+ if (buttonName is nameof(ProcessBookControl.cancelBtn))
{
- if (item is not null)
+ await item.CancelAsync();
+ ViewModel.Queue.RemoveQueued(item);
+ virtualFlowControl2.RefreshDisplay();
+ }
+ else
+ {
+ QueuePosition? position = buttonName switch
{
- await item.CancelAsync();
- if (ViewModel.Queue.RemoveQueued(item))
- virtualFlowControl2.VirtualControlCount = ViewModel.Queue.Count;
+ nameof(ProcessBookControl.moveFirstBtn) => QueuePosition.Fisrt,
+ nameof(ProcessBookControl.moveUpBtn) => QueuePosition.OneUp,
+ nameof(ProcessBookControl.moveDownBtn) => QueuePosition.OneDown,
+ nameof(ProcessBookControl.moveLastBtn) => QueuePosition.Last,
+ _ => null
+ };
+
+ if (position is not null)
+ {
+ ViewModel.Queue.MoveQueuePosition(item, position.Value);
+ virtualFlowControl2.RefreshDisplay();
}
}
- else if (buttonName == nameof(panelClicked.moveFirstBtn))
- {
- ViewModel.Queue.MoveQueuePosition(item, QueuePosition.Fisrt);
- UpdateAllControls();
- }
- else if (buttonName == nameof(panelClicked.moveUpBtn))
- {
- ViewModel.Queue.MoveQueuePosition(item, QueuePosition.OneUp);
- UpdateControl(queueIndex);
- if (queueIndex > 0)
- UpdateControl(queueIndex - 1);
- }
- else if (buttonName == nameof(panelClicked.moveDownBtn))
- {
- ViewModel.Queue.MoveQueuePosition(item, QueuePosition.OneDown);
- UpdateControl(queueIndex);
- if (queueIndex + 1 < ViewModel.Queue.Count)
- UpdateControl(queueIndex + 1);
- }
- else if (buttonName == nameof(panelClicked.moveLastBtn))
- {
- ViewModel.Queue.MoveQueuePosition(item, QueuePosition.Last);
- UpdateAllControls();
- }
}
catch(Exception ex)
{
@@ -242,17 +161,6 @@ internal partial class ProcessQueueControl : UserControl
}
}
- ///
- /// View needs updating
- ///
- private void VirtualFlowControl1_RequestData(int firstIndex, int numVisible, IReadOnlyList panelsToFill)
- {
- FirstVisible = firstIndex;
- NumVisible = numVisible;
- Panels = panelsToFill;
- UpdateAllControls();
- }
-
#endregion
private void numericUpDown1_ValueChanged(object? sender, EventArgs e)
diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueViewModel.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueViewModel.cs
index 6c4afd63..e8fb4d1e 100644
--- a/Source/LibationWinForms/ProcessQueue/ProcessQueueViewModel.cs
+++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueViewModel.cs
@@ -1,11 +1,7 @@
using DataLayer;
using LibationUiBase.ProcessQueue;
using System;
-using System.Collections;
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Linq;
+using System.Collections.Generic;
#nullable enable
namespace LibationWinForms.ProcessQueue;
@@ -13,62 +9,16 @@ namespace LibationWinForms.ProcessQueue;
internal class ProcessQueueViewModel : ProcessQueueViewModelBase
{
public event EventHandler? LogWritten;
- ///
- /// Fires when a ProcessBookViewModelBase in the queue has a property changed
- ///
- public event EventHandler? BookPropertyChanged;
- private ObservableCollection Items { get; }
+ public List Items { get; }
- public ProcessQueueViewModel() : base(CreateEmptyList())
+ public ProcessQueueViewModel() : base(new List())
{
- Items = Queue.UnderlyingList as ObservableCollection
+ Items = Queue.UnderlyingList as List
?? throw new ArgumentNullException(nameof(Queue.UnderlyingList));
- Items.CollectionChanged += Items_CollectionChanged;
}
- private void Items_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
- {
- switch (e.Action)
- {
- case NotifyCollectionChangedAction.Add:
- subscribe(e.NewItems);
- break;
- case NotifyCollectionChangedAction.Remove:
- unubscribe(e.OldItems);
- break;
- }
-
- void subscribe(IList? items)
- {
- foreach (var item in e.NewItems?.OfType() ?? [])
- item.PropertyChanged += Item_PropertyChanged;
- }
-
- void unubscribe(IList? items)
- {
- foreach (var item in e.NewItems?.OfType() ?? [])
- item.PropertyChanged -= Item_PropertyChanged;
- }
- }
-
- private void Item_PropertyChanged(object? sender, PropertyChangedEventArgs e)
- => BookPropertyChanged?.Invoke(sender, e);
-
public override void WriteLine(string text) => Invoke(() => LogWritten?.Invoke(this, text.Trim()));
protected override ProcessBookViewModelBase CreateNewProcessBook(LibraryBook libraryBook)
=> new ProcessBookViewModel(libraryBook, Logger);
-
- private static ObservableCollection CreateEmptyList()
- => new ProcessBookCollection();
-
- private class ProcessBookCollection : ObservableCollection
- {
- protected override void ClearItems()
- {
- //ObservableCollection doesn't raise Remove for each item on Clear, so we need to do it ourselves.
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, this));
- base.ClearItems();
- }
- }
}
diff --git a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs
index e0cadbc7..4149476b 100644
--- a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs
+++ b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs
@@ -1,44 +1,42 @@
-using System;
+using LibationUiBase.ProcessQueue;
+using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace LibationWinForms.ProcessQueue
{
-
- internal delegate void RequestDataDelegate(int queueIndex, int numVisible, IReadOnlyList panelsToFill);
- internal delegate void ControlButtonClickedDelegate(int queueIndex, string buttonName, ProcessBookControl panelClicked);
internal partial class VirtualFlowControl : UserControl
{
- ///
- /// Triggered when the needs to update the displayed s
- ///
- public event RequestDataDelegate RequestData;
///
/// Triggered when one of the 's buttons has been clicked
///
- public event ControlButtonClickedDelegate ButtonClicked;
+ public event EventHandler ButtonClicked;
+
+ private List m_Items;
+ public List Items
+ {
+ get => m_Items;
+ set
+ {
+ m_Items = value;
+ if (m_Items is not null)
+ RefreshDisplay();
+ }
+ }
+
+ public void RefreshDisplay()
+ {
+ AdjustScrollBar();
+ DoVirtualScroll();
+ }
#region Dynamic Properties
///
/// The number of virtual s in the
///
- public int VirtualControlCount
- {
- get => _virtualControlCount;
- set
- {
- if (_virtualControlCount == 0)
- vScrollBar1.Value = 0;
-
- _virtualControlCount = value;
- AdjustScrollBar();
- DoVirtualScroll();
- }
- }
-
- private int _virtualControlCount;
+ public int VirtualControlCount => Items?.Count ?? 0;
int ScrollValue => Math.Max(vScrollBar1.Value, 0);
///
@@ -100,12 +98,7 @@ namespace LibationWinForms.ProcessQueue
{
InitializeComponent();
- panel1.Resize += (_, _) =>
- {
- AdjustScrollBar();
- DoVirtualScroll();
- };
-
+ panel1.Resize += (_, _) => RefreshDisplay();
var control = InitControl(0);
VirtualControlHeight = this.DpiUnscale(control.Height + control.Margin.Top + control.Margin.Bottom);
@@ -151,9 +144,7 @@ namespace LibationWinForms.ProcessQueue
while (form is not ProcessBookControl)
form = form.Parent;
- int clickedIndex = BookControls.IndexOf((ProcessBookControl)form);
-
- ButtonClicked?.Invoke(FirstVisibleVirtualIndex + clickedIndex, button.Name, BookControls[clickedIndex]);
+ ButtonClicked?.Invoke(form, button.Name);
}
///
@@ -186,7 +177,8 @@ namespace LibationWinForms.ProcessQueue
///
/// Calculated the virtual controls that are in view at the currrent scroll position and windows size,
- /// positions to simulate scroll activity, then fires to notify the model to update all visible controls
+ /// positions to simulate scroll activity, then fires updates the controls with
+ /// the context corresponding to the virtual scroll position
///
private void DoVirtualScroll()
{
@@ -203,10 +195,15 @@ namespace LibationWinForms.ProcessQueue
numVisible = Math.Min(numVisible, VirtualControlCount);
numVisible = Math.Min(numVisible, VirtualControlCount - firstVisible);
- RequestData?.Invoke(firstVisible, numVisible, BookControls);
+ for (int i = 0; i < numVisible; i++)
+ {
+ BookControls[i].Context = Items[firstVisible + i];
+ }
for (int i = 0; i < BookControls.Count; i++)
+ {
BookControls[i].Visible = i < numVisible;
+ }
}
///