diff --git a/Files/Files.csproj b/Files/Files.csproj index d7f281d7cebc..889e95416dc8 100644 --- a/Files/Files.csproj +++ b/Files/Files.csproj @@ -220,6 +220,7 @@ + RestartControl.xaml @@ -290,11 +291,14 @@ Bundles.xaml + + WidgetsListControl.xaml + - - - + + + @@ -455,6 +459,9 @@ + + + ColumnShellPage.xaml @@ -916,6 +923,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/Files/Helpers/WidgetsHelpers.cs b/Files/Helpers/WidgetsHelpers.cs new file mode 100644 index 000000000000..7ea36d3fc93c --- /dev/null +++ b/Files/Helpers/WidgetsHelpers.cs @@ -0,0 +1,55 @@ +using Files.UserControls.Widgets; +using Files.ViewModels.Widgets; + +namespace Files.Helpers +{ + public static class WidgetsHelpers + { + public static TWidget TryGetWidget(WidgetsListControlViewModel widgetsViewModel, TWidget defaultValue = default(TWidget)) where TWidget : IWidgetItemModel, new() + { + bool canAddWidget = widgetsViewModel.CanAddWidget(typeof(TWidget).Name); + bool isWidgetSettingEnabled = TryGetIsWidgetSettingEnabled(); + + if (canAddWidget && isWidgetSettingEnabled) + { + return new TWidget(); + } + else if (!canAddWidget && !isWidgetSettingEnabled) // The widgets exists but the setting has been disabled for it + { + // Remove the widget + widgetsViewModel.RemoveWidget(); + return default(TWidget); + } + + return defaultValue; + } + + public static bool TryGetIsWidgetSettingEnabled() where TWidget : IWidgetItemModel + { + if (typeof(TWidget) == typeof(LibraryCards)) + { + return App.AppSettings.ShowLibraryCardsWidget; + } + if (typeof(TWidget) == typeof(DrivesWidget)) + { + return App.AppSettings.ShowDrivesWidget; + } + if (typeof(TWidget) == typeof(Bundles)) + { + return App.AppSettings.ShowBundlesWidget; + } + if (typeof(TWidget) == typeof(RecentFiles)) + { + return App.AppSettings.ShowRecentFilesWidget; + } + // A custom widget it is - TWidget implements ICustomWidgetItemModel + if (typeof(ICustomWidgetItemModel).IsAssignableFrom(typeof(TWidget))) + { + // Return true for custom widgets - they're always enabled + return true; + } + + return false; + } + } +} diff --git a/Files/UserControls/Widgets/Bundles.xaml.cs b/Files/UserControls/Widgets/Bundles.xaml.cs index b77b65d4992c..ce7ee17f5867 100644 --- a/Files/UserControls/Widgets/Bundles.xaml.cs +++ b/Files/UserControls/Widgets/Bundles.xaml.cs @@ -1,12 +1,13 @@ -using Files.ViewModels.Bundles; -using System; +using System; using Windows.UI.Xaml.Controls; +using Files.ViewModels.Widgets.Bundles; +using Files.ViewModels.Widgets; // The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 namespace Files.UserControls.Widgets { - public sealed partial class Bundles : UserControl, IDisposable + public sealed partial class Bundles : UserControl, IWidgetItemModel, IDisposable { public BundlesViewModel ViewModel { @@ -14,6 +15,8 @@ public BundlesViewModel ViewModel private set => DataContext = value; } + public string WidgetName => nameof(Bundles); + public Bundles() { this.InitializeComponent(); diff --git a/Files/UserControls/Widgets/DrivesWidget.xaml.cs b/Files/UserControls/Widgets/DrivesWidget.xaml.cs index 54dac5f60714..bb22eae7cc9e 100644 --- a/Files/UserControls/Widgets/DrivesWidget.xaml.cs +++ b/Files/UserControls/Widgets/DrivesWidget.xaml.cs @@ -2,9 +2,11 @@ using Files.Helpers; using Files.Interacts; using Files.ViewModels; +using Files.ViewModels.Widgets; using System; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; @@ -16,7 +18,7 @@ namespace Files.UserControls.Widgets { - public sealed partial class DrivesWidget : UserControl, INotifyPropertyChanged + public sealed partial class DrivesWidget : UserControl, IWidgetItemModel, INotifyPropertyChanged { public SettingsViewModel AppSettings => App.AppSettings; @@ -47,6 +49,8 @@ public IShellPage AppInstance } } + public string WidgetName => nameof(DrivesWidget); + public DrivesWidget() { InitializeComponent(); @@ -115,19 +119,9 @@ private void GridScaleNormal(object sender, Windows.UI.Xaml.Input.PointerRoutedE visual.Scale = new Vector3(1); } - private bool showMultiPaneControls; - public bool ShowMultiPaneControls { - get => showMultiPaneControls; - set - { - if (value != showMultiPaneControls) - { - showMultiPaneControls = value; - NotifyPropertyChanged(nameof(ShowMultiPaneControls)); - } - } + get => AppInstance.IsMultiPaneEnabled && AppInstance.IsPageMainPane; } private void OpenInNewPane_Click(object sender, RoutedEventArgs e) @@ -175,5 +169,10 @@ private async void GoToStorageSense_Click(object sender, RoutedEventArgs e) { await Launcher.LaunchUriAsync(new Uri("ms-settings:storagesense")); } + + public void Dispose() + { + Debugger.Break(); + } } } \ No newline at end of file diff --git a/Files/UserControls/Widgets/LibraryCards.xaml.cs b/Files/UserControls/Widgets/LibraryCards.xaml.cs index 8dc05eaf0d79..77e26f2a3156 100644 --- a/Files/UserControls/Widgets/LibraryCards.xaml.cs +++ b/Files/UserControls/Widgets/LibraryCards.xaml.cs @@ -5,11 +5,13 @@ using Files.Interacts; using Files.ViewModels; using Files.ViewModels.Dialogs; +using Files.ViewModels.Widgets; using Microsoft.Toolkit.Mvvm.Input; using Microsoft.Toolkit.Uwp; using System; using System.Collections.Specialized; using System.ComponentModel; +using System.Diagnostics; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; @@ -21,7 +23,7 @@ namespace Files.UserControls.Widgets { - public sealed partial class LibraryCards : UserControl, INotifyPropertyChanged + public sealed partial class LibraryCards : UserControl, IWidgetItemModel, INotifyPropertyChanged { public SettingsViewModel AppSettings => App.AppSettings; @@ -41,10 +43,14 @@ public sealed partial class LibraryCards : UserControl, INotifyPropertyChanged public event LibraryCardDeleteInvokedEventHandler LibraryCardDeleteInvoked; + public event EventHandler LibraryCardShowMultiPaneControlsInvoked; + public event PropertyChangedEventHandler PropertyChanged; public BulkConcurrentObservableCollection ItemsAdded = new BulkConcurrentObservableCollection(); + public string WidgetName => nameof(LibraryCards); + public RelayCommand LibraryCardClicked => new RelayCommand(item => { if (string.IsNullOrEmpty(item.Path)) @@ -154,7 +160,12 @@ private void GridScaleNormal(object sender, Windows.UI.Xaml.Input.PointerRoutedE public bool ShowMultiPaneControls { - get => showMultiPaneControls; + get + { + LibraryCardShowMultiPaneControlsInvoked?.Invoke(this, EventArgs.Empty); + + return showMultiPaneControls; + } set { if (value != showMultiPaneControls) @@ -296,6 +307,11 @@ private async void DeleteLibrary_Click(object sender, RoutedEventArgs e) await dialog.ShowAsync(); } } + + public void Dispose() + { + Debugger.Break(); + } } public class LibraryCardInvokedEventArgs : EventArgs diff --git a/Files/UserControls/Widgets/RecentFiles.xaml.cs b/Files/UserControls/Widgets/RecentFiles.xaml.cs index 525c3846f4fb..ba2167aff529 100644 --- a/Files/UserControls/Widgets/RecentFiles.xaml.cs +++ b/Files/UserControls/Widgets/RecentFiles.xaml.cs @@ -1,9 +1,11 @@ using Files.Enums; using Files.Filesystem; using Files.ViewModels; +using Files.ViewModels.Widgets; using System; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -15,7 +17,7 @@ namespace Files.UserControls.Widgets { - public sealed partial class RecentFiles : UserControl + public sealed partial class RecentFiles : UserControl, IWidgetItemModel { public delegate void RecentFilesOpenLocationInvokedEventHandler(object sender, PathNavigationEventArgs e); @@ -29,6 +31,8 @@ public sealed partial class RecentFiles : UserControl private EmptyRecentsText Empty { get; set; } = new EmptyRecentsText(); public SettingsViewModel AppSettings => App.AppSettings; + public string WidgetName => nameof(RecentFiles); + public RecentFiles() { InitializeComponent(); @@ -206,6 +210,11 @@ private void ClearRecentItems_Click(object sender, RoutedEventArgs e) mru.Clear(); Empty.Visibility = Visibility.Visible; } + + public void Dispose() + { + Debugger.Break(); + } } public class RecentItem diff --git a/Files/UserControls/Widgets/WidgetsListControl.xaml b/Files/UserControls/Widgets/WidgetsListControl.xaml new file mode 100644 index 000000000000..5de00be9e2f5 --- /dev/null +++ b/Files/UserControls/Widgets/WidgetsListControl.xaml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/Files/UserControls/Widgets/WidgetsListControl.xaml.cs b/Files/UserControls/Widgets/WidgetsListControl.xaml.cs new file mode 100644 index 000000000000..c473873e858d --- /dev/null +++ b/Files/UserControls/Widgets/WidgetsListControl.xaml.cs @@ -0,0 +1,29 @@ +using Windows.UI.Xaml.Controls; +using Files.ViewModels.Widgets; +using System; + +// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 + +namespace Files.UserControls.Widgets +{ + public sealed partial class WidgetsListControl : UserControl, IDisposable + { + public WidgetsListControlViewModel ViewModel + { + get => (WidgetsListControlViewModel)DataContext; + set => DataContext = value; + } + + public WidgetsListControl() + { + this.InitializeComponent(); + + this.ViewModel = new WidgetsListControlViewModel(); + } + + public void Dispose() + { + ViewModel?.Dispose(); + } + } +} diff --git a/Files/ViewModels/Bundles/BundleContainerViewModel.cs b/Files/ViewModels/Widgets/Bundles/BundleContainerViewModel.cs similarity index 99% rename from Files/ViewModels/Bundles/BundleContainerViewModel.cs rename to Files/ViewModels/Widgets/Bundles/BundleContainerViewModel.cs index 0ef69e02d1d0..db754fa36892 100644 --- a/Files/ViewModels/Bundles/BundleContainerViewModel.cs +++ b/Files/ViewModels/Widgets/Bundles/BundleContainerViewModel.cs @@ -19,7 +19,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -namespace Files.ViewModels.Bundles +namespace Files.ViewModels.Widgets.Bundles { /// /// Bundle's contents view model diff --git a/Files/ViewModels/Bundles/BundleItemViewModel.cs b/Files/ViewModels/Widgets/Bundles/BundleItemViewModel.cs similarity index 99% rename from Files/ViewModels/Bundles/BundleItemViewModel.cs rename to Files/ViewModels/Widgets/Bundles/BundleItemViewModel.cs index d41b457df3c4..65ce873c1167 100644 --- a/Files/ViewModels/Bundles/BundleItemViewModel.cs +++ b/Files/ViewModels/Widgets/Bundles/BundleItemViewModel.cs @@ -16,7 +16,7 @@ using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; -namespace Files.ViewModels.Bundles +namespace Files.ViewModels.Widgets.Bundles { public class BundleItemViewModel : ObservableObject, IDisposable { diff --git a/Files/ViewModels/Bundles/BundlesViewModel.cs b/Files/ViewModels/Widgets/Bundles/BundlesViewModel.cs similarity index 99% rename from Files/ViewModels/Bundles/BundlesViewModel.cs rename to Files/ViewModels/Widgets/Bundles/BundlesViewModel.cs index 0b2e5b671003..2f903e210d70 100644 --- a/Files/ViewModels/Bundles/BundlesViewModel.cs +++ b/Files/ViewModels/Widgets/Bundles/BundlesViewModel.cs @@ -21,7 +21,7 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; -namespace Files.ViewModels.Bundles +namespace Files.ViewModels.Widgets.Bundles { /// /// Bundles list View Model diff --git a/Files/ViewModels/Widgets/ICustomWidgetItemModel.cs b/Files/ViewModels/Widgets/ICustomWidgetItemModel.cs new file mode 100644 index 000000000000..78739fb6f372 --- /dev/null +++ b/Files/ViewModels/Widgets/ICustomWidgetItemModel.cs @@ -0,0 +1,9 @@ +namespace Files.ViewModels.Widgets +{ + /// + /// This interface is used to mark widgets that are not standard to Files i.e. custom widgets + /// + public interface ICustomWidgetItemModel : IWidgetItemModel + { + } +} diff --git a/Files/ViewModels/Widgets/IWidgetItemModel.cs b/Files/ViewModels/Widgets/IWidgetItemModel.cs new file mode 100644 index 000000000000..3a9f58deffdb --- /dev/null +++ b/Files/ViewModels/Widgets/IWidgetItemModel.cs @@ -0,0 +1,9 @@ +using System; + +namespace Files.ViewModels.Widgets +{ + public interface IWidgetItemModel : IDisposable + { + string WidgetName { get; } + } +} diff --git a/Files/ViewModels/Widgets/WidgetsListControlViewModel.cs b/Files/ViewModels/Widgets/WidgetsListControlViewModel.cs new file mode 100644 index 000000000000..45f16854ced4 --- /dev/null +++ b/Files/ViewModels/Widgets/WidgetsListControlViewModel.cs @@ -0,0 +1,95 @@ +using Microsoft.Toolkit.Mvvm.ComponentModel; +using System; +using System.Collections.ObjectModel; +using System.Linq; +using Windows.UI.Xaml.Controls; + +namespace Files.ViewModels.Widgets +{ + public class WidgetsListControlViewModel : ObservableObject, IDisposable + { + public ObservableCollection Widgets { get; private set; } = new ObservableCollection(); + + public bool AddWidget(Control widgetModel) + { + return InsertWidget(widgetModel, Widgets.Count + 1); + } + + public bool InsertWidget(Control widgetModel, int atIndex) + { + // The widget must not be null and must implement IWidgetItemModel + if (!(widgetModel is IWidgetItemModel widgetItemModel)) + { + return false; + } + + // Don't add existing ones! + if (!CanAddWidget(widgetItemModel.WidgetName)) + { + return false; + } + + if (atIndex > Widgets.Count) + { + Widgets.Add(widgetModel); + } + else + { + Widgets.Insert(atIndex, widgetModel); + } + + return true; + } + + public bool CanAddWidget(string widgetName) + { + return !(Widgets.Any((item) => (item as IWidgetItemModel).WidgetName == widgetName)); + } + + public void RemoveWidget(Control widgetModel) + { + int indexToRemove = Widgets.IndexOf(widgetModel); + (Widgets[indexToRemove] as IDisposable)?.Dispose(); + Widgets.RemoveAt(indexToRemove); + } + + public void RemoveWidget() where TWidget : IWidgetItemModel + { + int indexToRemove = -1; + + for (int i = 0; i < Widgets.Count; i++) + { + if (typeof(TWidget).IsAssignableFrom(Widgets[i].GetType())) + { + // Found matching types + indexToRemove = i; + break; + } + } + + if (indexToRemove == -1) + { + return; + } + + RemoveWidget(Widgets[indexToRemove]); + } + + public void ReorderWidget(Control widgetModel, int place) + { + int widgetIndex = Widgets.IndexOf(widgetModel); + Widgets.Move(widgetIndex, place); + } + + public void Dispose() + { + for (int i = 0; i < Widgets.Count; i++) + { + (Widgets[i] as IDisposable)?.Dispose(); + } + + Widgets.Clear(); + Widgets = null; + } + } +} diff --git a/Files/Views/YourHome.xaml b/Files/Views/YourHome.xaml index c3d8753d88ce..5ab5e68c77a1 100644 --- a/Files/Views/YourHome.xaml +++ b/Files/Views/YourHome.xaml @@ -2,7 +2,6 @@ x:Class="Files.Views.YourHome" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:converters="using:Files.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:widgets="using:Files.UserControls.Widgets" @@ -10,26 +9,10 @@ mc:Ignorable="d"> - - - - - - - - - - - - - - + diff --git a/Files/Views/YourHome.xaml.cs b/Files/Views/YourHome.xaml.cs index b475036734b0..97fd3117cf47 100644 --- a/Files/Views/YourHome.xaml.cs +++ b/Files/Views/YourHome.xaml.cs @@ -3,13 +3,11 @@ using Files.Helpers; using Files.UserControls.Widgets; using Files.ViewModels; -using Files.ViewModels.Bundles; using Microsoft.Toolkit.Uwp; using System; using System.IO; using System.Linq; using System.Runtime.InteropServices; -using Windows.ApplicationModel.AppService; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; @@ -22,46 +20,75 @@ public sealed partial class YourHome : Page, IDisposable public FolderSettingsViewModel FolderSettings => AppInstance?.InstanceViewModel.FolderSettings; public NamedPipeAsAppServiceConnection Connection => AppInstance?.ServiceConnection; + LibraryCards libraryCards; + DrivesWidget drivesWidget; + Bundles bundles; + RecentFiles recentFiles; + public YourHome() { InitializeComponent(); this.Loaded += YourHome_Loaded; } - private void YourHome_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) + private async void YourHome_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) { - if (DrivesWidget != null) + libraryCards = WidgetsHelpers.TryGetWidget(Widgets.ViewModel, libraryCards); + drivesWidget = WidgetsHelpers.TryGetWidget(Widgets.ViewModel, drivesWidget); + bundles = WidgetsHelpers.TryGetWidget(Widgets.ViewModel, bundles); + recentFiles = WidgetsHelpers.TryGetWidget(Widgets.ViewModel, recentFiles); + + // Now prepare widgets + if (libraryCards != null) { - DrivesWidget.DrivesWidgetInvoked -= DrivesWidget_DrivesWidgetInvoked; - DrivesWidget.DrivesWidgetNewPaneInvoked -= DrivesWidget_DrivesWidgetNewPaneInvoked; - DrivesWidget.DrivesWidgetInvoked += DrivesWidget_DrivesWidgetInvoked; - DrivesWidget.DrivesWidgetNewPaneInvoked += DrivesWidget_DrivesWidgetNewPaneInvoked; + libraryCards.LibraryCardInvoked -= LibraryWidget_LibraryCardInvoked; + libraryCards.LibraryCardNewPaneInvoked -= LibraryWidget_LibraryCardNewPaneInvoked; + libraryCards.LibraryCardInvoked += LibraryWidget_LibraryCardInvoked; + libraryCards.LibraryCardNewPaneInvoked += LibraryWidget_LibraryCardNewPaneInvoked; + libraryCards.LibraryCardPropertiesInvoked -= LibraryWidget_LibraryCardPropertiesInvoked; + libraryCards.LibraryCardPropertiesInvoked += LibraryWidget_LibraryCardPropertiesInvoked; + libraryCards.LibraryCardDeleteInvoked -= LibraryWidget_LibraryCardDeleteInvoked; + libraryCards.LibraryCardDeleteInvoked += LibraryWidget_LibraryCardDeleteInvoked; + libraryCards.LibraryCardShowMultiPaneControlsInvoked -= LibraryCards_LibraryCardShowMultiPaneControlsInvoked; + libraryCards.LibraryCardShowMultiPaneControlsInvoked += LibraryCards_LibraryCardShowMultiPaneControlsInvoked; + + Widgets.ViewModel.InsertWidget(libraryCards, 0); } - if (LibraryWidget != null) + if (drivesWidget != null) { - LibraryWidget.LibraryCardInvoked -= LibraryWidget_LibraryCardInvoked; - LibraryWidget.LibraryCardNewPaneInvoked -= LibraryWidget_LibraryCardNewPaneInvoked; - LibraryWidget.LibraryCardInvoked += LibraryWidget_LibraryCardInvoked; - LibraryWidget.LibraryCardNewPaneInvoked += LibraryWidget_LibraryCardNewPaneInvoked; - LibraryWidget.LibraryCardPropertiesInvoked -= LibraryWidget_LibraryCardPropertiesInvoked; - LibraryWidget.LibraryCardPropertiesInvoked += LibraryWidget_LibraryCardPropertiesInvoked; - LibraryWidget.LibraryCardDeleteInvoked -= LibraryWidget_LibraryCardDeleteInvoked; - LibraryWidget.LibraryCardDeleteInvoked += LibraryWidget_LibraryCardDeleteInvoked; + drivesWidget.AppInstance = AppInstance; + drivesWidget.DrivesWidgetInvoked -= DrivesWidget_DrivesWidgetInvoked; + drivesWidget.DrivesWidgetNewPaneInvoked -= DrivesWidget_DrivesWidgetNewPaneInvoked; + drivesWidget.DrivesWidgetInvoked += DrivesWidget_DrivesWidgetInvoked; + drivesWidget.DrivesWidgetNewPaneInvoked += DrivesWidget_DrivesWidgetNewPaneInvoked; + + Widgets.ViewModel.InsertWidget(drivesWidget, 1); } - if (RecentFilesWidget != null) + if (bundles != null) { - RecentFilesWidget.RecentFilesOpenLocationInvoked -= RecentFilesWidget_RecentFilesOpenLocationInvoked; - RecentFilesWidget.RecentFileInvoked -= RecentFilesWidget_RecentFileInvoked; - RecentFilesWidget.RecentFilesOpenLocationInvoked += RecentFilesWidget_RecentFilesOpenLocationInvoked; - RecentFilesWidget.RecentFileInvoked += RecentFilesWidget_RecentFileInvoked; + Widgets.ViewModel.InsertWidget(bundles, 2); + + bundles.ViewModel.Initialize(AppInstance); + await bundles.ViewModel.Load(); } - if (BundlesWidget != null) + if (recentFiles != null) { - (BundlesWidget?.DataContext as BundlesViewModel)?.Initialize(AppInstance); - (BundlesWidget?.DataContext as BundlesViewModel)?.Load(); + recentFiles.RecentFilesOpenLocationInvoked -= RecentFilesWidget_RecentFilesOpenLocationInvoked; + recentFiles.RecentFileInvoked -= RecentFilesWidget_RecentFileInvoked; + recentFiles.RecentFilesOpenLocationInvoked += RecentFilesWidget_RecentFilesOpenLocationInvoked; + recentFiles.RecentFileInvoked += RecentFilesWidget_RecentFileInvoked; + + Widgets.ViewModel.InsertWidget(recentFiles, 3); } } + private void LibraryCards_LibraryCardShowMultiPaneControlsInvoked(object sender, EventArgs e) + { + LibraryCards libraryCards = sender as LibraryCards; + + libraryCards.ShowMultiPaneControls = AppInstance.IsMultiPaneEnabled && AppInstance.IsPageMainPane; + } + private async void RecentFilesWidget_RecentFileInvoked(object sender, UserControls.PathNavigationEventArgs e) { try @@ -188,7 +215,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) // This IDisposable.Dispose() needs to be called to unhook events in BundlesWidget to avoid memory leaks. public void Dispose() { - BundlesWidget?.Dispose(); + Widgets?.Dispose(); } #endregion IDisposable