queues and scheduler window

master
subhra74 2022-01-14 16:43:59 +05:30
parent d62b43be3d
commit 3f397e8503
7 changed files with 491 additions and 24 deletions

View File

@ -18,7 +18,7 @@
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.ColumnSpan="2" MinWidth="150">
<ListBox Grid.ColumnSpan="2" MinWidth="150" Name="LbQueues">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
@ -26,7 +26,7 @@
</ListBox.ItemTemplate>
</ListBox>
<Button Name="BtnNew" Content="{StaticResource DESC_NEW}" Margin="5,10,5,5" Padding="3" Grid.Row="1" Click="BtnNew_Click"/>
<Button Name="BtnDel" Content="{StaticResource DESC_DEL}" Grid.Column="1" Margin="5,10,5,5" Padding="3" Grid.Row="1"/>
<Button Name="BtnDel" Content="{StaticResource DESC_DEL}" Grid.Column="1" Margin="5,10,5,5" Padding="3" Grid.Row="1" Click="BtnDel_Click"/>
</Grid>
<Grid Margin="0,5,10,5">
<Grid.ColumnDefinitions>
@ -40,7 +40,7 @@
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TabControl Grid.ColumnSpan="5">
<TabControl Grid.ColumnSpan="5" Name="Tab">
<TabItem Header="{StaticResource Q_LIST_FILES}">
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
@ -50,13 +50,14 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView Margin="10" Name="lvFiles" Grid.ColumnSpan="7">
<ListView Margin="10" Name="lvFiles" Grid.ColumnSpan="7" Grid.Column="1">
<ListView.View>
<GridView>
<GridViewColumn Header="{StaticResource ND_FILE}" Width="200" DisplayMemberBinding="{Binding Name}" />
@ -65,24 +66,31 @@
</GridView>
</ListView.View>
</ListView>
<Button Name="BtnAdd" Content="{StaticResource Q_ADD}" Margin="10,3,5,3" Padding="5,3" Grid.Row="1" Grid.Column="0"/>
<Button Name="BtnRemove" Content="{StaticResource Q_REMOVE}" Margin="0,3,5,3" Padding="5,3" Grid.Row="1" Grid.Column="1"/>
<Button Name="BtnUp" Content="{StaticResource Q_MOVE_UP}" Margin="0,3,5,3" Padding="5,3" Grid.Row="1" Grid.Column="2"/>
<Button Name="BtnDown" Content="{StaticResource Q_MOVE_DN}" Margin="0,3,5,3" Padding="5,3" Grid.Row="1" Grid.Column="3"/>
<Button Name="BtnMoveTo" Content="{StaticResource Q_MOVE_TO}" Margin="0,3,5,3" Padding="5,3" Grid.Row="1" Grid.Column="4"/>
<Button Name="BtnAdd" Content="{StaticResource Q_ADD}"
Margin="10,3,5,3" Padding="5,3" Grid.Row="1"
Grid.Column="1"
Click="BtnAdd_Click"/>
<Button Name="BtnRemove" Content="{StaticResource Q_REMOVE}" Margin="0,3,5,3" Padding="5,3"
Grid.Row="1" Grid.Column="2" Click="BtnRemove_Click"/>
<Button Name="BtnUp" Content="{StaticResource Q_MOVE_UP}" Margin="0,3,5,3" Padding="5,3"
Grid.Row="1" Grid.Column="3" Click="BtnUp_Click"/>
<Button Name="BtnDown" Content="{StaticResource Q_MOVE_DN}" Margin="0,3,5,3" Padding="5,3"
Grid.Row="1" Grid.Column="4" Click="BtnDown_Click"/>
<Button Name="BtnMoveTo" Content="{StaticResource Q_MOVE_TO}" Margin="0,3,5,3" Padding="5,3"
Grid.Row="1" Grid.Column="5" Click="BtnMoveTo_Click"/>
</Grid>
</TabItem>
<TabItem Header="{StaticResource Q_SCHEDULE_TXT}">
<DockPanel>
<CheckBox Name="ChkEnableScheduler" Content="{StaticResource Q_ENABLE}" DockPanel.Dock="Top" Margin="10"/>
<sc:SchedulerPanel/>
<sc:SchedulerPanel x:Name="SchedulerPanel"/>
</DockPanel>
</TabItem>
</TabControl>
<Button Name="BtnStart" Content="{StaticResource MENU_START_Q}" Grid.Row="1" Grid.Column="0" Margin="5,10,5,5" Padding="5,3"/>
<Button Name="BtnStop" Content="{StaticResource MENU_PAUSE}" Grid.Row="1" Grid.Column="1" Margin="5,10,5,5" Padding="20,3"/>
<Button Name="BtnSave" Content="{StaticResource DESC_SAVE_Q}" Grid.Row="1" Grid.Column="3" Margin="5,10,5,5" Padding="20,3"/>
<Button Name="BtnCancel" Content="{StaticResource ND_CANCEL}" Grid.Row="1" Grid.Column="4" Margin="5,10,5,5" Padding="15,3"/>
<Button Name="BtnStart" Content="{StaticResource MENU_START_Q}" Grid.Row="1" Grid.Column="0" Margin="5,10,5,5" Padding="5,3" Click="BtnStart_Click"/>
<Button Name="BtnStop" Content="{StaticResource MENU_PAUSE}" Grid.Row="1" Grid.Column="1" Margin="5,10,5,5" Padding="20,3" Click="BtnStop_Click"/>
<Button Name="BtnSave" Content="{StaticResource DESC_SAVE_Q}" Grid.Row="1" Grid.Column="3" Margin="5,10,5,5" Padding="20,3" Click="BtnSave_Click"/>
<Button Name="BtnCancel" Content="{StaticResource ND_CANCEL}" Grid.Row="1" Grid.Column="4" Margin="5,10,5,5" Padding="15,3" Click="BtnCancel_Click"/>
</Grid>
</DockPanel>
</Window>

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
@ -14,6 +15,7 @@ using XDM.Core.Lib.Common;
using XDM.Core.Lib.UI;
using XDM.Wpf.UI.Common;
using XDM.Wpf.UI.Win32;
using XDMApp;
namespace XDM.Wpf.UI.Dialogs.QueuesWindow
{
@ -30,10 +32,59 @@ namespace XDM.Wpf.UI.Dialogs.QueuesWindow
public event EventHandler<DownloadListEventArgs>? QueueStopRequested;
public event EventHandler? WindowClosing;
private readonly ObservableCollection<DownloadQueue> queues = new();
private readonly ObservableCollection<InProgressDownloadEntry> downloads = new();
public ManageQueueDialog(IAppUI appUI)
{
InitializeComponent();
this.appUI = appUI;
this.defaultSchedule = new DownloadSchedule
{
StartTime = DateTime.Now.TimeOfDay,
EndTime = DateTime.Now.Date.AddHours(23).AddMinutes(59).TimeOfDay
};
this.SchedulerPanel.Schedule = defaultSchedule;
this.SchedulerPanel.ValueChanged += (_, _) =>
{
if (ChkEnableScheduler.IsChecked.HasValue && ChkEnableScheduler.IsChecked.Value)
{
if (LbQueues.SelectedItem is DownloadQueue queue)
{
queue.Schedule = this.SchedulerPanel.Schedule;
}
}
};
this.LbQueues.ItemsSource = queues;
this.lvFiles.ItemsSource = downloads;
this.lvFiles.SelectionChanged += (_, _) => ListSelectionChanged();
this.ChkEnableScheduler.Checked += (_, _) =>
{
SchedulerPanel.IsEnabled = true;
if (LbQueues.SelectedItem is DownloadQueue queue)
{
queue.Schedule = this.SchedulerPanel.Schedule;
}
};
this.ChkEnableScheduler.Unchecked += (_, _) =>
{
SchedulerPanel.IsEnabled = false;
if (LbQueues.SelectedItem is DownloadQueue queue)
{
queue.Schedule = null;
}
};
Closing += (_, _) =>
this.WindowClosing?.Invoke(this, EventArgs.Empty);
}
protected override void OnSourceInitialized(EventArgs e)
@ -46,17 +97,46 @@ namespace XDM.Wpf.UI.Dialogs.QueuesWindow
private void BtnNew_Click(object sender, RoutedEventArgs e)
{
new NewQueueWindow(this.appUI, (a, b) => { }, null) { Owner = this }.ShowDialog(this);
new NewQueueWindow(this.appUI, (queue, newQueue) =>
{
this.queues.Add(queue);
}, null)
{ Owner = this }.ShowDialog(this);
}
public void RefreshView()
{
//throw new NotImplementedException();
if (LbQueues.SelectedItem is DownloadQueue queue)
{
ClearCollection<InProgressDownloadEntry>(this.downloads);
var realQueue = QueueManager.GetQueue(queue.ID);
if (realQueue != null)
{
foreach (var id in realQueue.DownloadIds)
{
var entry = appUI.GetInProgressDownloadEntry(id);
if (entry != null)
{
this.downloads.Add(entry);
}
}
}
}
}
private void ListSelectionChanged()
{
BtnAdd.IsEnabled = BtnRemove.IsEnabled = BtnUp.IsEnabled = BtnDown.IsEnabled = lvFiles.SelectedItems.Count > 0;
}
public void SetData(IEnumerable<DownloadQueue> queues)
{
//throw new NotImplementedException();
ClearCollection(this.queues);
foreach (var item in queues)
{
this.queues.Add(item);
}
}
public void ShowWindow(object peer)
@ -64,5 +144,199 @@ namespace XDM.Wpf.UI.Dialogs.QueuesWindow
this.Owner = (Window)peer;
NativeMethods.ShowDialog(this, (Window)peer);
}
private void LoadQueueDetails(DownloadQueue queue)
{
lvFiles.UnselectAll();
ClearCollection(this.downloads);
foreach (var id in queue.DownloadIds)
{
var ent = appUI.GetInProgressDownloadEntry(id);
if (ent != null)
{
this.downloads.Add(ent);
}
}
lvFiles.ItemsSource = this.downloads;
ChkEnableScheduler.IsChecked = queue.Schedule.HasValue;
if (queue.Schedule.HasValue)
{
this.SchedulerPanel.Schedule = queue.Schedule.Value;
}
else
{
this.SchedulerPanel.Schedule = this.defaultSchedule;
}
this.SchedulerPanel.Schedule = queue.Schedule ?? default;
}
private void EnableControls(bool enable)
{
this.Tab.IsEnabled = enable;
this.BtnNew.IsEnabled = this.BtnDel.IsEnabled = this.BtnStart.IsEnabled = this.BtnStop.IsEnabled = enable;
}
private void UpdateControls(DownloadQueue? queue)
{
if (queue != null)
{
LoadQueueDetails(queue);
EnableControls(true);
}
else
{
this.SchedulerPanel.Schedule = defaultSchedule;
EnableControls(false);
}
}
private void ClearCollection<T>(ObservableCollection<T> collection)
{
for (int i = collection.Count - 1; i >= 0; i--)
{
collection.RemoveAt(i);
}
}
private void BtnSave_Click(object sender, RoutedEventArgs e)
{
SaveQueues();
Close();
}
private void SaveQueues()
{
QueuesModified?.Invoke(this, new QueueListEventArgs(this.queues.Select(x => x).ToList()));
}
private void BtnStart_Click(object sender, RoutedEventArgs e)
{
SaveQueues();
var queue = (DownloadQueue)LbQueues.SelectedItem;
QueueStartRequested?.Invoke(this, new DownloadListEventArgs(queue.DownloadIds));
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
SaveQueues();
var queue = (DownloadQueue)LbQueues.SelectedItem;
QueueStopRequested?.Invoke(this, new DownloadListEventArgs(queue.DownloadIds));
}
private void BtnDel_Click(object sender, RoutedEventArgs e)
{
if (LbQueues.SelectedIndex >= 0)
{
this.queues.RemoveAt(LbQueues.SelectedIndex);
}
}
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void BtnAdd_Click(object sender, RoutedEventArgs e)
{
if (LbQueues.SelectedIndex < 0) return;
var newQueueDialog = new NewQueueWindow(appUI, (queue, newQueue) =>
{
LoadQueueDetails(queue);
}, (DownloadQueue)LbQueues.SelectedItem)
{ Owner = this }.ShowDialog(this);
}
private void BtnRemove_Click(object sender, RoutedEventArgs e)
{
var selectedQueue = (DownloadQueue)LbQueues.SelectedItem;
if (selectedQueue == null) return;
foreach (InProgressDownloadEntry entry in this.downloads)
{
selectedQueue.DownloadIds.Remove(entry.Id);
}
LoadQueueDetails(selectedQueue);
}
private void BtnUp_Click(object sender, RoutedEventArgs e)
{
//if (listView1.SelectedIndices.Count > 0 && listView1.SelectedIndices[0] > 0)
//{
// var lvi = listView1.Items[listView1.SelectedIndices[0] - 1];
// listView1.Items.Remove(lvi);
// listView1.Items.Insert(listView1.SelectedIndices[listView1.SelectedIndices.Count - 1] + 1, lvi);
//}
if (lvFiles.SelectedItems.Count > 0 && lvFiles.SelectedIndex > 0)
{
var index1 = lvFiles.SelectedIndex - 1;
var index2 = lvFiles.SelectedIndex + lvFiles.SelectedItems.Count;
var value = this.downloads[index1];
this.downloads.RemoveAt(index1);
this.downloads.Insert(index2, value);
}
}
private void BtnDown_Click(object sender, RoutedEventArgs e)
{
if (lvFiles.SelectedItems.Count > 0 && lvFiles.SelectedItems.Count + lvFiles.SelectedIndex < this.downloads.Count)
{
var item = this.downloads[lvFiles.SelectedItems.Count + lvFiles.SelectedIndex];
this.downloads.RemoveAt(lvFiles.SelectedItems.Count + lvFiles.SelectedIndex);
this.downloads.Insert(lvFiles.SelectedIndex, item);
}
//if (listView1.SelectedIndices.Count > 0 && listView1.SelectedIndices[listView1.SelectedIndices.Count - 1] < listView1.Items.Count - 1)
//{
// var lvi = listView1.Items[listView1.SelectedIndices[listView1.SelectedIndices.Count - 1] + 1];
// listView1.Items.Remove(lvi);
// listView1.Items.Insert(listView1.SelectedIndices[0], lvi);
//}
}
private void BtnMoveTo_Click(object sender, RoutedEventArgs e)
{
if (lvFiles.SelectedItems.Count > 0 && queues.Count > 1)
{
var qsd = new QueueSelectionWindow() { Owner = this };
var queues1 = new List<string>(this.queues.Count);
var selectedItems = LbQueues.SelectedItems;
foreach (DownloadQueue item in queues)
{
var found = false;
foreach (var si in selectedItems)
{
if (si == item)
{
found = true;
break;
}
}
if (!found)
{
queues1.Add(item.Name);
}
}
var downloadIds = new string[this.lvFiles.SelectedItems.Count];
var index = 0;
foreach (InProgressDownloadEntry lvi in this.lvFiles.SelectedItems)
{
downloadIds[index++] = lvi.Id;
}
qsd.SetData(queues1, downloadIds);
qsd.QueueSelected += QueueSelectionDialog_QueueSelected;
qsd.ShowDialog(this);
}
}
private void QueueSelectionDialog_QueueSelected(object sender, QueueSelectionEventArgs e)
{
var queue = (DownloadQueue)queues[e.SelectedQueueIndex];
var downloadIds = e.DownloadIds;
var selectedQueue = (DownloadQueue)LbQueues.SelectedItem;
foreach (var id in downloadIds)
{
selectedQueue.DownloadIds.Remove(id);
queue.DownloadIds.Add(id);
}
LoadQueueDetails(selectedQueue);
}
}
}

View File

@ -48,7 +48,7 @@ namespace XDM.Wpf.UI.Dialogs.QueuesWindow
}
}
var list = new List<InProgressDownloadEntryWrapper>();
var list = new ObservableCollection<InProgressDownloadEntryWrapper>();
foreach (var ent in ui.GetAllInProgressDownloads())
{
if (!set.Contains(ent.Id))
@ -57,16 +57,16 @@ namespace XDM.Wpf.UI.Dialogs.QueuesWindow
}
}
this.list = new ObservableCollection<InProgressDownloadEntryWrapper>(list);
this.list = list;
lvDownloads.ItemsSource = this.list;
}
private void OnApproved()
private bool OnApproved()
{
if (string.IsNullOrEmpty(TxtQueueName.Text))
{
MessageBox.Show(this, TextResource.GetText("MSG_QUEUE_NAME_MISSING"));
return;
return false;
}
var list2 = new List<string>(this.list.Count);
foreach (var entry in list)
@ -82,6 +82,7 @@ namespace XDM.Wpf.UI.Dialogs.QueuesWindow
modifyingQueue.DownloadIds.AddRange(list2);
okAction.Invoke(modifyingQueue, false);
}
return true;
}
protected override void OnSourceInitialized(EventArgs e)
@ -94,8 +95,10 @@ namespace XDM.Wpf.UI.Dialogs.QueuesWindow
private void BtnOK_Click(object sender, RoutedEventArgs e)
{
OnApproved();
Close();
if (OnApproved())
{
Close();
}
}
private void BtnCancel_Click(object sender, RoutedEventArgs e)

View File

@ -0,0 +1,27 @@
<Window x:Class="XDM.Wpf.UI.Dialogs.QueuesWindow.QueueSelectionWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:XDM.Wpf.UI.Dialogs.QueuesWindow"
mc:Ignorable="d"
Title="{StaticResource LBL_QUEUE_OPT1}"
Height="300" Width="400" ShowInTaskbar="False" WindowStartupLocation="CenterOwner">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{StaticResource Q_MOVE_TO}" Grid.ColumnSpan="3" Margin="0,0,0,10"/>
<ListBox Name="LbQueues" Grid.Row="1" Grid.ColumnSpan="3" Margin="0,0,0,10"></ListBox>
<Button Name="BtnOK" Content="{StaticResource MSG_OK}" Grid.Row="2" Grid.Column="1" Margin="0,0,10,0" Padding="15,3" Click="BtnOK_Click"/>
<Button Name="BtnCancel" Content="{StaticResource ND_CANCEL}" Grid.Row="2" Grid.Column="2" Padding="5,3" Click="BtnCancel_Click"/>
</Grid>
</Window>

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Windows;
using XDM.Core.Lib.UI;
using XDM.Wpf.UI.Common;
using XDM.Wpf.UI.Win32;
namespace XDM.Wpf.UI.Dialogs.QueuesWindow
{
/// <summary>
/// Interaction logic for QueueSelectionWindow.xaml
/// </summary>
public partial class QueueSelectionWindow : Window, IDialog, IQueueSelectionDialog
{
public event EventHandler<QueueSelectionEventArgs>? QueueSelected;
public event EventHandler? ManageQueuesClicked;
private string[] downloadIds = new string[0];
public bool Result { get; set; } = false;
public QueueSelectionWindow()
{
InitializeComponent();
}
public void SetData(IEnumerable<string> items, string[] downloadIds)
{
this.downloadIds = downloadIds;
this.LbQueues.ItemsSource = items;
LbQueues.SelectedIndex = 0;
}
public void ShowWindow(IAppWinPeer peer)
{
NativeMethods.ShowDialog(this, (Window)peer);
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
NativeMethods.DisableMinMaxButton(this);
}
private void BtnOK_Click(object sender, RoutedEventArgs e)
{
QueueSelected?.Invoke(this, new QueueSelectionEventArgs(LbQueues.SelectedIndex, downloadIds));
QueueSelected = null;
Close();
}
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
QueueSelected = null;
Close();
}
}
}

View File

@ -1,4 +1,6 @@
using System.Windows.Controls;
using System;
using System.Windows.Controls;
using XDM.Core.Lib.Common;
namespace XDM.Wpf.UI.Dialogs.Scheduler
{
@ -7,9 +9,101 @@ namespace XDM.Wpf.UI.Dialogs.Scheduler
/// </summary>
public partial class SchedulerPanel : UserControl
{
public event EventHandler? ValueChanged;
private byte[] bits;
private readonly CheckBox[] checkboxes;
public SchedulerPanel()
{
InitializeComponent();
bits = new byte[]
{
0, 1, 2, 4, 8, 16, 32, 64
};
checkboxes = new CheckBox[] { chkSun, chkMon, chkTue, chkWed, chkThu, chkFri, chkSat };
foreach (var chk in checkboxes)
{
chk.Unchecked += (a, b) =>
{
chkEveryday.IsChecked = false;
this.ValueChanged?.Invoke(this, EventArgs.Empty);
};
chk.Checked += (a, b) =>
{
this.ValueChanged?.Invoke(this, EventArgs.Empty);
};
}
chkEveryday.Checked += (_, _) =>
{
foreach (var chk in checkboxes)
{
chk.IsChecked = true;
}
};
StartTime.ValueChanged += (_, _) => this.ValueChanged?.Invoke(this, EventArgs.Empty);
EndTime.ValueChanged += (_, _) => this.ValueChanged?.Invoke(this, EventArgs.Empty);
}
private void SetDays(WeekDays days)
{
var index = 1;
var allChecked = true;
foreach (var chk in checkboxes)
{
var day = (WeekDays)bits[index];
if (((byte)days & (byte)day) == (byte)day)
{
chk.IsChecked = true;
}
else
{
allChecked = false;
}
index++;
}
chkEveryday.IsChecked = allChecked;
}
private WeekDays GetDaysOfWeek()
{
var index = 1;
var weekdays = WeekDays.None;
foreach (var chk in checkboxes)
{
if (chk.IsChecked.HasValue && chk.IsChecked.Value)
{
weekdays |= (WeekDays)bits[index];
}
index++;
}
return weekdays;
}
public DownloadSchedule Schedule
{
get
{
return new DownloadSchedule
{
StartTime = StartTime.Time,
EndTime = EndTime.Time,
Days = GetDaysOfWeek()
};
}
set
{
SetDays(value.Days);
StartTime.Time = value.StartTime;
EndTime.Time = value.EndTime;
}
}
}
}

View File

@ -9,6 +9,7 @@ namespace XDM.Wpf.UI.Dialogs.Scheduler
/// </summary>
public partial class TimePicker : UserControl
{
public event EventHandler? ValueChanged;
public TimePicker()
{
InitializeComponent();
@ -33,6 +34,10 @@ namespace XDM.Wpf.UI.Dialogs.Scheduler
CmbMinute.SelectedIndex = 0;
CmbAmPm.SelectedIndex = 0;
CmbHour.SelectionChanged += (_, _) => this.ValueChanged?.Invoke(this, EventArgs.Empty);
CmbMinute.SelectionChanged += (_, _) => this.ValueChanged?.Invoke(this, EventArgs.Empty);
CmbAmPm.SelectionChanged += (_, _) => this.ValueChanged?.Invoke(this, EventArgs.Empty);
}
public TimeSpan Time