Move ProcessQueue biz logic into viewmodel
This commit is contained in:
parent
3a61c32881
commit
c727286d22
BIN
Source/LibationWinForms/AvaloniaUI/Assets/glass-with-glow_16.png
Normal file
BIN
Source/LibationWinForms/AvaloniaUI/Assets/glass-with-glow_16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 482 B |
@ -1,14 +0,0 @@
|
||||
using Dinah.Core.Threading;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace LibationWinForms.AvaloniaUI
|
||||
{
|
||||
public abstract class AsyncNotifyPropertyChanged2 : INotifyPropertyChanged
|
||||
{
|
||||
// see also notes in Libation/Source/_ARCHITECTURE NOTES.txt :: MVVM
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
|
||||
=> Avalonia.Threading.Dispatcher.UIThread.Post(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)));
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,10 @@
|
||||
using Avalonia.Media;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationWinForms.AvaloniaUI
|
||||
{
|
||||
internal static class AvaloniaUtils
|
||||
{
|
||||
|
||||
public static IBrush GetBrushFromResources(string name)
|
||||
=> GetBrushFromResources(name, Brushes.Transparent);
|
||||
public static IBrush GetBrushFromResources(string name, IBrush defaultBrush)
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
using Avalonia.Threading;
|
||||
using ApplicationServices;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationWinForms.AvaloniaUI.ViewModels
|
||||
{
|
||||
@ -15,7 +19,52 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
|
||||
set => this.RaiseAndSetIfChanged(ref _items, value);
|
||||
}
|
||||
|
||||
private TrackedQueue2<ProcessBook2> Queue => Items;
|
||||
|
||||
public ProcessBook2 SelectedItem { get; set; }
|
||||
public Task QueueRunner { get; private set; }
|
||||
public bool Running => !QueueRunner?.IsCompleted ?? false;
|
||||
|
||||
public ProcessQueueViewModel()
|
||||
{
|
||||
Queue.QueuededCountChanged += Queue_QueuededCountChanged;
|
||||
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
|
||||
}
|
||||
|
||||
private int _completedCount;
|
||||
private int _errorCount;
|
||||
private int _queuedCount;
|
||||
private string _runningTime;
|
||||
private bool _progressBarVisible;
|
||||
|
||||
public int CompletedCount { get => _completedCount; private set { this.RaiseAndSetIfChanged(ref _completedCount, value); this.RaisePropertyChanged(nameof(AnyCompleted)); } }
|
||||
public int QueuedCount { get => _queuedCount; private set { this.RaiseAndSetIfChanged(ref _queuedCount, value); this.RaisePropertyChanged(nameof(AnyQueued)); } }
|
||||
public int ErrorCount { get => _errorCount; private set { this.RaiseAndSetIfChanged(ref _errorCount, value); this.RaisePropertyChanged(nameof(AnyErrors)); } }
|
||||
public string RunningTime { get => _runningTime; set { this.RaiseAndSetIfChanged(ref _runningTime, value); } }
|
||||
public bool ProgressBarVisible { get => _progressBarVisible; set { this.RaiseAndSetIfChanged(ref _progressBarVisible, value); } }
|
||||
|
||||
public bool AnyCompleted => CompletedCount > 0;
|
||||
public bool AnyQueued => QueuedCount > 0;
|
||||
public bool AnyErrors => ErrorCount > 0;
|
||||
|
||||
public double Progress => 100d * Queue.Completed.Count / Queue.Count;
|
||||
|
||||
|
||||
|
||||
private void Queue_CompletedCountChanged(object sender, int e)
|
||||
{
|
||||
int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail);
|
||||
int completeCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.Success);
|
||||
|
||||
ErrorCount = errCount;
|
||||
CompletedCount = completeCount;
|
||||
this.RaisePropertyChanged(nameof(Progress));
|
||||
}
|
||||
private void Queue_QueuededCountChanged(object sender, int cueCount)
|
||||
{
|
||||
QueuedCount = cueCount;
|
||||
this.RaisePropertyChanged(nameof(Progress));
|
||||
}
|
||||
|
||||
public void WriteLine(string text)
|
||||
{
|
||||
@ -26,6 +75,69 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
|
||||
LogMessage = text.Trim()
|
||||
}));
|
||||
}
|
||||
|
||||
public void AddToQueue(IEnumerable<ProcessBook2> pbook)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
Queue.Enqueue(pbook);
|
||||
if (!Running)
|
||||
QueueRunner = QueueLoop();
|
||||
});
|
||||
}
|
||||
|
||||
DateTime StartingTime;
|
||||
private async Task QueueLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin processing queue");
|
||||
|
||||
RunningTime = string.Empty;
|
||||
ProgressBarVisible = true;
|
||||
StartingTime = DateTime.Now;
|
||||
|
||||
using var counterTimer = new System.Threading.Timer(CounterTimer_Tick, null, 0, 500);
|
||||
|
||||
while (Queue.MoveNext())
|
||||
{
|
||||
var nextBook = Queue.Current;
|
||||
|
||||
Serilog.Log.Logger.Information("Begin processing queued item. {item_LibraryBook}", nextBook?.LibraryBook);
|
||||
|
||||
var result = await nextBook.ProcessOneAsync();
|
||||
|
||||
Serilog.Log.Logger.Information("Completed processing queued item: {item_LibraryBook}\r\nResult: {result}", nextBook?.LibraryBook, result);
|
||||
|
||||
if (result == ProcessBookResult.ValidationFail)
|
||||
Queue.ClearCurrent();
|
||||
else if (result == ProcessBookResult.FailedAbort)
|
||||
Queue.ClearQueue();
|
||||
else if (result == ProcessBookResult.FailedSkip)
|
||||
nextBook.LibraryBook.Book.UpdateBookStatus(DataLayer.LiberatedStatus.Error);
|
||||
}
|
||||
Serilog.Log.Logger.Information("Completed processing queue");
|
||||
|
||||
Queue_CompletedCountChanged(this, 0);
|
||||
ProgressBarVisible = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Logger.Error(ex, "An error was encountered while processing queued items");
|
||||
}
|
||||
}
|
||||
|
||||
private void CounterTimer_Tick(object? state)
|
||||
{
|
||||
string timeToStr(TimeSpan time)
|
||||
{
|
||||
string minsSecs = $"{time:mm\\:ss}";
|
||||
if (time.TotalHours >= 1)
|
||||
return $"{time.TotalHours:F0}:{minsSecs}";
|
||||
return minsSecs;
|
||||
}
|
||||
RunningTime = timeToStr(DateTime.Now - StartingTime);
|
||||
}
|
||||
}
|
||||
|
||||
public class LogEntry
|
||||
|
||||
@ -8,7 +8,9 @@
|
||||
xmlns:prgid="clr-namespace:LibationWinForms.AvaloniaUI.Views.ProductsGrid"
|
||||
xmlns:controls="clr-namespace:LibationWinForms.AvaloniaUI.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="2000" d:DesignHeight="700"
|
||||
x:Class="LibationWinForms.AvaloniaUI.Views.MainWindow" Title="MainWindow">
|
||||
x:Class="LibationWinForms.AvaloniaUI.Views.MainWindow"
|
||||
Title="MainWindow"
|
||||
Icon="/AvaloniaUI/Assets/glass-with-glow_16.png">
|
||||
|
||||
<Border BorderBrush="{DynamicResource DataGridGridLinesBrush}" BorderThickness="2" Padding="15">
|
||||
<Grid RowDefinitions="Auto,Auto,*,Auto">
|
||||
|
||||
@ -91,24 +91,24 @@
|
||||
<Setter Property="MinWidth" Value="100" />
|
||||
</Style>
|
||||
</Panel.Styles>
|
||||
<ProgressBar Name="toolStripProgressBar1" ShowProgressText="True" />
|
||||
<ProgressBar IsVisible="{Binding ProgressBarVisible}" Value="{Binding Progress}" ShowProgressText="True" />
|
||||
</Panel>
|
||||
<StackPanel Orientation="Horizontal" Grid.Column="1">
|
||||
<StackPanel Margin="5,0,0,0" Orientation="Horizontal">
|
||||
<Image Name="queueNumberLbl_Icon" Width="20" Height="20" Source="/AvaloniaUI/Assets/queued.png" />
|
||||
<TextBlock Name="queueNumberLbl_Text" VerticalAlignment="Center" Text="[Q#]" />
|
||||
<Image IsVisible="{Binding AnyQueued}" Width="20" Height="20" Source="/AvaloniaUI/Assets/queued.png" />
|
||||
<TextBlock IsVisible="{Binding AnyQueued}" VerticalAlignment="Center" Text="{Binding QueuedCount}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="5,0,0,0" Orientation="Horizontal">
|
||||
<Image Name="completedNumberLbl_Icon" Width="20" Height="20" Source="/AvaloniaUI/Assets/completed.png" />
|
||||
<TextBlock Name="completedNumberLbl_Text" VerticalAlignment="Center" Text="[DL#]" />
|
||||
<Image IsVisible="{Binding AnyCompleted}" Width="20" Height="20" Source="/AvaloniaUI/Assets/completed.png" />
|
||||
<TextBlock IsVisible="{Binding AnyCompleted}" VerticalAlignment="Center" Text="{Binding CompletedCount}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="5,0,0,0" Orientation="Horizontal">
|
||||
<Image Name="errorNumberLbl_Icon" Width="20" Height="20" Source="/AvaloniaUI/Assets/errored.png" />
|
||||
<TextBlock Name="errorNumberLbl_Text" VerticalAlignment="Center" Text="[ERR#]" />
|
||||
<Image IsVisible="{Binding AnyErrors}" Width="20" Height="20" Source="/AvaloniaUI/Assets/errored.png" />
|
||||
<TextBlock IsVisible="{Binding AnyErrors}" VerticalAlignment="Center" Text="{Binding ErrorCount}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Panel Grid.Column="2" Margin="0,0,5,0" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<TextBlock Name="runningTimeLbl">00:00:25</TextBlock>
|
||||
<TextBlock Text="{Binding RunningTime}" />
|
||||
</Panel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@ -26,37 +26,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
private TrackedQueue2<ProcessBook2> Queue => _viewModel.Items;
|
||||
|
||||
private readonly ProcessQueue.LogMe Logger;
|
||||
private int QueuedCount
|
||||
{
|
||||
set
|
||||
{
|
||||
queueNumberLbl_Text.Text = value.ToString();
|
||||
queueNumberLbl_Text.IsVisible = value > 0;
|
||||
queueNumberLbl_Icon.IsVisible = value > 0;
|
||||
}
|
||||
}
|
||||
private int ErrorCount
|
||||
{
|
||||
set
|
||||
{
|
||||
errorNumberLbl_Text.Text = value.ToString();
|
||||
errorNumberLbl_Text.IsVisible = value > 0;
|
||||
errorNumberLbl_Icon.IsVisible = value > 0;
|
||||
}
|
||||
}
|
||||
|
||||
private int CompletedCount
|
||||
{
|
||||
set
|
||||
{
|
||||
completedNumberLbl_Text.Text = value.ToString();
|
||||
completedNumberLbl_Text.IsVisible = value > 0;
|
||||
completedNumberLbl_Icon.IsVisible = value > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Task QueueRunner { get; private set; }
|
||||
public bool Running => !QueueRunner?.IsCompleted ?? false;
|
||||
|
||||
|
||||
public ProcessQueueControl2()
|
||||
{
|
||||
@ -71,22 +41,6 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
ProcessBookControl2.PositionButtonClicked += ProcessBookControl2_ButtonClicked;
|
||||
ProcessBookControl2.CancelButtonClicked += ProcessBookControl2_CancelButtonClicked;
|
||||
|
||||
queueNumberLbl_Icon = this.FindControl<Image>(nameof(queueNumberLbl_Icon));
|
||||
errorNumberLbl_Icon = this.FindControl<Image>(nameof(errorNumberLbl_Icon));
|
||||
completedNumberLbl_Icon = this.FindControl<Image>(nameof(completedNumberLbl_Icon));
|
||||
|
||||
queueNumberLbl_Text = this.FindControl<TextBlock>(nameof(queueNumberLbl_Text));
|
||||
errorNumberLbl_Text = this.FindControl<TextBlock>(nameof(errorNumberLbl_Text));
|
||||
completedNumberLbl_Text = this.FindControl<TextBlock>(nameof(completedNumberLbl_Text));
|
||||
|
||||
runningTimeLbl = this.FindControl<TextBlock>(nameof(runningTimeLbl));
|
||||
|
||||
toolStripProgressBar1 = this.FindControl<ProgressBar>(nameof(toolStripProgressBar1));
|
||||
|
||||
|
||||
Queue.QueuededCountChanged += Queue_QueuededCountChanged;
|
||||
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
|
||||
|
||||
#region Design Mode Testing
|
||||
if (Design.IsDesignMode)
|
||||
{
|
||||
@ -140,11 +94,6 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
runningTimeLbl.Text = string.Empty;
|
||||
QueuedCount = 0;
|
||||
ErrorCount = 0;
|
||||
CompletedCount = 0;
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
@ -152,61 +101,6 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private async void ProcessBookControl2_CancelButtonClicked(ProcessBook2 item)
|
||||
{
|
||||
if (item is not null)
|
||||
await item.CancelAsync();
|
||||
Queue.RemoveQueued(item);
|
||||
}
|
||||
|
||||
private void ProcessBookControl2_ButtonClicked(ProcessBook2 item, QueuePosition queueButton)
|
||||
{
|
||||
Queue.MoveQueuePosition(item, queueButton);
|
||||
}
|
||||
|
||||
private void RepeaterClick(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if ((e.Source as TextBlock)?.DataContext is ProcessBook2 item)
|
||||
{
|
||||
_viewModel.SelectedItem = item;
|
||||
_selectedIndex = _viewModel.Items.IndexOf(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void RepeaterOnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.F5)
|
||||
{
|
||||
//_viewModel.ResetItems();
|
||||
}
|
||||
}
|
||||
public async void CancelAllBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
Queue.ClearQueue();
|
||||
if (Queue.Current is not null)
|
||||
await Queue.Current.CancelAsync();
|
||||
}
|
||||
|
||||
public void ClearFinishedBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
Queue.ClearCompleted();
|
||||
|
||||
if (!Running)
|
||||
runningTimeLbl.Text = string.Empty;
|
||||
}
|
||||
|
||||
public void ClearLogBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
_viewModel.LogEntries.Clear();
|
||||
}
|
||||
|
||||
private void LogCopyBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
string logText = string.Join("\r\n", _viewModel.LogEntries.Select(r => $"{r.LogDate.ToShortDateString()} {r.LogDate.ToShortTimeString()}\t{r.LogMessage}"));
|
||||
System.Windows.Forms.Clipboard.SetDataObject(logText, false, 5, 150);
|
||||
}
|
||||
|
||||
|
||||
private bool isBookInQueue(LibraryBook libraryBook)
|
||||
=> Queue.Any(b => b?.LibraryBook?.Book?.AudibleProductId == libraryBook.Book.AudibleProductId);
|
||||
|
||||
@ -233,7 +127,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
}
|
||||
|
||||
Serilog.Log.Logger.Information("Queueing {count} books", procs.Count);
|
||||
AddToQueue(procs);
|
||||
_viewModel.AddToQueue(procs);
|
||||
}
|
||||
|
||||
public void AddDownloadDecrypt(IEnumerable<LibraryBook> entries)
|
||||
@ -251,7 +145,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
}
|
||||
|
||||
Serilog.Log.Logger.Information("Queueing {count} books", procs.Count);
|
||||
AddToQueue(procs);
|
||||
_viewModel.AddToQueue(procs);
|
||||
}
|
||||
|
||||
public void AddConvertMp3(IEnumerable<LibraryBook> entries)
|
||||
@ -268,75 +162,63 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
}
|
||||
|
||||
Serilog.Log.Logger.Information("Queueing {count} books", procs.Count);
|
||||
AddToQueue(procs);
|
||||
}
|
||||
private void AddToQueue(IEnumerable<ProcessBook2> pbook)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
Queue.Enqueue(pbook);
|
||||
if (!Running)
|
||||
QueueRunner = QueueLoop();
|
||||
});
|
||||
_viewModel.AddToQueue(procs);
|
||||
}
|
||||
|
||||
DateTime StartingTime;
|
||||
private async Task QueueLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin processing queue");
|
||||
|
||||
StartingTime = DateTime.Now;
|
||||
|
||||
using var counterTimer = new System.Threading.Timer(CounterTimer_Tick, null, 0, 500);
|
||||
|
||||
while (Queue.MoveNext())
|
||||
{
|
||||
var nextBook = Queue.Current;
|
||||
|
||||
Serilog.Log.Logger.Information("Begin processing queued item. {item_LibraryBook}", nextBook?.LibraryBook);
|
||||
|
||||
var result = await nextBook.ProcessOneAsync();
|
||||
|
||||
Serilog.Log.Logger.Information("Completed processing queued item: {item_LibraryBook}\r\nResult: {result}", nextBook?.LibraryBook, result);
|
||||
|
||||
if (result == ProcessBookResult.ValidationFail)
|
||||
Queue.ClearCurrent();
|
||||
else if (result == ProcessBookResult.FailedAbort)
|
||||
Queue.ClearQueue();
|
||||
else if (result == ProcessBookResult.FailedSkip)
|
||||
nextBook.LibraryBook.Book.UpdateBookStatus(DataLayer.LiberatedStatus.Error);
|
||||
}
|
||||
Serilog.Log.Logger.Information("Completed processing queue");
|
||||
|
||||
Queue_CompletedCountChanged(this, 0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Logger.Error(ex, "An error was encountered while processing queued items");
|
||||
}
|
||||
}
|
||||
#region Control event handlers
|
||||
|
||||
private void Queue_CompletedCountChanged(object sender, int e)
|
||||
private async void ProcessBookControl2_CancelButtonClicked(ProcessBook2 item)
|
||||
{
|
||||
int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail);
|
||||
int completeCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.Success);
|
||||
if (item is not null)
|
||||
await item.CancelAsync();
|
||||
Queue.RemoveQueued(item);
|
||||
}
|
||||
|
||||
ErrorCount = errCount;
|
||||
CompletedCount = completeCount;
|
||||
UpdateProgressBar();
|
||||
}
|
||||
private void Queue_QueuededCountChanged(object sender, int cueCount)
|
||||
private void ProcessBookControl2_ButtonClicked(ProcessBook2 item, QueuePosition queueButton)
|
||||
{
|
||||
QueuedCount = cueCount;
|
||||
UpdateProgressBar();
|
||||
Queue.MoveQueuePosition(item, queueButton);
|
||||
}
|
||||
private void UpdateProgressBar()
|
||||
|
||||
private void RepeaterClick(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
double percent = 100d * Queue.Completed.Count / Queue.Count;
|
||||
toolStripProgressBar1.Value = percent;
|
||||
if ((e.Source as TextBlock)?.DataContext is ProcessBook2 item)
|
||||
{
|
||||
_viewModel.SelectedItem = item;
|
||||
_selectedIndex = _viewModel.Items.IndexOf(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void RepeaterOnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.F5)
|
||||
{
|
||||
//_viewModel.ResetItems();
|
||||
}
|
||||
}
|
||||
public async void CancelAllBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
Queue.ClearQueue();
|
||||
if (Queue.Current is not null)
|
||||
await Queue.Current.CancelAsync();
|
||||
}
|
||||
|
||||
public void ClearFinishedBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
Queue.ClearCompleted();
|
||||
|
||||
if (!_viewModel.Running)
|
||||
_viewModel.RunningTime = string.Empty;
|
||||
}
|
||||
|
||||
public void ClearLogBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
_viewModel.LogEntries.Clear();
|
||||
}
|
||||
|
||||
private void LogCopyBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
string logText = string.Join("\r\n", _viewModel.LogEntries.Select(r => $"{r.LogDate.ToShortDateString()} {r.LogDate.ToShortTimeString()}\t{r.LogMessage}"));
|
||||
System.Windows.Forms.Clipboard.SetDataObject(logText, false, 5, 150);
|
||||
}
|
||||
|
||||
private async void cancelAllBtn_Click(object sender, EventArgs e)
|
||||
@ -350,22 +232,8 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
{
|
||||
Queue.ClearCompleted();
|
||||
|
||||
if (!Running)
|
||||
runningTimeLbl.Text = string.Empty;
|
||||
}
|
||||
|
||||
private void CounterTimer_Tick(object? state)
|
||||
{
|
||||
string timeToStr(TimeSpan time)
|
||||
{
|
||||
string minsSecs = $"{time:mm\\:ss}";
|
||||
if (time.TotalHours >= 1)
|
||||
return $"{time.TotalHours:F0}:{minsSecs}";
|
||||
return minsSecs;
|
||||
}
|
||||
|
||||
if (Running)
|
||||
Dispatcher.UIThread.Post(() => runningTimeLbl.Text = timeToStr(DateTime.Now - StartingTime));
|
||||
if (!_viewModel.Running)
|
||||
_viewModel.RunningTime = string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -46,6 +46,7 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
|
||||
}
|
||||
else
|
||||
{
|
||||
//List is already displayed. Replace all items with new ones, refilter, and re-sort
|
||||
string existingFilter = _viewModel?.GridEntries?.Filter;
|
||||
bindingList.ReplaceList(ProductsDisplayViewModel.CreateGridEntries(dbBooks));
|
||||
bindingList.Filter = existingFilter;
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
<None Remove="AvaloniaUI\Assets\edit_25x25.png" />
|
||||
<None Remove="AvaloniaUI\Assets\errored.png" />
|
||||
<None Remove="AvaloniaUI\Assets\first.png" />
|
||||
<None Remove="AvaloniaUI\Assets\glass-with-glow_16.png" />
|
||||
<None Remove="AvaloniaUI\Assets\import_16x16.png" />
|
||||
<None Remove="AvaloniaUI\Assets\last.png" />
|
||||
<None Remove="AvaloniaUI\Assets\LibationStyles.xaml" />
|
||||
|
||||
@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\publish\</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user