diff --git a/Accounts/Accounts.csproj b/Accounts/Accounts.csproj index a50de92..966a61f 100644 --- a/Accounts/Accounts.csproj +++ b/Accounts/Accounts.csproj @@ -3,19 +3,19 @@ WinExe - net6.0-windows + net8.0-windows true enable - 0.1.0 - 0.1.0 + 0.2.0 + 0.2.0 Images\Icon.ico Accounts - 0.1.0 + 0.2.0 A basic personal financial-accounting software https://github.com/GaelGirodon/accounts - https://github.com/GaelGirodon/accounts/blob/master/LICENSE + https://github.com/GaelGirodon/accounts/blob/main/LICENSE https://github.com/GaelGirodon/accounts.git git diff --git a/Accounts/App.xaml.cs b/Accounts/App.xaml.cs index 44b8739..db49c92 100644 --- a/Accounts/App.xaml.cs +++ b/Accounts/App.xaml.cs @@ -3,24 +3,23 @@ using System.Windows.Markup; using Accounts.Windows; -namespace Accounts +namespace Accounts; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application { - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application + private void App_OnStartup(object sender, StartupEventArgs e) { - private void App_OnStartup(object sender, StartupEventArgs e) - { - // Set culture for WPF formats - FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), - new FrameworkPropertyMetadata(XmlLanguage - .GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))); - // Start the main window - var window = new MainWindow(); - if (e.Args.Length == 1) // Open with - window.OpenFile(e.Args[0]); - window.Show(); - } + // Set culture for WPF formats + FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), + new FrameworkPropertyMetadata(XmlLanguage + .GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))); + // Start the main window + var window = new MainWindow(); + if (e.Args.Length == 1) // Open with + window.OpenFile(e.Args[0]); + window.Show(); } } diff --git a/Accounts/Controls/AmountArrow.xaml.cs b/Accounts/Controls/AmountArrow.xaml.cs index 4debc30..78a84c8 100644 --- a/Accounts/Controls/AmountArrow.xaml.cs +++ b/Accounts/Controls/AmountArrow.xaml.cs @@ -2,68 +2,65 @@ using System.Windows.Controls; using System.Windows.Media; -namespace Accounts.Controls +namespace Accounts.Controls; + +/// +/// Amount arrow control. +///
+/// Displays a circle with: +/// +///
+public partial class AmountArrow : UserControl { /// - /// Amount arrow control. - ///
- /// Displays a circle with: - /// + /// Amount value. ///
- public partial class AmountArrow : UserControl + public decimal Amount { - /// - /// Amount value. - /// - public decimal Amount - { - get => (decimal) GetValue(AmountProperty); - set => SetValue(AmountProperty, value); - } + get => (decimal)GetValue(AmountProperty); + set => SetValue(AmountProperty, value); + } - /// - /// Amount dependency property (allows data binding and more). - /// - public static readonly DependencyProperty AmountProperty = DependencyProperty - .Register(nameof(Amount), typeof(decimal), typeof(AmountArrow), - new UIPropertyMetadata(0m, AmountPropertyChanged)); + /// + /// Amount dependency property (allows data binding and more). + /// + public static readonly DependencyProperty AmountProperty = DependencyProperty + .Register(nameof(Amount), typeof(decimal), typeof(AmountArrow), + new UIPropertyMetadata(0m, AmountPropertyChanged)); - /// - /// Modify the circle background and the arrow angle - /// depending on the amount value. - /// - private static void AmountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + /// + /// Modify the circle background and the arrow angle + /// depending on the amount value. + /// + private static void AmountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var amount = e.NewValue is decimal value ? value : 0; + if (d is not AmountArrow control) + return; + control.ArrowRotation.Angle = amount switch { - var amount = e.NewValue is decimal value ? value : 0; - if (!(d is AmountArrow control)) - return; - control.ArrowRotation.Angle = amount switch - { - { } a when a >= 0 => -45, - { } a when a <= 0 => 45, - _ => 0 - }; - control.Ellipse.Fill = new SolidColorBrush(amount switch - { - { } a when a >= 0 => Color.FromRgb(68, 189, 50), - { } a when a <= 0 => Color.FromRgb(194, 54, 22), - _ => Colors.Gray - }); - } - - /// - /// Initialize the circle with a gray background - /// and an arrow pointing to the right. - /// - public AmountArrow() + >= 0 => -45, + <= 0 => 45 + }; + control.Ellipse.Fill = new SolidColorBrush(amount switch { - InitializeComponent(); - ArrowRotation.Angle = 0; - Ellipse.Fill = new SolidColorBrush(Colors.Gray); - } + >= 0 => Color.FromRgb(68, 189, 50), + <= 0 => Color.FromRgb(194, 54, 22) + }); + } + + /// + /// Initialize the circle with a gray background + /// and an arrow pointing to the right. + /// + public AmountArrow() + { + InitializeComponent(); + ArrowRotation.Angle = 0; + Ellipse.Fill = new SolidColorBrush(Colors.Gray); } } diff --git a/Accounts/Models/Account.cs b/Accounts/Models/Account.cs index 1c2734d..6a563fb 100644 --- a/Accounts/Models/Account.cs +++ b/Accounts/Models/Account.cs @@ -3,120 +3,119 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Accounts.Models +namespace Accounts.Models; + +/// +/// An account with transactions. +/// +public class Account { /// - /// An account with transactions. + /// Account file extension. /// - public class Account - { - /// - /// Account file extension. - /// - public const string Extension = ".account"; + public const string Extension = ".account"; - /// - /// Account transactions, sorted by date. - /// - public Transactions Transactions { get; set; } + /// + /// Account transactions, sorted by date. + /// + public Transactions Transactions { get; init; } - /// - /// Path to the account file. - /// - [JsonIgnore] - public string? Path { get; set; } + /// + /// Path to the account file. + /// + [JsonIgnore] + public string? Path { get; set; } - /// - /// Initialize an empty account. - /// - public Account() - { - Transactions = new Transactions(); - } + /// + /// Initialize an empty account. + /// + public Account() + { + Transactions = new Transactions(); + } - /// - /// Initialize an account with the path and the - /// transactions list of another account. - /// - /// Account to clone - public Account(Account account) - { - Transactions = account.Transactions; - Path = account.Path; - } + /// + /// Initialize an account with the path and the + /// transactions list of another account. + /// + /// Account to clone + public Account(Account account) + { + Transactions = account.Transactions; + Path = account.Path; + } - #region Serialization + #region Serialization - /// - /// Serialize the current account object to JSON. - /// - /// A JSON string encoded as UTF-8 bytes - public byte[] Serialize() - { - return JsonSerializer.SerializeToUtf8Bytes(this, - new JsonSerializerOptions {WriteIndented = true}); - } + /// + /// Serialize the current account object to JSON. + /// + /// A JSON string encoded as UTF-8 bytes + public byte[] Serialize() + { + return JsonSerializer.SerializeToUtf8Bytes(this, + new JsonSerializerOptions { WriteIndented = true }); + } - /// - /// Deserialize the given JSON data to an account object. - /// - /// A JSON string encoded as UTF-8 bytes - /// The deserialized account - public static Account? Deserialize(byte[] data) - { - return JsonSerializer.Deserialize(data); - } + /// + /// Deserialize the given JSON data to an account object. + /// + /// A JSON string encoded as UTF-8 bytes + /// The deserialized account + public static Account? Deserialize(byte[] data) + { + return JsonSerializer.Deserialize(data); + } - #endregion + #endregion - #region Save & Load + #region Save & Load - /// - /// Save the current account to its path. - /// - /// true if the account has been saved - public bool Save() - { - if (string.IsNullOrEmpty(Path)) - return false; - // Serialize - var data = Serialize(); - // Compress and write - using FileStream file = File.Create(Path); - using var output = new GZipStream(file, CompressionMode.Compress); - output.Write(data, 0, data.Length); - return true; - } + /// + /// Save the current account to its path. + /// + /// true if the account has been saved + public bool Save() + { + if (string.IsNullOrEmpty(Path)) + return false; + // Serialize + var data = Serialize(); + // Compress and write + using var file = File.Create(Path); + using var output = new GZipStream(file, CompressionMode.Compress); + output.Write(data, 0, data.Length); + return true; + } - /// - /// Open an account from the given file path. - /// - /// Path to the account file - /// The opened account - public static Account Load(string path) + /// + /// Open an account from the given file path. + /// + /// Path to the account file + /// The opened account + public static Account Load(string path) + { + // Read and decompress + byte[] data; + using (var file = File.OpenRead(path)) + using (var input = new GZipStream(file, CompressionMode.Decompress)) + using (var output = new MemoryStream()) { - // Read and decompress - byte[] data; - using (var file = File.OpenRead(path)) - using (var input = new GZipStream(file, CompressionMode.Decompress)) - using (var output = new MemoryStream()) - { - input.CopyTo(output); - data = output.ToArray(); - } - - if (data.Length == 0) - throw new InvalidDataException(); - // Deserialize - var account = Deserialize(data); - if (account == null) - throw new InvalidDataException(); - // Prepare account object - account.Path = path; - account.Transactions.Sort(); - return account; + input.CopyTo(output); + data = output.ToArray(); } - #endregion + if (data.Length == 0) + throw new InvalidDataException(); + // Deserialize + var account = Deserialize(data); + if (account == null) + throw new InvalidDataException(); + // Prepare account object + account.Path = path; + account.Transactions.Sort(); + return account; } + + #endregion } diff --git a/Accounts/Models/Transaction.cs b/Accounts/Models/Transaction.cs index 91c546a..4835b7c 100644 --- a/Accounts/Models/Transaction.cs +++ b/Accounts/Models/Transaction.cs @@ -3,91 +3,90 @@ using System.Text.Json.Serialization; using static Accounts.Properties.Resources; -namespace Accounts.Models +namespace Accounts.Models; + +/// +/// An account transaction. +/// +public class Transaction : IComparable { /// - /// An account transaction. + /// Suggested values for the payment method. /// - public class Transaction : IComparable - { - /// - /// Suggested values for the payment method. - /// - public static readonly List Methods = new() - { - Transaction_Method_Card, Transaction_Method_Cheque, - Transaction_Method_Cash, Transaction_Method_Transfer - }; + public static readonly List Methods = + [ + Transaction_Method_Card, Transaction_Method_Cheque, + Transaction_Method_Cash, Transaction_Method_Transfer + ]; - /// - /// Transaction id. - /// - public Guid Id { get; init; } + /// + /// Transaction id. + /// + public Guid Id { get; init; } - /// - /// Transaction name. - /// - public string Name { get; init; } + /// + /// Transaction name. + /// + public string Name { get; init; } - /// - /// Transaction date. - /// - public DateTime Date { get; init; } + /// + /// Transaction date. + /// + public DateTime Date { get; init; } - /// - /// Transaction method. - /// - public string Method { get; init; } + /// + /// Transaction method. + /// + public string Method { get; init; } - /// - /// Transaction amount (positive or negative). - /// - public decimal Amount { get; init; } + /// + /// Transaction amount (positive or negative). + /// + public decimal Amount { get; init; } - /// - /// Transaction amount, formatted. - /// - [JsonIgnore] - public string AmountAsString => $"{Amount:F2}"; + /// + /// Transaction amount, formatted. + /// + [JsonIgnore] + public string AmountAsString => $"{Amount:F2}"; - /// - /// Indicates whether the transaction is checked or not. - /// - public bool IsChecked { get; init; } + /// + /// Indicates whether the transaction is checked or not. + /// + public bool IsChecked { get; init; } - /// - /// Initialize a new transaction with default values. - /// - public Transaction() - { - Id = Guid.NewGuid(); - Name = Transaction_Name_DefaultValue; - Date = DateTime.Today; - Method = string.Empty; - } + /// + /// Initialize a new transaction with default values. + /// + public Transaction() + { + Id = Guid.NewGuid(); + Name = Transaction_Name_DefaultValue; + Date = DateTime.Today; + Method = string.Empty; + } - /// - /// Initialize a new transaction using the given transaction fields values. - /// - /// Other transaction - public Transaction(Transaction transaction) - { - Id = transaction.Id; - Name = transaction.Name; - Date = transaction.Date; - Method = transaction.Method; - Amount = transaction.Amount; - IsChecked = transaction.IsChecked; - } + /// + /// Initialize a new transaction using the given transaction fields values. + /// + /// Other transaction + public Transaction(Transaction transaction) + { + Id = transaction.Id; + Name = transaction.Name; + Date = transaction.Date; + Method = transaction.Method; + Amount = transaction.Amount; + IsChecked = transaction.IsChecked; + } - /// - /// Compare two transactions by date, or if equals, by id. - /// - /// Other transaction to compare with this - public int CompareTo(Transaction? other) - { - var dateComparison = Date.CompareTo(other?.Date); - return dateComparison != 0 ? dateComparison : Id.CompareTo(other?.Id); - } + /// + /// Compare two transactions by date, or if equals, by id. + /// + /// Other transaction to compare with this + public int CompareTo(Transaction? other) + { + var dateComparison = Date.CompareTo(other?.Date); + return dateComparison != 0 ? dateComparison : Id.CompareTo(other?.Id); } } diff --git a/Accounts/Models/Transactions.cs b/Accounts/Models/Transactions.cs index 9a310de..ee714fe 100644 --- a/Accounts/Models/Transactions.cs +++ b/Accounts/Models/Transactions.cs @@ -1,41 +1,40 @@ using System; using System.Collections.Generic; -namespace Accounts.Models +namespace Accounts.Models; + +/// +/// A list of transactions with additional methods. +/// +public class Transactions : List { /// - /// A list of transactions with additional methods. + /// Save the given transaction in the list. /// - public class Transactions : List + /// Transaction to save + public void Save(Transaction transaction) { - /// - /// Save the given transaction in the list. - /// - /// Transaction to save - public void Save(Transaction transaction) - { - // Remove the old transaction - RemoveAll(t => t.Id == transaction.Id); - // Insert at the right position - var nextIndex = FindIndex(t => transaction.CompareTo(t) < 0); - Insert(nextIndex >= 0 ? nextIndex : Count, transaction); - } + // Remove the old transaction + RemoveAll(t => t.Id == transaction.Id); + // Insert at the right position + var nextIndex = FindIndex(t => transaction.CompareTo(t) < 0); + Insert(nextIndex >= 0 ? nextIndex : Count, transaction); + } - /// - /// Copy a list of transactions to a new date. - /// - /// Transactions to copy - /// Date for the new transactions - /// Copied transactions count - public int Duplicate(List transactions, DateTime targetDate) - { - for (var i = 0; i < transactions.Count; i++) - Save(new Transaction(transactions[i]) - { - Id = Guid.NewGuid(), - Date = targetDate.AddSeconds(i) // Keep transactions order - }); - return transactions.Count; - } + /// + /// Copy a list of transactions to a new date. + /// + /// Transactions to copy + /// Date for the new transactions + /// Copied transactions count + public int Duplicate(List transactions, DateTime targetDate) + { + for (var i = 0; i < transactions.Count; i++) + Save(new Transaction(transactions[i]) + { + Id = Guid.NewGuid(), + Date = targetDate.AddSeconds(i) // Keep transactions order + }); + return transactions.Count; } } diff --git a/Accounts/Services/AccountService.cs b/Accounts/Services/AccountService.cs index 7e61c41..6374fcb 100644 --- a/Accounts/Services/AccountService.cs +++ b/Accounts/Services/AccountService.cs @@ -4,82 +4,81 @@ using Accounts.Properties; using Microsoft.Win32; -namespace Accounts.Services +namespace Accounts.Services; + +/// +/// Account file management. +/// +public static class AccountService { /// - /// Account file management. + /// Open an account file. /// - public static class AccountService + /// Optional account file path + /// The account + /// Invalid format + public static Account? Open(string? path = null) { - /// - /// Open an account file. - /// - /// Optional account file path - /// The account - /// Invalid format - public static Account? Open(string? path = null) + if (path == null) // Select file path { - if (path == null) // Select file path + var dialog = new OpenFileDialog { - var dialog = new OpenFileDialog - { - Title = Resources.Account_Open_Dialog_Title, - Filter = $"{Resources.Account_FileType} ({Account.Extension})|*{Account.Extension}" - }; - var result = dialog.ShowDialog(); - if (result == false || !File.Exists(dialog.FileName)) - return null; - path = dialog.FileName; - } - - return Account.Load(path); + Title = Resources.Account_Open_Dialog_Title, + Filter = $"{Resources.Account_FileType} ({Account.Extension})|*{Account.Extension}" + }; + var result = dialog.ShowDialog(); + if (result == false || !File.Exists(dialog.FileName)) + return null; + path = dialog.FileName; } - /// - /// Save an account to a file. - /// - /// Account to save - /// Saved account - public static Account Save(Account account) - { - if (string.IsNullOrEmpty(account.Path)) // Select file path - { - var dialog = new SaveFileDialog - { - Title = Resources.Account_Save_Dialog_Title, - FileName = Resources.Account_DefaultFileName, - DefaultExt = Account.Extension, - Filter = $"{Resources.Account_FileType} ({Account.Extension})|*{Account.Extension}" - }; - var result = dialog.ShowDialog(); - if (result == false || string.IsNullOrWhiteSpace(dialog.FileName)) - return account; - account.Path = dialog.FileName; - } - - account.Save(); - return account; - } + return Account.Load(path); + } - /// - /// Create a backup of an account file. - /// - /// The account to backup - /// The account backup - public static Account? Archive(Account account) + /// + /// Save an account to a file. + /// + /// Account to save + /// Saved account + public static Account Save(Account account) + { + if (string.IsNullOrEmpty(account.Path)) // Select file path { - if (string.IsNullOrEmpty(account.Path)) return null; - var directoryName = Path.GetDirectoryName(account.Path); - if (directoryName == null) return null; - var backup = new Account(account) + var dialog = new SaveFileDialog { - Path = Path.Combine(directoryName, - Path.GetFileNameWithoutExtension(account.Path) + - "-" + DateTime.Now.ToString("yyyyMMddhhmmss") + - Path.GetExtension(account.Path)) + Title = Resources.Account_Save_Dialog_Title, + FileName = Resources.Account_DefaultFileName, + DefaultExt = Account.Extension, + Filter = $"{Resources.Account_FileType} ({Account.Extension})|*{Account.Extension}" }; - backup.Save(); - return backup; + var result = dialog.ShowDialog(); + if (result == false || string.IsNullOrWhiteSpace(dialog.FileName)) + return account; + account.Path = dialog.FileName; } + + account.Save(); + return account; + } + + /// + /// Create a backup of an account file. + /// + /// The account to backup + /// The account backup + public static Account? Archive(Account account) + { + if (string.IsNullOrEmpty(account.Path)) return null; + var directoryName = Path.GetDirectoryName(account.Path); + if (directoryName == null) return null; + var backup = new Account(account) + { + Path = Path.Combine(directoryName, + Path.GetFileNameWithoutExtension(account.Path) + + "-" + DateTime.Now.ToString("yyyyMMddhhmmss") + + Path.GetExtension(account.Path)) + }; + backup.Save(); + return backup; } } diff --git a/Accounts/ViewModels/MainViewModel.cs b/Accounts/ViewModels/MainViewModel.cs index 8195cdb..e491610 100644 --- a/Accounts/ViewModels/MainViewModel.cs +++ b/Accounts/ViewModels/MainViewModel.cs @@ -11,360 +11,359 @@ using Accounts.Models; using Accounts.Properties; -namespace Accounts.ViewModels +namespace Accounts.ViewModels; + +/// +/// View model associated to the main window. +/// +public class MainViewModel : INotifyPropertyChanged { + #region Global + /// - /// View model associated to the main window. + /// Current account file. /// - public class MainViewModel : INotifyPropertyChanged + public Account? Account { - #region Global - - /// - /// Current account file. - /// - public Account? Account + get => _account; + set { - get => _account; - set - { - _account = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(Title)); - OnPropertyChanged(nameof(AccountVisibility)); - OnPropertyChanged(nameof(HomeVisibility)); - OnPropertyChanged(nameof(HasAccount)); - } + _account = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(Title)); + OnPropertyChanged(nameof(AccountVisibility)); + OnPropertyChanged(nameof(HomeVisibility)); + OnPropertyChanged(nameof(HasAccount)); } + } - private Account? _account; + private Account? _account; - /// - /// Reset the view model to a clean state - /// (to be invoked after the account is changed). - /// - public void Clean() - { - DateFilter = DateTime.Today; - SelectedTransaction = null; - Name = string.Empty; - Date = DateTime.Now; - Method = string.Empty; - Amount = string.Empty; - IsChecked = false; - UpdateTransactionsView(); - } + /// + /// Reset the view model to a clean state + /// (to be invoked after the account is changed). + /// + public void Clean() + { + DateFilter = DateTime.Today; + SelectedTransaction = null; + Name = string.Empty; + Date = DateTime.Now; + Method = string.Empty; + Amount = string.Empty; + IsChecked = false; + UpdateTransactionsView(); + } + + /// + /// Window title. + /// + public string Title => (_account?.Path == null + ? string.Empty + : $"{Path.GetFileNameWithoutExtension(_account.Path)} - ") + + Resources.Application_Name; + + /// + /// Visibility of the account panel. + /// + public Visibility AccountVisibility => Account == null ? Visibility.Collapsed : Visibility.Visible; + + /// + /// Visibility of the home panel. + /// + public Visibility HomeVisibility => Account == null ? Visibility.Visible : Visibility.Collapsed; + + /// + /// Indicates whether there is an opened account or not. + /// + public bool HasAccount => Account != null; + + #endregion + + #region Transactions view + + /// + /// Transactions in the account. + /// + private IEnumerable Transactions => Account?.Transactions ?? new List(); - /// - /// Window title. - /// - public string Title => (_account?.Path == null - ? string.Empty - : $"{Path.GetFileNameWithoutExtension(_account.Path)} - ") - + Resources.Application_Name; - - /// - /// Visibility of the account panel. - /// - public Visibility AccountVisibility => Account == null ? Visibility.Collapsed : Visibility.Visible; - - /// - /// Visibility of the home panel. - /// - public Visibility HomeVisibility => Account == null ? Visibility.Visible : Visibility.Collapsed; - - /// - /// Indicates whether there is an opened account or not. - /// - public bool HasAccount => Account != null; - - #endregion - - #region Transactions view - - /// - /// Transactions in the account. - /// - private List Transactions => Account?.Transactions ?? new List(); - - /// - /// Filters transactions by date. - /// - public DateTime DateFilter + /// + /// Filters transactions by date. + /// + public DateTime DateFilter + { + get => _dateFilter; + set { - get => _dateFilter; - set - { - _dateFilter = new DateTime(value.Year, value.Month, 1) - .AddMonths(1).AddTicks(-1); - OnPropertyChanged(); - } + _dateFilter = new DateTime(value.Year, value.Month, 1) + .AddMonths(1).AddTicks(-1); + OnPropertyChanged(); } + } - private DateTime _dateFilter; + private DateTime _dateFilter; - /// - /// Transactions, filtered by date, currently displayed. - /// - public List FilteredTransactions + /// + /// Transactions, filtered by date, currently displayed. + /// + public List FilteredTransactions + { + get => _filteredTransactions; + private set { - get => _filteredTransactions; - set - { - _filteredTransactions = value; - OnPropertyChanged(nameof(FilteredTransactions)); - } + _filteredTransactions = value; + OnPropertyChanged(); } + } - private List _filteredTransactions; + private List _filteredTransactions; - /// - /// Account balance at the selected month and year. - /// - public decimal CurrentBalance + /// + /// Account balance at the selected month and year. + /// + public decimal CurrentBalance + { + get => _currentBalance; + private set { - get => _currentBalance; - private set - { - _currentBalance = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(CurrentBalanceColor)); - } + _currentBalance = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(CurrentBalanceColor)); } + } - private decimal _currentBalance; + private decimal _currentBalance; - /// - /// Background color of the current balance indicator. - /// - public SolidColorBrush CurrentBalanceColor => new(CurrentBalance >= 0 - ? Color.FromRgb(68, 189, 50) - : Color.FromRgb(194, 54, 22)); + /// + /// Background color of the current balance indicator. + /// + public SolidColorBrush CurrentBalanceColor => new(CurrentBalance >= 0 + ? Color.FromRgb(68, 189, 50) + : Color.FromRgb(194, 54, 22)); - /// - /// Sum of the credit transactions for the current displayed month. - /// - public decimal MonthCredit + /// + /// Sum of the credit transactions for the current displayed month. + /// + public decimal MonthCredit + { + get => _monthCredit; + private set { - get => _monthCredit; - private set - { - _monthCredit = value; - OnPropertyChanged(); - } + _monthCredit = value; + OnPropertyChanged(); } + } - private decimal _monthCredit; + private decimal _monthCredit; - /// - /// Sum of the debit transactions for the current displayed month. - /// - public decimal MonthDebit + /// + /// Sum of the debit transactions for the current displayed month. + /// + public decimal MonthDebit + { + get => _monthDebit; + private set { - get => _monthDebit; - private set - { - _monthDebit = value; - OnPropertyChanged(); - } + _monthDebit = value; + OnPropertyChanged(); } + } - private decimal _monthDebit; + private decimal _monthDebit; - /// - /// Filter transactions according to the current date filter. - /// - public void UpdateTransactionsView() - { - FilteredTransactions = Transactions - .Where(t => t.Date.Year == DateFilter.Year && t.Date.Month == DateFilter.Month) - .Select(t => new TransactionViewModel(t)).ToList(); - CurrentBalance = Transactions - .Where(t => t.Date <= DateFilter) - .Aggregate(0m, (sum, t) => sum + t.Amount); - MonthCredit = FilteredTransactions - .Select(vm => vm.Transaction) - .Where(t => t.Amount > 0) - .Aggregate(0m, (sum, t) => sum + t.Amount); - MonthDebit = FilteredTransactions - .Select(vm => vm.Transaction) - .Where(t => t.Amount < 0) - .Aggregate(0m, (sum, t) => sum + t.Amount); - } + /// + /// Filter transactions according to the current date filter. + /// + public void UpdateTransactionsView() + { + FilteredTransactions = Transactions + .Where(t => t.Date.Year == DateFilter.Year && t.Date.Month == DateFilter.Month) + .Select(t => new TransactionViewModel(t)).ToList(); + CurrentBalance = Transactions + .Where(t => t.Date <= DateFilter) + .Aggregate(0m, (sum, t) => sum + t.Amount); + MonthCredit = FilteredTransactions + .Select(vm => vm.Transaction) + .Where(t => t.Amount > 0) + .Aggregate(0m, (sum, t) => sum + t.Amount); + MonthDebit = FilteredTransactions + .Select(vm => vm.Transaction) + .Where(t => t.Amount < 0) + .Aggregate(0m, (sum, t) => sum + t.Amount); + } - /// - /// Transactions checked in the list. - /// - public List SelectedTransactions { get; set; } + /// + /// Transactions checked in the list. + /// + public List SelectedTransactions { get; set; } - /// - /// Selected transaction in the list. - /// - public TransactionViewModel? SelectedTransaction + /// + /// Selected transaction in the list. + /// + public TransactionViewModel? SelectedTransaction + { + get => _selectedTransaction; + set { - get => _selectedTransaction; - set - { - _selectedTransaction = value; - Transaction = value?.Transaction; - OnPropertyChanged(); - } + _selectedTransaction = value; + Transaction = value?.Transaction; + OnPropertyChanged(); } + } - private TransactionViewModel? _selectedTransaction; + private TransactionViewModel? _selectedTransaction; - /// - /// Current transaction. - /// - public Transaction? Transaction + /// + /// Current transaction. + /// + public Transaction? Transaction + { + get => _transaction; + set { - get => _transaction; - set - { - _transaction = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(AddButtonVisibility)); - OnPropertyChanged(nameof(FormVisibility)); - OnPropertyChanged(nameof(SuggestedNames)); - } + _transaction = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(AddButtonVisibility)); + OnPropertyChanged(nameof(FormVisibility)); + OnPropertyChanged(nameof(SuggestedNames)); } + } - private Transaction? _transaction; + private Transaction? _transaction; - #endregion + #endregion - #region Transaction form + #region Transaction form - /// - /// Transaction name. - /// - public string? Name + /// + /// Transaction name. + /// + public string? Name + { + get => _name; + set { - get => _name; - set - { - _name = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(SuggestedNames)); - } + _name = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(SuggestedNames)); } + } - private string? _name; + private string? _name; - /// - /// Suggested values for the name of the transaction. - /// - public List SuggestedNames => Name == null - ? new List() - : Transactions.Select(t => t.Name).Distinct() - .Where(n => n.Contains(Name)).Take(10).ToList(); + /// + /// Suggested values for the name of the transaction. + /// + public List SuggestedNames => Name == null + ? [] + : Transactions.Select(t => t.Name).Distinct() + .Where(n => n.Contains(Name)).Take(10).ToList(); - /// - /// Transaction date. - /// - public DateTime Date + /// + /// Transaction date. + /// + public DateTime Date + { + get => _date; + set { - get => _date; - set - { - _date = value; - OnPropertyChanged(); - } + _date = value; + OnPropertyChanged(); } + } - private DateTime _date; + private DateTime _date; - /// - /// Transaction method. - /// - public string? Method + /// + /// Transaction method. + /// + public string? Method + { + get => _method; + set { - get => _method; - set - { - _method = value; - OnPropertyChanged(); - } + _method = value; + OnPropertyChanged(); } + } - private string? _method; + private string? _method; - /// - /// Suggested values for the payment method. - /// - public static List SuggestedMethods => Transaction.Methods; + /// + /// Suggested values for the payment method. + /// + public static List SuggestedMethods => Transaction.Methods; - /// - /// Transaction amount. - /// - public string Amount + /// + /// Transaction amount. + /// + public string Amount + { + get => _amount; + set { - get => _amount; - set - { - _amount = value; - OnPropertyChanged(); - } + _amount = value; + OnPropertyChanged(); } + } - private string _amount; + private string _amount; - /// - /// Currency symbol of the current culture. - /// - public static string Currency => new RegionInfo(Thread.CurrentThread.CurrentUICulture.LCID).CurrencySymbol; + /// + /// Currency symbol of the current culture. + /// + public static string Currency => new RegionInfo(Thread.CurrentThread.CurrentUICulture.LCID).CurrencySymbol; - /// - /// Transaction checked. - /// - public bool IsChecked + /// + /// Transaction checked. + /// + public bool IsChecked + { + get => _isChecked; + set { - get => _isChecked; - set - { - _isChecked = value; - OnPropertyChanged(); - } + _isChecked = value; + OnPropertyChanged(); } + } - private bool _isChecked; - - /// - /// Visibility of the add transaction button. - /// - public Visibility AddButtonVisibility => Transaction == null ? Visibility.Visible : Visibility.Collapsed; + private bool _isChecked; - /// - /// Visibility of the form panel. - /// - public Visibility FormVisibility => Transaction == null ? Visibility.Collapsed : Visibility.Visible; + /// + /// Visibility of the add transaction button. + /// + public Visibility AddButtonVisibility => Transaction == null ? Visibility.Visible : Visibility.Collapsed; - #endregion + /// + /// Visibility of the form panel. + /// + public Visibility FormVisibility => Transaction == null ? Visibility.Collapsed : Visibility.Visible; - public MainViewModel() - { - DateFilter = DateTime.Today; - _filteredTransactions = new List(); - SelectedTransactions = new List(); - _amount = string.Empty; - } + #endregion - #region INotifyPropertyChanged + public MainViewModel() + { + DateFilter = DateTime.Today; + _filteredTransactions = new List(); + SelectedTransactions = new List(); + _amount = string.Empty; + } - /// - /// Event raised when a property is changed. - /// - public event PropertyChangedEventHandler? PropertyChanged; + #region INotifyPropertyChanged - /// - /// Raise the PropertyChanged event for the given property. - /// - /// Property name - private void OnPropertyChanged([CallerMemberName] string? propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } + /// + /// Event raised when a property is changed. + /// + public event PropertyChangedEventHandler? PropertyChanged; - #endregion + /// + /// Raise the PropertyChanged event for the given property. + /// + /// Property name + private void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + + #endregion } diff --git a/Accounts/ViewModels/TransactionViewModel.cs b/Accounts/ViewModels/TransactionViewModel.cs index 6878725..a692076 100644 --- a/Accounts/ViewModels/TransactionViewModel.cs +++ b/Accounts/ViewModels/TransactionViewModel.cs @@ -3,62 +3,56 @@ using System.Windows.Media; using Accounts.Models; -namespace Accounts.ViewModels +namespace Accounts.ViewModels; + +/// +/// Transaction view model. +/// +public class TransactionViewModel(Transaction transaction) { /// - /// Transaction view model. + /// Transaction date. /// - public class TransactionViewModel - { - /// - /// Transaction date. - /// - public DateTime Date => Transaction.Date; - - /// - /// Transaction name. - /// - public string Name => Transaction.Name; + public DateTime Date => Transaction.Date; - /// - /// Transaction payment method. - /// - public string PaymentMethod => Transaction.Method; + /// + /// Transaction name. + /// + public string Name => Transaction.Name; - /// - /// Formatted transaction amount. - /// - public decimal Amount => Transaction.Amount; + /// + /// Transaction payment method. + /// + public string PaymentMethod => Transaction.Method; - /// - /// Amount color. - /// - public SolidColorBrush AmountColor => new(Transaction.Amount >= 0 - ? Color.FromRgb(68, 189, 50) - : Color.FromRgb(194, 54, 22)); + /// + /// Formatted transaction amount. + /// + public decimal Amount => Transaction.Amount; - /// - /// Checked image visibility. - /// - public Visibility CheckedVisibility => Transaction.IsChecked - ? Visibility.Visible - : Visibility.Collapsed; + /// + /// Amount color. + /// + public SolidColorBrush AmountColor => new(Transaction.Amount >= 0 + ? Color.FromRgb(68, 189, 50) + : Color.FromRgb(194, 54, 22)); - /// - /// Not checked image visibility - /// - public Visibility NotCheckedVisibility => Transaction.IsChecked - ? Visibility.Collapsed - : Visibility.Visible; + /// + /// Checked image visibility. + /// + public Visibility CheckedVisibility => Transaction.IsChecked + ? Visibility.Visible + : Visibility.Collapsed; - /// - /// Original transaction. - /// - public Transaction Transaction { get; } + /// + /// Not checked image visibility + /// + public Visibility NotCheckedVisibility => Transaction.IsChecked + ? Visibility.Collapsed + : Visibility.Visible; - public TransactionViewModel(Transaction transaction) - { - Transaction = transaction; - } - } + /// + /// Original transaction. + /// + public Transaction Transaction { get; } = transaction; } diff --git a/Accounts/Windows/MainWindow.xaml b/Accounts/Windows/MainWindow.xaml index ae60d3e..a2d3309 100644 --- a/Accounts/Windows/MainWindow.xaml +++ b/Accounts/Windows/MainWindow.xaml @@ -195,12 +195,14 @@ Visibility="{Binding NotCheckedVisibility}" RenderOptions.BitmapScalingMode="HighQuality" /> - + - + diff --git a/Accounts/Windows/MainWindow.xaml.cs b/Accounts/Windows/MainWindow.xaml.cs index 47734d4..21e8c25 100644 --- a/Accounts/Windows/MainWindow.xaml.cs +++ b/Accounts/Windows/MainWindow.xaml.cs @@ -1,621 +1,601 @@ using System; -using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; +using System.Windows.Threading; using Accounts.Models; using Accounts.Properties; using Accounts.Services; using Accounts.ViewModels; -namespace Accounts.Windows +namespace Accounts.Windows; + +/// +/// Interaction logic for MainWindow.xaml, +/// the main window of the application. +/// +public partial class MainWindow : Window { - /// - /// Interaction logic for MainWindow.xaml, - /// the main window of the application. - /// - public partial class MainWindow : Window + private readonly MainViewModel _vm; + + public MainWindow() { - private readonly MainViewModel _vm; + _vm = new MainViewModel(); + InitializeComponent(); + _vm.UpdateTransactionsView(); + DataContext = _vm; + } - public MainWindow() - { - _vm = new MainViewModel(); - InitializeComponent(); - _vm.UpdateTransactionsView(); - DataContext = _vm; - } + #region Account file management - #region Account file management + private void NewCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = true; + } - private void NewCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = true; - } + /// + /// Create a new empty account file without saving it. + /// + private void NewCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + if (!AskSave()) + return; + _vm.Account = new Account(); + _vm.Clean(); + } - /// - /// Create a new empty account file without saving it. - /// - private void NewCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + /// + /// Open an account file using the given path. + /// + /// Account file path + public void OpenFile(string path) + { + try { - if (!AskSave()) - return; - _vm.Account = new Account(); + _vm.Account = AccountService.Open(path); _vm.Clean(); } - - /// - /// Open an account file using the given path. - /// - /// Account file path - public void OpenFile(string path) + catch (Exception) { - try - { - _vm.Account = AccountService.Open(path); - _vm.Clean(); - } - catch (Exception) - { - MessageBox.Show(this, - Properties.Resources.Account_Open_InvalidFile, - Properties.Resources.Account_Open_Dialog_Title, - MessageBoxButton.OK, MessageBoxImage.Error); - } + MessageBox.Show(this, + Properties.Resources.Account_Open_InvalidFile, + Properties.Resources.Account_Open_Dialog_Title, + MessageBoxButton.OK, MessageBoxImage.Error); } + } - private void OpenCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = true; - } + private void OpenCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = true; + } - /// - /// Open an account file from the disk. - /// - private void OpenCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + /// + /// Open an account file from the disk. + /// + private void OpenCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + if (!AskSave()) + return; + try { - if (!AskSave()) + var account = AccountService.Open(); + if (account == null) return; - try - { - var account = AccountService.Open(); - if (account == null) - return; - _vm.Account = account; - _vm.Clean(); - } - catch (Exception ex) - { - Console.WriteLine(ex); - MessageBox.Show(this, - Properties.Resources.Account_Open_InvalidFile, - Properties.Resources.Account_Open_Dialog_Title, - MessageBoxButton.OK, MessageBoxImage.Error); - } + _vm.Account = account; + _vm.Clean(); } - - private void SaveCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + catch (Exception ex) { - e.CanExecute = _vm.HasAccount; + Console.WriteLine(ex); + MessageBox.Show(this, + Properties.Resources.Account_Open_InvalidFile, + Properties.Resources.Account_Open_Dialog_Title, + MessageBoxButton.OK, MessageBoxImage.Error); } + } + + private void SaveCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.HasAccount; + } - /// - /// Save an account file to the disk. If the account has never been persisted to the disk, - /// a dialog opens to ask the user for the destination path. - /// - private void SaveCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + /// + /// Save an account file to the disk. If the account has never been persisted to the disk, + /// a dialog opens to ask the user for the destination path. + /// + private void SaveCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + if (_vm.Account == null) + return; + try { - if (_vm.Account == null) - return; - try - { - _vm.Account = AccountService.Save(_vm.Account); - if (!string.IsNullOrEmpty(_vm.Account.Path)) - MessageBox.Show(this, - string.Format(Properties.Resources.Account_Save_Success, _vm.Account.Path), - Properties.Resources.Account_Save_Dialog_Title, - MessageBoxButton.OK, MessageBoxImage.Information); - } - catch (Exception) - { + _vm.Account = AccountService.Save(_vm.Account); + if (!string.IsNullOrEmpty(_vm.Account.Path)) MessageBox.Show(this, - Properties.Resources.Account_Save_Error, + string.Format(Properties.Resources.Account_Save_Success, _vm.Account.Path), Properties.Resources.Account_Save_Dialog_Title, - MessageBoxButton.OK, MessageBoxImage.Error); - } + MessageBoxButton.OK, MessageBoxImage.Information); } - - private void ArchiveCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + catch (Exception) { - e.CanExecute = _vm.HasAccount && !string.IsNullOrEmpty(_vm.Account?.Path); + MessageBox.Show(this, + Properties.Resources.Account_Save_Error, + Properties.Resources.Account_Save_Dialog_Title, + MessageBoxButton.OK, MessageBoxImage.Error); } + } + + private void ArchiveCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.HasAccount && !string.IsNullOrEmpty(_vm.Account?.Path); + } - /// - /// Create a backup of the current account file. - /// - private void ArchiveCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + /// + /// Create a backup of the current account file. + /// + private void ArchiveCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + if (_vm.Account == null || string.IsNullOrEmpty(_vm.Account?.Path)) + return; + try { - if (_vm.Account == null || string.IsNullOrEmpty(_vm.Account?.Path)) - return; - try - { - var backup = AccountService.Archive(_vm.Account); - if (backup != null) - MessageBox.Show(this, - string.Format(Properties.Resources.Account_Archive_Success, backup.Path), - Properties.Resources.Account_Archive_Title, - MessageBoxButton.OK, MessageBoxImage.Information); - } - catch (Exception) - { + var backup = AccountService.Archive(_vm.Account); + if (backup != null) MessageBox.Show(this, - Properties.Resources.Account_Archive_Error, + string.Format(Properties.Resources.Account_Archive_Success, backup.Path), Properties.Resources.Account_Archive_Title, - MessageBoxButton.OK, MessageBoxImage.Error); - } + MessageBoxButton.OK, MessageBoxImage.Information); } - - private void CloseCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + catch (Exception) { - e.CanExecute = _vm.HasAccount; + MessageBox.Show(this, + Properties.Resources.Account_Archive_Error, + Properties.Resources.Account_Archive_Title, + MessageBoxButton.OK, MessageBoxImage.Error); } + } - /// - /// Close the current account file. - /// - private void CloseCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - if (!AskSave()) - return; - _vm.Account = null; - _vm.Clean(); - } + private void CloseCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.HasAccount; + } - /// - /// Ask the user for saving the file before closing the window. - /// - private void Window_OnClosing(object sender, CancelEventArgs e) - { - if (!AskSave()) - e.Cancel = true; - } + /// + /// Close the current account file. + /// + private void CloseCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + if (!AskSave()) + return; + _vm.Account = null; + _vm.Clean(); + } - /// - /// Exit the application. - /// - private void ExitMenuItem_OnClick(object sender, RoutedEventArgs e) - { - Close(); - } + /// + /// Ask the user for saving the file before closing the window. + /// + private void Window_OnClosing(object sender, CancelEventArgs e) + { + if (!AskSave()) + e.Cancel = true; + } - /// - /// Ask whether the user wants to save modifications to the account file, - /// before closing the file or exiting the application. - /// - /// false to cancel the action, true to proceed - private bool AskSave() - { - if (_vm.Account == null) - return true; - var result = MessageBox.Show(this, Properties.Resources.Account_Save_BeforeExit_Content, - Properties.Resources.Account_Save_BeforeExit_Title, MessageBoxButton.YesNoCancel, - MessageBoxImage.Question, MessageBoxResult.Yes); - if (result == MessageBoxResult.Cancel) - return false; // Cancel the original action - if (result != MessageBoxResult.Yes) - return true; // Confirm the original action - try - { - _vm.Account = AccountService.Save(_vm.Account); - if (!string.IsNullOrEmpty(_vm.Account.Path)) - MessageBox.Show(this, - string.Format(Properties.Resources.Account_Save_Success, _vm.Account.Path), - Properties.Resources.Account_Save_Dialog_Title, - MessageBoxButton.OK, MessageBoxImage.Information); - } - catch (Exception) - { - MessageBox.Show(this, - Properties.Resources.Account_Save_Error, - Properties.Resources.Account_Save_Dialog_Title, - MessageBoxButton.OK, MessageBoxImage.Error); - } + /// + /// Exit the application. + /// + private void ExitMenuItem_OnClick(object sender, RoutedEventArgs e) + { + Close(); + } + /// + /// Ask whether the user wants to save modifications to the account file, + /// before closing the file or exiting the application. + /// + /// false to cancel the action, true to proceed + private bool AskSave() + { + if (_vm.Account == null) return true; + var result = MessageBox.Show(this, Properties.Resources.Account_Save_BeforeExit_Content, + Properties.Resources.Account_Save_BeforeExit_Title, MessageBoxButton.YesNoCancel, + MessageBoxImage.Question, MessageBoxResult.Yes); + if (result == MessageBoxResult.Cancel) + return false; // Cancel the original action + if (result != MessageBoxResult.Yes) + return true; // Confirm the original action + try + { + _vm.Account = AccountService.Save(_vm.Account); + if (!string.IsNullOrEmpty(_vm.Account.Path)) + MessageBox.Show(this, + string.Format(Properties.Resources.Account_Save_Success, _vm.Account.Path), + Properties.Resources.Account_Save_Dialog_Title, + MessageBoxButton.OK, MessageBoxImage.Information); } - - #endregion - - #region About - - /// - /// Display the "About" prompt. - /// - private void AboutMenuItem_OnClick(object sender, RoutedEventArgs e) + catch (Exception) { MessageBox.Show(this, - Properties.Resources.About_Content, - Properties.Resources.About_Title, - MessageBoxButton.OK, MessageBoxImage.Information); + Properties.Resources.Account_Save_Error, + Properties.Resources.Account_Save_Dialog_Title, + MessageBoxButton.OK, MessageBoxImage.Error); } - #endregion + return true; + } - #region Transactions list management + #endregion - private void PreviousYearCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = _vm.HasAccount; - } + #region About - /// - /// Navigate to the previous year. - /// - private void PreviousYearCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - _vm.DateFilter = _vm.DateFilter.AddYears(-1); - _vm.UpdateTransactionsView(); - } + /// + /// Display the "About" prompt. + /// + private void AboutMenuItem_OnClick(object sender, RoutedEventArgs e) + { + MessageBox.Show(this, + Properties.Resources.About_Content, + Properties.Resources.About_Title, + MessageBoxButton.OK, MessageBoxImage.Information); + } - private void PreviousMonthCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = _vm.HasAccount; - } + #endregion - /// - /// Navigate to the previous month. - /// - private void PreviousMonthCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - _vm.DateFilter = _vm.DateFilter.AddMonths(-1); - _vm.UpdateTransactionsView(); - } + #region Transactions list management - private void NextMonthCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = _vm.HasAccount; - } + private void PreviousYearCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.HasAccount; + } - /// - /// Navigate to the next month. - /// - private void NextMonthCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - _vm.DateFilter = _vm.DateFilter.AddMonths(1); - _vm.UpdateTransactionsView(); - } + /// + /// Navigate to the previous year. + /// + private void PreviousYearCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + _vm.DateFilter = _vm.DateFilter.AddYears(-1); + _vm.UpdateTransactionsView(); + } - private void NextYearCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = _vm.HasAccount; - } + private void PreviousMonthCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.HasAccount; + } - /// - /// Navigate to the next year. - /// - private void NextYearCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - _vm.DateFilter = _vm.DateFilter.AddYears(1); - _vm.UpdateTransactionsView(); - } + /// + /// Navigate to the previous month. + /// + private void PreviousMonthCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + _vm.DateFilter = _vm.DateFilter.AddMonths(-1); + _vm.UpdateTransactionsView(); + } - private void CurrentMonthCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = _vm.HasAccount; - } + private void NextMonthCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.HasAccount; + } - /// - /// Navigate to the current month. - /// - private void CurrentMonthCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - _vm.DateFilter = DateTime.Today; - _vm.UpdateTransactionsView(); - } + /// + /// Navigate to the next month. + /// + private void NextMonthCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + _vm.DateFilter = _vm.DateFilter.AddMonths(1); + _vm.UpdateTransactionsView(); + } - private void DuplicateCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = _vm.SelectedTransactions.Count > 0; - } + private void NextYearCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.HasAccount; + } - /// - /// Duplicate selected transactions to a new date. - /// - private void DuplicateCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - if (_vm.SelectedTransactions.Count == 0) - return; - // Open the dialog to allow the user to select the target date - var dialog = new TransactionsDuplicationWindow {Owner = this}; - dialog.ShowDialog(); - if (!dialog.SelectedDate.HasValue) - return; - // Duplicate selected transactions to the target date - var transactions = _vm.SelectedTransactions - .Select(tvm => tvm.Transaction).ToList(); - var targetDate = dialog.SelectedDate.Value; - if (_vm.Account?.Transactions.Duplicate(transactions, targetDate) == 0) - return; - MessageBox.Show(this, string.Format(Properties.Resources.Transactions_Duplicate_Success, - targetDate.ToShortDateString()), - Properties.Resources.Menu_Edit_Duplicate_Description, - MessageBoxButton.OK, MessageBoxImage.Information); - // Navigate to the target month - _vm.DateFilter = targetDate; - _vm.UpdateTransactionsView(); - } + /// + /// Navigate to the next year. + /// + private void NextYearCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + _vm.DateFilter = _vm.DateFilter.AddYears(1); + _vm.UpdateTransactionsView(); + } - private void CheckOffCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = _vm.Transaction != null; - } + private void CurrentMonthCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.HasAccount; + } - /// - /// Check the selected transaction off. - /// - private void CheckOffCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - var transaction = e.Parameter is Transaction p ? p : _vm.Transaction; - if (transaction == null || transaction.IsChecked) - return; - var updatedTransaction = new Transaction(transaction) {IsChecked = true}; - _vm.Account?.Transactions.Save(updatedTransaction); - _vm.UpdateTransactionsView(); - _vm.SelectedTransaction = _vm.FilteredTransactions - .Find(t => t.Transaction.Id == transaction.Id); - Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input, new Action(() => - ((UIElement) TransactionsListView.ItemContainerGenerator - .ContainerFromItem(_vm.SelectedTransaction)).Focus())); - } + /// + /// Navigate to the current month. + /// + private void CurrentMonthCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + _vm.DateFilter = DateTime.Today; + _vm.UpdateTransactionsView(); + } - #endregion + private void DuplicateCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.SelectedTransactions.Count > 0; + } - #region Transaction form + /// + /// Duplicate selected transactions to a new date. + /// + private void DuplicateCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + if (_vm.SelectedTransactions.Count == 0) + return; + // Open the dialog to allow the user to select the target date + var dialog = new TransactionsDuplicationWindow { Owner = this }; + dialog.ShowDialog(); + if (!dialog.SelectedDate.HasValue) + return; + // Duplicate selected transactions to the target date + var transactions = _vm.SelectedTransactions + .Select(tvm => tvm.Transaction).ToList(); + var targetDate = dialog.SelectedDate.Value; + if (_vm.Account?.Transactions.Duplicate(transactions, targetDate) == 0) + return; + MessageBox.Show(this, string.Format(Properties.Resources.Transactions_Duplicate_Success, + targetDate.ToShortDateString()), + Properties.Resources.Menu_Edit_Duplicate_Description, + MessageBoxButton.OK, MessageBoxImage.Information); + // Navigate to the target month + _vm.DateFilter = targetDate; + _vm.UpdateTransactionsView(); + } - /// - /// Update the transaction form when the selected transaction in the ListView changes. - /// - private void TransactionsListView_OnSelectionChanged(object sender, SelectionChangedEventArgs e) - { - _vm.SelectedTransactions = (sender as ListView)?.SelectedItems.Cast().ToList() - ?? new List(); - if (_vm.Transaction == null) - return; - ResetComboBoxes(); - _vm.Name = _vm.Transaction.Name; - _vm.Date = _vm.Transaction.Date; - _vm.Method = _vm.Transaction.Method; - _vm.Amount = _vm.Transaction.AmountAsString; - _vm.IsChecked = _vm.Transaction.IsChecked; - } + private void CheckOffCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm.Transaction != null; + } - private void AddTransactionCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = _vm.HasAccount && _vm.Transaction == null; - } + /// + /// Check the selected transaction off. + /// + private void CheckOffCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + var transaction = e.Parameter as Transaction ?? _vm.Transaction; + if (transaction == null || transaction.IsChecked) + return; + var updatedTransaction = new Transaction(transaction) { IsChecked = true }; + _vm.Account?.Transactions.Save(updatedTransaction); + _vm.UpdateTransactionsView(); + _vm.SelectedTransaction = _vm.FilteredTransactions + .Find(t => t.Transaction.Id == transaction.Id); + Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() => + ((UIElement)TransactionsListView.ItemContainerGenerator + .ContainerFromItem(_vm.SelectedTransaction)).Focus())); + } - /// - /// Initialize the form with default values for a new transaction. - /// - private void AddTransactionCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - _vm.Transaction = new Transaction(); - ResetComboBoxes(); - _vm.Name = _vm.Transaction.Name; - _vm.Date = _vm.Transaction.Date; - _vm.Method = _vm.Transaction.Method; - _vm.Amount = string.Empty; - _vm.IsChecked = _vm.Transaction.IsChecked; - NameComboBox.Focus(); - } + #endregion - /// - /// Reset transaction form combo-boxes. - /// - private void ResetComboBoxes() - { - NameComboBox.SelectedIndex = -1; - MethodComboBox.SelectedIndex = -1; - } + #region Transaction form - /// - /// Type the right currency decimal separator when the decimal key is pressed. - /// - private void AmountTextBox_OnPreviewKeyDown(object sender, KeyEventArgs e) - { - var currentDecimal = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator; - var invariantDecimal = NumberFormatInfo.InvariantInfo.CurrencyDecimalSeparator; - if (e.Key != Key.Decimal || currentDecimal == invariantDecimal || sender is not TextBox textBox) - return; - var caretIndex = textBox.CaretIndex; - textBox.Text = textBox.Text.Substring(0, textBox.SelectionStart) - + currentDecimal - + textBox.Text.Substring(textBox.SelectionStart + textBox.SelectionLength); - textBox.CaretIndex = caretIndex + 1; - e.Handled = true; - } + /// + /// Update the transaction form when the selected transaction in the ListView changes. + /// + private void TransactionsListView_OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + _vm.SelectedTransactions = (sender as ListView)?.SelectedItems.Cast().ToList() + ?? []; + if (_vm.Transaction == null) + return; + ResetComboBoxes(); + _vm.Name = _vm.Transaction.Name; + _vm.Date = _vm.Transaction.Date; + _vm.Method = _vm.Transaction.Method; + _vm.Amount = _vm.Transaction.AmountAsString; + _vm.IsChecked = _vm.Transaction.IsChecked; + } - /// - /// Close the form and unset the selected transaction. - /// - private void CancelButton_OnClick(object sender, RoutedEventArgs e) - { - _vm.SelectedTransaction = null; - } + private void AddTransactionCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = _vm is { HasAccount: true, Transaction: null }; + } - /// - /// Delete the selected transaction (with confirmation). - /// - private void DeleteButton_OnClick(object sender, RoutedEventArgs e) - { - if (_vm.Transaction == null) - return; - var result = MessageBox.Show(this, - Properties.Resources.Transaction_Form_Delete_Confirmation, - Properties.Resources.Transaction_Form_Delete_Description, - MessageBoxButton.YesNo, MessageBoxImage.Question); - if (result != MessageBoxResult.Yes) - return; // Cancel - // Delete the selected transaction - _vm.Account?.Transactions.Remove(_vm.Transaction); - _vm.SelectedTransaction = null; - _vm.UpdateTransactionsView(); - } + /// + /// Initialize the form with default values for a new transaction. + /// + private void AddTransactionCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + _vm.Transaction = new Transaction(); + ResetComboBoxes(); + _vm.Name = _vm.Transaction.Name; + _vm.Date = _vm.Transaction.Date; + _vm.Method = _vm.Transaction.Method; + _vm.Amount = string.Empty; + _vm.IsChecked = _vm.Transaction.IsChecked; + NameComboBox.Focus(); + } - /// - /// Validate and save the transaction. - /// - private void SaveButton_OnClick(object sender, RoutedEventArgs e) - { - // Validate fields - string? error = null; - var amount = decimal.Zero; - if (string.IsNullOrWhiteSpace(_vm.Name)) - error = Properties.Resources.Transaction_Form_Save_Validation_NameRequired; - else if (string.IsNullOrWhiteSpace(_vm.Method)) - error = Properties.Resources.Transaction_Form_Save_Validation_MethodRequired; - else if (!decimal.TryParse(_vm.Amount, out amount) || amount == 0) - error = Properties.Resources.Transaction_Form_Save_Validation_InvalidAmount; - if (error != null) - { - MessageBox.Show(this, error, - Properties.Resources.Transaction_Form_Save_Description, - MessageBoxButton.OK, MessageBoxImage.Warning); - return; - } - - // Save (add or update) the transaction - _vm.Account?.Transactions.Save(new Transaction - { - Id = _vm.Transaction?.Id ?? Guid.Empty, - Name = _vm.Name ?? string.Empty, - Date = _vm.Date, - Method = _vm.Method ?? string.Empty, - Amount = amount, - IsChecked = _vm.IsChecked - }); - _vm.DateFilter = _vm.Date; - _vm.SelectedTransaction = null; - _vm.UpdateTransactionsView(); - } + /// + /// Reset transaction form combo-boxes. + /// + private void ResetComboBoxes() + { + NameComboBox.SelectedIndex = -1; + MethodComboBox.SelectedIndex = -1; + } + + /// + /// Type the right currency decimal separator when the decimal key is pressed. + /// + private void AmountTextBox_OnPreviewKeyDown(object sender, KeyEventArgs e) + { + var currentDecimal = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator; + var invariantDecimal = NumberFormatInfo.InvariantInfo.CurrencyDecimalSeparator; + if (e.Key != Key.Decimal || currentDecimal == invariantDecimal || sender is not TextBox textBox) + return; + var caretIndex = textBox.CaretIndex; + textBox.Text = string.Concat( + textBox.Text.AsSpan(0, textBox.SelectionStart), + currentDecimal, + textBox.Text.AsSpan(textBox.SelectionStart + textBox.SelectionLength) + ); + textBox.CaretIndex = caretIndex + 1; + e.Handled = true; + } + + /// + /// Close the form and unset the selected transaction. + /// + private void CancelButton_OnClick(object sender, RoutedEventArgs e) + { + _vm.SelectedTransaction = null; + } + + /// + /// Delete the selected transaction (with confirmation). + /// + private void DeleteButton_OnClick(object sender, RoutedEventArgs e) + { + if (_vm.Transaction == null) + return; + var result = MessageBox.Show(this, + Properties.Resources.Transaction_Form_Delete_Confirmation, + Properties.Resources.Transaction_Form_Delete_Description, + MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result != MessageBoxResult.Yes) + return; // Cancel + // Delete the selected transaction + _vm.Account?.Transactions.Remove(_vm.Transaction); + _vm.SelectedTransaction = null; + _vm.UpdateTransactionsView(); + } - #endregion - } - - /// - /// Main window custom commands. - /// - public static class MainWindowCommands - { - /// - /// Create a backup of the current account file. - /// - public static readonly RoutedUICommand Archive = new( - Resources.Menu_File_Archive, - Resources.Menu_File_Archive, - typeof(MainWindowCommands), - new InputGestureCollection - { - new KeyGesture(Key.B, ModifierKeys.Control) - }); - - /// - /// Close the current account file. - /// - public static readonly RoutedUICommand Close = new( - Resources.Menu_File_Close, - Resources.Menu_File_Close, - typeof(MainWindowCommands), - new InputGestureCollection - { - new KeyGesture(Key.W, ModifierKeys.Control) - }); - - /// - /// Duplicate transactions selected in the list to another date. - /// - public static readonly RoutedUICommand Duplicate = new( - Resources.Menu_Edit_Duplicate_Description, - Resources.Menu_Edit_Duplicate, - typeof(MainWindowCommands), - new InputGestureCollection - { - new KeyGesture(Key.D, ModifierKeys.Control) - }); - - /// - /// Check the selected transaction off. - /// - public static readonly RoutedUICommand CheckOff = new( - Resources.Transaction_CheckOff, - Resources.Transaction_CheckOff, - typeof(MainWindowCommands)); - - /// - /// Initialize the form with default values for a new transaction. - /// - public static readonly RoutedUICommand AddTransaction = new( - Resources.Transaction_Form_Add, - Resources.Transaction_Form_Add_Description, - typeof(MainWindowCommands), - new InputGestureCollection - { - new KeyGesture(Key.T, ModifierKeys.Control) - }); - - /// - /// Navigate to the previous year. - /// - public static readonly RoutedUICommand PreviousYear = new( - Resources.Navigation_Year_Previous, - Resources.Navigation_Year_Previous, - typeof(MainWindowCommands)); - - /// - /// Navigate to the previous month. - /// - public static readonly RoutedUICommand PreviousMonth = new( - Resources.Navigation_Month_Previous, - Resources.Navigation_Month_Previous, - typeof(MainWindowCommands), - new InputGestureCollection - { - new KeyGesture(Key.Tab, ModifierKeys.Control | ModifierKeys.Shift) - }); - - /// - /// Navigate to the next month. - /// - public static readonly RoutedUICommand NextMonth = new( - Resources.Navigation_Month_Next, - Resources.Navigation_Month_Next, - typeof(MainWindowCommands), - new InputGestureCollection - { - new KeyGesture(Key.Tab, ModifierKeys.Control) - }); - - /// - /// Navigate to the next year. - /// - public static readonly RoutedUICommand NextYear = new( - Resources.Navigation_Year_Next, - Resources.Navigation_Year_Next, - typeof(MainWindowCommands)); - - /// - /// Navigate to the current month. - /// - public static readonly RoutedUICommand CurrentMonth = new( - Resources.Navigation_Current, - Resources.Navigation_Current, - typeof(MainWindowCommands), - new InputGestureCollection - { - new KeyGesture(Key.M, ModifierKeys.Control) - }); + /// + /// Validate and save the transaction. + /// + private void SaveButton_OnClick(object sender, RoutedEventArgs e) + { + // Validate fields + string? error = null; + var amount = decimal.Zero; + if (string.IsNullOrWhiteSpace(_vm.Name)) + error = Properties.Resources.Transaction_Form_Save_Validation_NameRequired; + else if (string.IsNullOrWhiteSpace(_vm.Method)) + error = Properties.Resources.Transaction_Form_Save_Validation_MethodRequired; + else if (!decimal.TryParse(_vm.Amount, out amount) || amount == 0) + error = Properties.Resources.Transaction_Form_Save_Validation_InvalidAmount; + if (error != null) + { + MessageBox.Show(this, error, + Properties.Resources.Transaction_Form_Save_Description, + MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + // Save (add or update) the transaction + _vm.Account?.Transactions.Save(new Transaction + { + Id = _vm.Transaction?.Id ?? Guid.Empty, + Name = _vm.Name ?? string.Empty, + Date = _vm.Date, + Method = _vm.Method ?? string.Empty, + Amount = amount, + IsChecked = _vm.IsChecked + }); + _vm.DateFilter = _vm.Date; + _vm.SelectedTransaction = null; + _vm.UpdateTransactionsView(); } + + #endregion +} + +/// +/// Main window custom commands. +/// +public static class MainWindowCommands +{ + /// + /// Create a backup of the current account file. + /// + public static readonly RoutedUICommand Archive = new( + Resources.Menu_File_Archive, + Resources.Menu_File_Archive, + typeof(MainWindowCommands), + [new KeyGesture(Key.B, ModifierKeys.Control)]); + + /// + /// Close the current account file. + /// + public static readonly RoutedUICommand Close = new( + Resources.Menu_File_Close, + Resources.Menu_File_Close, + typeof(MainWindowCommands), + [new KeyGesture(Key.W, ModifierKeys.Control)]); + + /// + /// Duplicate transactions selected in the list to another date. + /// + public static readonly RoutedUICommand Duplicate = new( + Resources.Menu_Edit_Duplicate_Description, + Resources.Menu_Edit_Duplicate, + typeof(MainWindowCommands), + [new KeyGesture(Key.D, ModifierKeys.Control)]); + + /// + /// Check the selected transaction off. + /// + public static readonly RoutedUICommand CheckOff = new( + Resources.Transaction_CheckOff, + Resources.Transaction_CheckOff, + typeof(MainWindowCommands)); + + /// + /// Initialize the form with default values for a new transaction. + /// + public static readonly RoutedUICommand AddTransaction = new( + Resources.Transaction_Form_Add, + Resources.Transaction_Form_Add_Description, + typeof(MainWindowCommands), + [new KeyGesture(Key.T, ModifierKeys.Control)]); + + /// + /// Navigate to the previous year. + /// + public static readonly RoutedUICommand PreviousYear = new( + Resources.Navigation_Year_Previous, + Resources.Navigation_Year_Previous, + typeof(MainWindowCommands)); + + /// + /// Navigate to the previous month. + /// + public static readonly RoutedUICommand PreviousMonth = new( + Resources.Navigation_Month_Previous, + Resources.Navigation_Month_Previous, + typeof(MainWindowCommands), + [new KeyGesture(Key.Tab, ModifierKeys.Control | ModifierKeys.Shift)]); + + /// + /// Navigate to the next month. + /// + public static readonly RoutedUICommand NextMonth = new( + Resources.Navigation_Month_Next, + Resources.Navigation_Month_Next, + typeof(MainWindowCommands), + [new KeyGesture(Key.Tab, ModifierKeys.Control)]); + + /// + /// Navigate to the next year. + /// + public static readonly RoutedUICommand NextYear = new( + Resources.Navigation_Year_Next, + Resources.Navigation_Year_Next, + typeof(MainWindowCommands)); + + /// + /// Navigate to the current month. + /// + public static readonly RoutedUICommand CurrentMonth = new( + Resources.Navigation_Current, + Resources.Navigation_Current, + typeof(MainWindowCommands), + [new KeyGesture(Key.M, ModifierKeys.Control)]); } diff --git a/Accounts/Windows/TransactionsDuplicationWindow.xaml.cs b/Accounts/Windows/TransactionsDuplicationWindow.xaml.cs index 8a9dc96..875cb15 100644 --- a/Accounts/Windows/TransactionsDuplicationWindow.xaml.cs +++ b/Accounts/Windows/TransactionsDuplicationWindow.xaml.cs @@ -1,77 +1,77 @@ using System; using System.Windows; using System.Windows.Input; +using Accounts.Properties; -namespace Accounts.Windows +namespace Accounts.Windows; + +/// +/// Date picker prompt used for transactions duplication. +/// +public partial class TransactionsDuplicationWindow : Window { /// - /// Date picker prompt used for transactions duplication. + /// Selected date. /// - public partial class TransactionsDuplicationWindow : Window - { - /// - /// Selected date. - /// - public DateTime? SelectedDate { get; private set; } - - public TransactionsDuplicationWindow() - { - InitializeComponent(); - } + public DateTime? SelectedDate { get; private set; } - #region Commands - - private void SubmitCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = DatePicker?.SelectedDate != null && DatePicker.SelectedDate.Value > new DateTime(2000, 1, 1); - } + public TransactionsDuplicationWindow() + { + InitializeComponent(); + } - /// - /// Store the selected date and close the window. - /// - private void SubmitCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - SelectedDate = DatePicker.SelectedDate; - Close(); - } + #region Commands - private void CancelCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = true; - } + private void SubmitCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = DatePicker?.SelectedDate != null && DatePicker.SelectedDate.Value > new DateTime(2000, 1, 1); + } - /// - /// Close the window. - /// - private void CancelCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) - { - Close(); - } + /// + /// Store the selected date and close the window. + /// + private void SubmitCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) + { + SelectedDate = DatePicker.SelectedDate; + Close(); + } - #endregion + private void CancelCommand_OnCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = true; } /// - /// Transactions duplication window custom commands. + /// Close the window. /// - public static class TransactionsDuplicationWindowCommands + private void CancelCommand_OnExecuted(object sender, ExecutedRoutedEventArgs e) { - /// - /// Submit the form: duplicate selected transactions to the selected date. - /// - public static readonly RoutedUICommand Submit = new( - Properties.Resources.Transactions_Duplicate_Dialog_Submit, - Properties.Resources.Transactions_Duplicate_Dialog_Submit, - typeof(TransactionsDuplicationWindowCommands), - new InputGestureCollection {new KeyGesture(Key.Enter)}); - - /// - /// Cancel duplication and close the dialog. - /// - public static readonly RoutedUICommand Cancel = new( - Properties.Resources.Transactions_Duplicate_Dialog_Cancel, - Properties.Resources.Transactions_Duplicate_Dialog_Cancel, - typeof(TransactionsDuplicationWindowCommands), - new InputGestureCollection {new KeyGesture(Key.Escape)}); + Close(); } + + #endregion +} + +/// +/// Transactions duplication window custom commands. +/// +public static class TransactionsDuplicationWindowCommands +{ + /// + /// Submit the form: duplicate selected transactions to the selected date. + /// + public static readonly RoutedUICommand Submit = new( + Resources.Transactions_Duplicate_Dialog_Submit, + Resources.Transactions_Duplicate_Dialog_Submit, + typeof(TransactionsDuplicationWindowCommands), + [new KeyGesture(Key.Enter)]); + + /// + /// Cancel duplication and close the dialog. + /// + public static readonly RoutedUICommand Cancel = new( + Resources.Transactions_Duplicate_Dialog_Cancel, + Resources.Transactions_Duplicate_Dialog_Cancel, + typeof(TransactionsDuplicationWindowCommands), + [new KeyGesture(Key.Escape)]); } diff --git a/README.md b/README.md index b7636d4..b39dfdc 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A basic personal financial-accounting software. ## Installation -- Install the [.NET Desktop Runtime](https://dotnet.microsoft.com/download/dotnet/current/runtime/desktop) +- Install the .NET Desktop Runtime - Download the [latest release](https://github.com/GaelGirodon/accounts/releases/latest) - Extract the archive - Run `Accounts.exe`