Migrate GUI to CommunityToolkit.Mvvm.

This commit is contained in:
2025-08-20 21:19:43 +01:00
parent f5414ff23d
commit 13ea0d299b
41 changed files with 1519 additions and 3388 deletions

View File

@@ -34,22 +34,24 @@ using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reactive;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Input;
using Aaru.Gui.Models;
using Aaru.Gui.Views.Dialogs;
using Aaru.Localization;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using JetBrains.Annotations;
using ReactiveUI;
namespace Aaru.Gui.ViewModels.Dialogs;
public sealed class AboutViewModel : ViewModelBase
public sealed partial class AboutViewModel : ViewModelBase
{
readonly About _view;
string _versionText;
[ObservableProperty]
string _versionText;
public AboutViewModel(About view)
{
@@ -59,13 +61,13 @@ public sealed class AboutViewModel : ViewModelBase
(Attribute.GetCustomAttribute(typeof(App).Assembly, typeof(AssemblyInformationalVersionAttribute)) as
AssemblyInformationalVersionAttribute)?.InformationalVersion;
WebsiteCommand = ReactiveCommand.Create(ExecuteWebsiteCommand);
LicenseCommand = ReactiveCommand.Create(ExecuteLicenseCommand);
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
WebsiteCommand = new RelayCommand(OpenWebsite);
LicenseCommand = new AsyncRelayCommand(LicenseAsync);
CloseCommand = new RelayCommand(Close);
Assemblies = [];
Task.Run(() =>
_ = Task.Run(() =>
{
foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies().OrderBy(a => a.FullName))
{
@@ -125,18 +127,12 @@ public sealed class AboutViewModel : ViewModelBase
[NotNull]
public string Authors => UI.Text_Authors;
public ReactiveCommand<Unit, Unit> WebsiteCommand { get; }
public ReactiveCommand<Unit, Unit> LicenseCommand { get; }
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
public ICommand WebsiteCommand { get; }
public ICommand LicenseCommand { get; }
public ICommand CloseCommand { get; }
public ObservableCollection<AssemblyModel> Assemblies { get; }
public string VersionText
{
get => _versionText;
set => this.RaiseAndSetIfChanged(ref _versionText, value);
}
static void ExecuteWebsiteCommand()
static void OpenWebsite()
{
var process = new Process
{
@@ -163,12 +159,13 @@ public sealed class AboutViewModel : ViewModelBase
process.Start();
}
void ExecuteLicenseCommand()
Task LicenseAsync()
{
var dialog = new LicenseDialog();
dialog.DataContext = new LicenseViewModel(dialog);
dialog.ShowDialog(_view);
return dialog.ShowDialog(_view);
}
void ExecuteCloseCommand() => _view.Close();
void Close() => _view.Close();
}

View File

@@ -34,17 +34,17 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Reactive;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Input;
using Aaru.CommonTypes.Interop;
using Aaru.Localization;
using Aaru.Logging;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.Input;
using JetBrains.Annotations;
using MsBox.Avalonia;
using MsBox.Avalonia.Enums;
using ReactiveUI;
using Console = Aaru.Gui.Views.Dialogs.Console;
using PlatformID = Aaru.CommonTypes.Interop.PlatformID;
using Version = Aaru.CommonTypes.Interop.Version;
@@ -59,15 +59,15 @@ public sealed class ConsoleViewModel : ViewModelBase
public ConsoleViewModel(Console view)
{
_view = view;
SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand);
ClearCommand = ReactiveCommand.Create(ExecuteClearCommand);
SaveCommand = new AsyncRelayCommand(SaveAsync);
ClearCommand = new RelayCommand(Clear);
}
[NotNull]
public string Title => UI.Title_Console;
public ReactiveCommand<Unit, Unit> ClearCommand { get; }
public ReactiveCommand<Unit, Task> SaveCommand { get; }
public ICommand ClearCommand { get; }
public ICommand SaveCommand { get; }
public ObservableCollection<LogEntry> Entries => ConsoleHandler.Entries;
[NotNull]
@@ -90,11 +90,11 @@ public sealed class ConsoleViewModel : ViewModelBase
set
{
ConsoleHandler.Debug = value;
this.RaiseAndSetIfChanged(ref _debugChecked, value);
SetProperty(ref _debugChecked, value);
}
}
async Task ExecuteSaveCommand()
async Task SaveAsync()
{
IStorageFile result = await _view.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
{
@@ -165,11 +165,11 @@ public sealed class ConsoleViewModel : ViewModelBase
Icon.Error)
.ShowWindowDialogAsync(_view);
AaruLogging.Exception(exception, UI
.Exception_0_trying_to_save_logfile_details_has_been_sent_to_console,
AaruLogging.Exception(exception,
UI.Exception_0_trying_to_save_logfile_details_has_been_sent_to_console,
exception.Message);
}
}
static void ExecuteClearCommand() => ConsoleHandler.Entries.Clear();
static void Clear() => ConsoleHandler.Entries.Clear();
}

View File

@@ -32,14 +32,14 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Aaru.Gui.Models;
using Aaru.Gui.Views.Dialogs;
using Aaru.Localization;
using CommunityToolkit.Mvvm.Input;
using JetBrains.Annotations;
using ReactiveUI;
namespace Aaru.Gui.ViewModels.Dialogs;
@@ -51,9 +51,9 @@ public sealed class EncodingsViewModel : ViewModelBase
{
_view = view;
Encodings = [];
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
CloseCommand = new RelayCommand(Close);
Task.Run(() =>
_ = Task.Run(() =>
{
var encodings = Encoding.GetEncodings()
.Select(info => new EncodingModel
@@ -83,8 +83,8 @@ public sealed class EncodingsViewModel : ViewModelBase
public string CodeLabel => UI.Title_Code_for_encoding;
public string NameLabel => UI.Title_Name;
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
public ICommand CloseCommand { get; }
public ObservableCollection<EncodingModel> Encodings { get; }
void ExecuteCloseCommand() => _view.Close();
void Close() => _view.Close();
}

View File

@@ -31,12 +31,12 @@
// ****************************************************************************/
using System.IO;
using System.Reactive;
using System.Reflection;
using System.Windows.Input;
using Aaru.Gui.Views.Dialogs;
using Aaru.Localization;
using CommunityToolkit.Mvvm.Input;
using JetBrains.Annotations;
using ReactiveUI;
namespace Aaru.Gui.ViewModels.Dialogs;
@@ -48,7 +48,7 @@ public sealed class LicenseViewModel : ViewModelBase
public LicenseViewModel(LicenseDialog view)
{
_view = view;
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
CloseCommand = new RelayCommand(Close);
// TODO: Localize
using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Aaru.Gui.LICENSE");
@@ -66,8 +66,8 @@ public sealed class LicenseViewModel : ViewModelBase
[NotNull]
public string CloseLabel => UI.ButtonLabel_Close;
public string LicenseText { get; }
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
public string LicenseText { get; }
public ICommand CloseCommand { get; }
void ExecuteCloseCommand() => _view.Close();
void Close() => _view.Close();
}

View File

@@ -31,15 +31,15 @@
// ****************************************************************************/
using System.Collections.ObjectModel;
using System.Reactive;
using System.Reflection;
using System.Windows.Input;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Interfaces;
using Aaru.Gui.Models;
using Aaru.Gui.Views.Dialogs;
using Aaru.Localization;
using CommunityToolkit.Mvvm.Input;
using JetBrains.Annotations;
using ReactiveUI;
namespace Aaru.Gui.ViewModels.Dialogs;
@@ -58,7 +58,7 @@ public sealed class PluginsViewModel : ViewModelBase
WritableImages = [];
FloppyImages = [];
WritableFloppyImages = [];
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
CloseCommand = new RelayCommand(Close);
// TODO: Takes too much time
foreach(IFilter filter in PluginRegister.Singleton.Filters.Values)
@@ -201,7 +201,7 @@ public sealed class PluginsViewModel : ViewModelBase
public string VersionLabel => UI.Title_Version;
public string AuthorLabel => UI.Title_Author;
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
public ICommand CloseCommand { get; }
public ObservableCollection<PluginModel> Filters { get; }
public ObservableCollection<PluginModel> PartitionSchemes { get; }
public ObservableCollection<PluginModel> Filesystems { get; }
@@ -211,5 +211,5 @@ public sealed class PluginsViewModel : ViewModelBase
public ObservableCollection<PluginModel> FloppyImages { get; }
public ObservableCollection<PluginModel> WritableFloppyImages { get; }
void ExecuteCloseCommand() => _view.Close();
void Close() => _view.Close();
}

View File

@@ -30,33 +30,49 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System.Reactive;
using System.Windows.Input;
using Aaru.Gui.Views.Dialogs;
using Aaru.Localization;
using Aaru.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using JetBrains.Annotations;
using ReactiveUI;
namespace Aaru.Gui.ViewModels.Dialogs;
public sealed class SettingsViewModel : ViewModelBase
public sealed partial class SettingsViewModel : ViewModelBase
{
readonly SettingsDialog _view;
bool _commandStatsChecked;
bool _deviceStatsChecked;
bool _filesystemStatsChecked;
bool _filterStatsChecked;
bool _gdprVisible;
bool _mediaImageStatsChecked;
bool _mediaScanStatsChecked;
bool _mediaStatsChecked;
bool _partitionStatsChecked;
bool _saveReportsGloballyChecked;
bool _saveStatsChecked;
bool _shareReportsChecked;
bool _shareStatsChecked;
int _tabControlSelectedIndex;
bool _verifyStatsChecked;
[ObservableProperty]
bool _commandStatsChecked;
[ObservableProperty]
bool _deviceStatsChecked;
[ObservableProperty]
bool _filesystemStatsChecked;
[ObservableProperty]
bool _filterStatsChecked;
[ObservableProperty]
bool _gdprVisible;
[ObservableProperty]
bool _mediaImageStatsChecked;
[ObservableProperty]
bool _mediaScanStatsChecked;
[ObservableProperty]
bool _mediaStatsChecked;
[ObservableProperty]
bool _partitionStatsChecked;
[ObservableProperty]
bool _saveReportsGloballyChecked;
[ObservableProperty]
bool _saveStatsChecked;
[ObservableProperty]
bool _shareReportsChecked;
[ObservableProperty]
bool _shareStatsChecked;
[ObservableProperty]
int _tabControlSelectedIndex;
[ObservableProperty]
bool _verifyStatsChecked;
public SettingsViewModel(SettingsDialog view, bool gdprChange)
{
@@ -82,8 +98,8 @@ public sealed class SettingsViewModel : ViewModelBase
else
SaveStatsChecked = false;
CancelCommand = ReactiveCommand.Create(ExecuteCancelCommand);
SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand);
CancelCommand = new RelayCommand(Cancel);
SaveCommand = new RelayCommand(Save);
if(!_gdprVisible) _tabControlSelectedIndex = 1;
}
@@ -164,100 +180,10 @@ public sealed class SettingsViewModel : ViewModelBase
[NotNull]
public string VerifyStatsText => UI.Gather_statistics_about_media_image_verifications_Q;
public ReactiveCommand<Unit, Unit> CancelCommand { get; }
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
public ICommand CancelCommand { get; }
public ICommand SaveCommand { get; }
public bool GdprVisible
{
get => _gdprVisible;
set => this.RaiseAndSetIfChanged(ref _gdprVisible, value);
}
public bool SaveReportsGloballyChecked
{
get => _saveReportsGloballyChecked;
set => this.RaiseAndSetIfChanged(ref _saveReportsGloballyChecked, value);
}
public bool ShareReportsChecked
{
get => _shareReportsChecked;
set => this.RaiseAndSetIfChanged(ref _shareReportsChecked, value);
}
public bool SaveStatsChecked
{
get => _saveStatsChecked;
set => this.RaiseAndSetIfChanged(ref _saveStatsChecked, value);
}
public bool ShareStatsChecked
{
get => _shareStatsChecked;
set => this.RaiseAndSetIfChanged(ref _shareStatsChecked, value);
}
public bool CommandStatsChecked
{
get => _commandStatsChecked;
set => this.RaiseAndSetIfChanged(ref _commandStatsChecked, value);
}
public bool DeviceStatsChecked
{
get => _deviceStatsChecked;
set => this.RaiseAndSetIfChanged(ref _deviceStatsChecked, value);
}
public bool FilesystemStatsChecked
{
get => _filesystemStatsChecked;
set => this.RaiseAndSetIfChanged(ref _filesystemStatsChecked, value);
}
public bool FilterStatsChecked
{
get => _filterStatsChecked;
set => this.RaiseAndSetIfChanged(ref _filterStatsChecked, value);
}
public bool MediaImageStatsChecked
{
get => _mediaImageStatsChecked;
set => this.RaiseAndSetIfChanged(ref _mediaImageStatsChecked, value);
}
public bool MediaScanStatsChecked
{
get => _mediaScanStatsChecked;
set => this.RaiseAndSetIfChanged(ref _mediaScanStatsChecked, value);
}
public bool PartitionStatsChecked
{
get => _partitionStatsChecked;
set => this.RaiseAndSetIfChanged(ref _partitionStatsChecked, value);
}
public bool MediaStatsChecked
{
get => _mediaStatsChecked;
set => this.RaiseAndSetIfChanged(ref _mediaStatsChecked, value);
}
public bool VerifyStatsChecked
{
get => _verifyStatsChecked;
set => this.RaiseAndSetIfChanged(ref _verifyStatsChecked, value);
}
public int TabControlSelectedIndex
{
get => _tabControlSelectedIndex;
set => this.RaiseAndSetIfChanged(ref _tabControlSelectedIndex, value);
}
void ExecuteSaveCommand()
void Save()
{
Settings.Settings.Current.SaveReportsGlobally = SaveReportsGloballyChecked;
Settings.Settings.Current.ShareReports = ShareReportsChecked;
@@ -286,5 +212,5 @@ public sealed class SettingsViewModel : ViewModelBase
_view.Close();
}
void ExecuteCancelCommand() => _view.Close();
void Cancel() => _view.Close();
}

View File

@@ -32,14 +32,14 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Windows.Input;
using Aaru.Database;
using Aaru.Database.Models;
using Aaru.Gui.Models;
using Aaru.Gui.Views.Dialogs;
using Aaru.Localization;
using CommunityToolkit.Mvvm.Input;
using JetBrains.Annotations;
using ReactiveUI;
using NameCountModel = Aaru.Gui.Models.NameCountModel;
namespace Aaru.Gui.ViewModels.Dialogs;
@@ -96,7 +96,7 @@ public sealed class StatisticsViewModel : ViewModelBase
Filesystems = [];
Devices = [];
Medias = [];
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
CloseCommand = new RelayCommand(Close);
using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath);
if(ctx.Commands.Any())
@@ -481,235 +481,235 @@ public sealed class StatisticsViewModel : ViewModelBase
public string FsInfoText
{
get => _fsinfoText;
set => this.RaiseAndSetIfChanged(ref _fsinfoText, value);
set => SetProperty(ref _fsinfoText, value);
}
public bool FsInfoVisible
{
get => _fsinfoVisible;
set => this.RaiseAndSetIfChanged(ref _fsinfoVisible, value);
set => SetProperty(ref _fsinfoVisible, value);
}
public string ChecksumText
{
get => _checksumText;
set => this.RaiseAndSetIfChanged(ref _checksumText, value);
set => SetProperty(ref _checksumText, value);
}
public bool ChecksumVisible
{
get => _checksumVisible;
set => this.RaiseAndSetIfChanged(ref _checksumVisible, value);
set => SetProperty(ref _checksumVisible, value);
}
public string CompareText
{
get => _compareText;
set => this.RaiseAndSetIfChanged(ref _compareText, value);
set => SetProperty(ref _compareText, value);
}
public bool CompareVisible
{
get => _compareVisible;
set => this.RaiseAndSetIfChanged(ref _compareVisible, value);
set => SetProperty(ref _compareVisible, value);
}
public string ConvertImageText
{
get => _convertImageText;
set => this.RaiseAndSetIfChanged(ref _convertImageText, value);
set => SetProperty(ref _convertImageText, value);
}
public bool ConvertImageVisible
{
get => _convertImageVisible;
set => this.RaiseAndSetIfChanged(ref _convertImageVisible, value);
set => SetProperty(ref _convertImageVisible, value);
}
public string CreateSidecarText
{
get => _createSidecarText;
set => this.RaiseAndSetIfChanged(ref _createSidecarText, value);
set => SetProperty(ref _createSidecarText, value);
}
public bool CreateSidecarVisible
{
get => _createSidecarVisible;
set => this.RaiseAndSetIfChanged(ref _createSidecarVisible, value);
set => SetProperty(ref _createSidecarVisible, value);
}
public string DecodeText
{
get => _decodeText;
set => this.RaiseAndSetIfChanged(ref _decodeText, value);
set => SetProperty(ref _decodeText, value);
}
public bool DecodeVisible
{
get => _decodeVisible;
set => this.RaiseAndSetIfChanged(ref _decodeVisible, value);
set => SetProperty(ref _decodeVisible, value);
}
public string DeviceInfoText
{
get => _deviceInfoText;
set => this.RaiseAndSetIfChanged(ref _deviceInfoText, value);
set => SetProperty(ref _deviceInfoText, value);
}
public bool DeviceInfoVisible
{
get => _deviceInfoVisible;
set => this.RaiseAndSetIfChanged(ref _deviceInfoVisible, value);
set => SetProperty(ref _deviceInfoVisible, value);
}
public string DeviceReportText
{
get => _deviceReportText;
set => this.RaiseAndSetIfChanged(ref _deviceReportText, value);
set => SetProperty(ref _deviceReportText, value);
}
public bool DeviceReportVisible
{
get => _deviceReportVisible;
set => this.RaiseAndSetIfChanged(ref _deviceReportVisible, value);
set => SetProperty(ref _deviceReportVisible, value);
}
public string DumpMediaText
{
get => _dumpMediaText;
set => this.RaiseAndSetIfChanged(ref _dumpMediaText, value);
set => SetProperty(ref _dumpMediaText, value);
}
public bool DumpMediaVisible
{
get => _dumpMediaVisible;
set => this.RaiseAndSetIfChanged(ref _dumpMediaVisible, value);
set => SetProperty(ref _dumpMediaVisible, value);
}
public string EntropyText
{
get => _entropyText;
set => this.RaiseAndSetIfChanged(ref _entropyText, value);
set => SetProperty(ref _entropyText, value);
}
public bool EntropyVisible
{
get => _entropyVisible;
set => this.RaiseAndSetIfChanged(ref _entropyVisible, value);
set => SetProperty(ref _entropyVisible, value);
}
public string FormatsCommandText
{
get => _formatsText;
set => this.RaiseAndSetIfChanged(ref _formatsText, value);
set => SetProperty(ref _formatsText, value);
}
public bool FormatsCommandVisible
{
get => _formatsCommandVisible;
set => this.RaiseAndSetIfChanged(ref _formatsCommandVisible, value);
set => SetProperty(ref _formatsCommandVisible, value);
}
public string ImageInfoText
{
get => _imageInfoText;
set => this.RaiseAndSetIfChanged(ref _imageInfoText, value);
set => SetProperty(ref _imageInfoText, value);
}
public bool ImageInfoVisible
{
get => _imageInfoVisible;
set => this.RaiseAndSetIfChanged(ref _imageInfoVisible, value);
set => SetProperty(ref _imageInfoVisible, value);
}
public string MediaInfoText
{
get => _mediaInfoText;
set => this.RaiseAndSetIfChanged(ref _mediaInfoText, value);
set => SetProperty(ref _mediaInfoText, value);
}
public bool MediaInfoVisible
{
get => _mediaInfoVisible;
set => this.RaiseAndSetIfChanged(ref _mediaInfoVisible, value);
set => SetProperty(ref _mediaInfoVisible, value);
}
public string MediaScanText
{
get => _mediaScanText;
set => this.RaiseAndSetIfChanged(ref _mediaScanText, value);
set => SetProperty(ref _mediaScanText, value);
}
public bool MediaScanVisible
{
get => _mediaScanVisible;
set => this.RaiseAndSetIfChanged(ref _mediaScanVisible, value);
set => SetProperty(ref _mediaScanVisible, value);
}
public string PrintHexText
{
get => _printHexText;
set => this.RaiseAndSetIfChanged(ref _printHexText, value);
set => SetProperty(ref _printHexText, value);
}
public bool PrintHexVisible
{
get => _printHexVisible;
set => this.RaiseAndSetIfChanged(ref _printHexVisible, value);
set => SetProperty(ref _printHexVisible, value);
}
public string VerifyText
{
get => _verifyText;
set => this.RaiseAndSetIfChanged(ref _verifyText, value);
set => SetProperty(ref _verifyText, value);
}
public bool VerifyVisible
{
get => _verifyVisible;
set => this.RaiseAndSetIfChanged(ref _verifyVisible, value);
set => SetProperty(ref _verifyVisible, value);
}
public bool CommandsVisible
{
get => _commandsVisible;
set => this.RaiseAndSetIfChanged(ref _commandsVisible, value);
set => SetProperty(ref _commandsVisible, value);
}
public bool FiltersVisible
{
get => _filtersVisible;
set => this.RaiseAndSetIfChanged(ref _filtersVisible, value);
set => SetProperty(ref _filtersVisible, value);
}
public bool PartitionsVisible
{
get => _partitionsVisible;
set => this.RaiseAndSetIfChanged(ref _partitionsVisible, value);
set => SetProperty(ref _partitionsVisible, value);
}
public bool FormatsVisible
{
get => _formatsVisible;
set => this.RaiseAndSetIfChanged(ref _formatsVisible, value);
set => SetProperty(ref _formatsVisible, value);
}
public bool FilesystemsVisible
{
get => _filesystemsVisible;
set => this.RaiseAndSetIfChanged(ref _filesystemsVisible, value);
set => SetProperty(ref _filesystemsVisible, value);
}
public bool DevicesVisible
{
get => _devicesVisible;
set => this.RaiseAndSetIfChanged(ref _devicesVisible, value);
set => SetProperty(ref _devicesVisible, value);
}
public bool MediasVisible
{
get => _mediasVisible;
set => this.RaiseAndSetIfChanged(ref _mediasVisible, value);
set => SetProperty(ref _mediasVisible, value);
}
[NotNull]
@@ -772,7 +772,7 @@ public sealed class StatisticsViewModel : ViewModelBase
[NotNull]
public string CloseLabel => UI.ButtonLabel_Close;
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
public ICommand CloseCommand { get; }
public ObservableCollection<NameCountModel> Filters { get; }
public ObservableCollection<NameCountModel> Formats { get; }
public ObservableCollection<NameCountModel> Partitions { get; }
@@ -780,5 +780,5 @@ public sealed class StatisticsViewModel : ViewModelBase
public ObservableCollection<DeviceStatsModel> Devices { get; }
public ObservableCollection<MediaStatsModel> Medias { get; }
void ExecuteCloseCommand() => _view.Close();
void Close() => _view.Close();
}