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:
+///
+/// - A green background and an arrow pointing to the top-right corner if the amount is positive,
+/// - A red background and an arrow pointing to the bottom-right corner if the amount is negative,
+/// - A gray background and an arrow pointing to the right elsewhere.
+///
+///
+public partial class AmountArrow : UserControl
{
///
- /// Amount arrow control.
- ///
- /// Displays a circle with:
- ///
- /// - A green background and an arrow pointing to the top-right corner if the amount is positive,
- /// - A red background and an arrow pointing to the bottom-right corner if the amount is negative,
- /// - A gray background and an arrow pointing to the right elsewhere.
- ///
+ /// 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`