From 17ff9d0c0bd62322db03be4e4338f177549b7d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Erbsh=C3=A4u=C3=9Fer?= Date: Tue, 6 Apr 2021 13:41:41 +0200 Subject: [PATCH] Fixed bugs the following bugs: * Opening the cost overview could lead to a crash if a cost entry did not cover all tenants. * The amount in the unit calculation window was not updated when changes were made. * After creation of a new billing the main window could be closed without getting a save dialog. Added autosave functionality. --- README.md | 3 + ucalc/App.xaml.cs | 11 +++ ucalc/Autosaver.cs | 106 +++++++++++++++++++++++++++ ucalc/BillingWindow.xaml.cs | 6 +- ucalc/CostEntryDetailsWindow.xaml.cs | 7 ++ ucalc/Data/BillingCalculator.cs | 12 ++- ucalc/MainWindow.xaml.cs | 40 +++++++++- ucalc/Models/Model.cs | 9 ++- ucalc/Models/Properties.cs | 3 +- ucalc/Pages/SideBar.xaml.cs | 2 +- 10 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 README.md create mode 100644 ucalc/Autosaver.cs diff --git a/README.md b/README.md new file mode 100644 index 0000000..7d59584 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# UCalc + +UCalc oder MietRechner ist eine Anwendung zur Berechnung der Nebenkosten. diff --git a/ucalc/App.xaml.cs b/ucalc/App.xaml.cs index d1a7db1..aae3e6b 100644 --- a/ucalc/App.xaml.cs +++ b/ucalc/App.xaml.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Windows; using UCalc.Data; namespace UCalc @@ -7,7 +8,9 @@ namespace UCalc public partial class App { private readonly RecentlyOpenedList _recentlyOpenedList; + private readonly Autosaver _autosaver; public static RecentlyOpenedList RecentlyOpenedList => ((App) Current)._recentlyOpenedList; + public static Autosaver Autosaver => ((App) Current)._autosaver; public App() { @@ -15,6 +18,14 @@ namespace UCalc Directory.CreateDirectory(appDataPath); _recentlyOpenedList = new RecentlyOpenedList($"{appDataPath}/recently.txt"); + _autosaver = new Autosaver($"{appDataPath}/running.txt", $"{appDataPath}/autosave.mr"); + } + + protected override void OnExit(ExitEventArgs e) + { + _autosaver.OnExit(); + + base.OnExit(e); } } } \ No newline at end of file diff --git a/ucalc/Autosaver.cs b/ucalc/Autosaver.cs new file mode 100644 index 0000000..542ec6e --- /dev/null +++ b/ucalc/Autosaver.cs @@ -0,0 +1,106 @@ +using System; +using System.IO; +using System.Threading; +using UCalc.Data; +using UCalc.Models; + +namespace UCalc +{ + public class Autosaver + { + private readonly string _runningPath; + private SaverThread _saverThread; + public string AutosavePath { get; } + public bool CanRecover { get; private set; } + + public Model OpenModel + { + set + { + CanRecover = false; + + if (value != null) + { + _saverThread = new SaverThread(15, AutosavePath, value); + } + else if (_saverThread != null) + { + _saverThread.Terminate(); + _saverThread = null; + } + } + } + + public Autosaver(string runningPath, string autosavePath) + { + _runningPath = runningPath; + AutosavePath = autosavePath; + + CanRecover = File.Exists(runningPath) && File.Exists(autosavePath); + + File.WriteAllText(runningPath, ""); + } + + public void OnExit() + { + if (!CanRecover) + { + try + { + File.Delete(_runningPath); + } + catch (IOException) + { + // Do nothing + } + } + } + + private class SaverThread + { + private volatile bool _terminate; + private readonly int _saveIntervalInS; + private readonly string _autosavePath; + private readonly Model _model; + + public SaverThread(int saveIntervalInS, string autosavePath, Model model) + { + _terminate = false; + _saveIntervalInS = saveIntervalInS; + _autosavePath = autosavePath; + _model = model; + + new Thread(ThreadFunc).Start(); + } + + public void Terminate() + { + _terminate = true; + } + + private void ThreadFunc() + { + var start = DateTime.Now; + + while (!_terminate) + { + Thread.Sleep(500); + + if ((DateTime.Now - start).Seconds >= _saveIntervalInS) + { + try + { + BillingLoader.Store(_autosavePath, _model.Dump()); + } + catch (Exception) + { + // Do nothing + } + + start = DateTime.Now; + } + } + } + } + } +} \ No newline at end of file diff --git a/ucalc/BillingWindow.xaml.cs b/ucalc/BillingWindow.xaml.cs index 1d8df98..2fae305 100644 --- a/ucalc/BillingWindow.xaml.cs +++ b/ucalc/BillingWindow.xaml.cs @@ -47,7 +47,7 @@ namespace UCalc public BillingWindow(string filePath, Billing billing) { FilePath = filePath; - Model = new Model(billing); + Model = new Model(billing, filePath == null); SaveCommand = new DelegateCommand(parameter => { Save(); }); SaveAsCommand = new DelegateCommand(parameter => { Save(true); }); PrintCommand = new DelegateCommand(parameter => { Print(); }); @@ -56,6 +56,8 @@ namespace UCalc Title = $"MietRechner - Abrechnung von {billing.StartDate.ToString(Constants.DateFormat)} - {billing.EndDate.Date.ToString(Constants.DateFormat)}"; + + App.Autosaver.OpenModel = Model; } private void OnClosing(object sender, CancelEventArgs e) @@ -90,6 +92,8 @@ namespace UCalc private void OnClosed(object sender, EventArgs e) { + App.Autosaver.OpenModel = null; + Application.Current.MainWindow?.Show(); } diff --git a/ucalc/CostEntryDetailsWindow.xaml.cs b/ucalc/CostEntryDetailsWindow.xaml.cs index f0e404a..799a887 100644 --- a/ucalc/CostEntryDetailsWindow.xaml.cs +++ b/ucalc/CostEntryDetailsWindow.xaml.cs @@ -46,6 +46,11 @@ namespace UCalc Details.UnitCount.PropertyChanged += DetailsPropertyChanged; Details.DiscountsInUnits.CollectionChanged += DetailsDiscountChanged; + foreach (var discountInUnits in Details.DiscountsInUnits) + { + discountInUnits.PropertyChanged += DetailsPropertyChanged; + } + DetailsPropertyChanged(null, null); if (Details.TotalAmount.ConvertedValue != 0 || Details.UnitCount.ConvertedValue != 0 || @@ -91,6 +96,8 @@ namespace UCalc discount.PropertyChanged -= DetailsPropertyChanged; discount.PropertyChanged += DetailsPropertyChanged; } + + DetailsPropertyChanged(sender, null); } private void OnCEClick(object sender, RoutedEventArgs e) diff --git a/ucalc/Data/BillingCalculator.cs b/ucalc/Data/BillingCalculator.cs index 9affbe0..7ae4494 100644 --- a/ucalc/Data/BillingCalculator.cs +++ b/ucalc/Data/BillingCalculator.cs @@ -663,9 +663,15 @@ namespace UCalc.Data new Tuple(0, 0, 0), (sum, tenantResult) => { - var costResult = tenantResult.Costs[cost]; - return new Tuple(sum.Item1 + costResult.TotalAmount, - sum.Item2 + costResult.PastAmount, sum.Item3 + costResult.FutureAmount); + if (tenantResult.Costs.TryGetValue(cost, out var costResult)) + { + return new Tuple(sum.Item1 + costResult.TotalAmount, + sum.Item2 + costResult.PastAmount, sum.Item3 + costResult.FutureAmount); + } + else + { + return sum; + } }); str.Append("Auf Mieter umgelegt:\n"); diff --git a/ucalc/MainWindow.xaml.cs b/ucalc/MainWindow.xaml.cs index b7ec7c8..0af7972 100644 --- a/ucalc/MainWindow.xaml.cs +++ b/ucalc/MainWindow.xaml.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; @@ -10,9 +11,37 @@ namespace UCalc { public partial class MainWindow { + private bool _showRecover; + public MainWindow() { InitializeComponent(); + + _showRecover = App.Autosaver.CanRecover; + } + + protected override void OnContentRendered(EventArgs e) + { + base.OnContentRendered(e); + + if (_showRecover) + { + _showRecover = false; + + switch (MessageBox.Show( + "Die Anwendung wurde das letzte Mal nicht richtig beendet. Es existiert allerdings eine Sicherheitskopie. Möchten Sie diese laden?", + "Sicherheitskopie laden?", + MessageBoxButton.YesNo, MessageBoxImage.Question)) + { + case MessageBoxResult.Yes: + OpenBilling(App.Autosaver.AutosavePath, true); + break; + case MessageBoxResult.No: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } } private void OnNewClick(object sender, RoutedEventArgs e) @@ -67,14 +96,17 @@ namespace UCalc OpenBilling(recentlyOpenedItem.Path); } - private void OpenBilling(string path) + private void OpenBilling(string path, bool isRecovery = false) { try { var billing = BillingLoader.Load(path); - App.RecentlyOpenedList.Add(new RecentlyOpenedItem(path)); + if (!isRecovery) + { + App.RecentlyOpenedList.Add(new RecentlyOpenedItem(path)); + } - new BillingWindow(path, billing).Show(); + new BillingWindow(isRecovery ? null : path, billing).Show(); Hide(); } catch (IOException e) diff --git a/ucalc/Models/Model.cs b/ucalc/Models/Model.cs index dd3bfbf..9fda144 100644 --- a/ucalc/Models/Model.cs +++ b/ucalc/Models/Model.cs @@ -161,12 +161,19 @@ namespace UCalc.Models private readonly Validator _validator; public BillingProperty Root { get; } - public Model(Billing billing) + public Model(Billing billing, bool modified) { _validator = new Validator(this); using var validator = BeginValidation(true); + Root = new BillingProperty(billing.StartDate, billing.EndDate, this, null, billing); + if (modified) + { + var oldName = Root.Landlord.Name.Value; + Root.Landlord.Name.Value = $"{oldName}_"; + Root.Landlord.Name.Value = oldName; + } } public Validator BeginValidation(bool postPone = false) diff --git a/ucalc/Models/Properties.cs b/ucalc/Models/Properties.cs index ae8a236..d7a1098 100644 --- a/ucalc/Models/Properties.cs +++ b/ucalc/Models/Properties.cs @@ -106,11 +106,12 @@ namespace UCalc.Models } _value = value; - OnPropertyChanged(); using var validator = Model.BeginValidation(); Modified = true; validator.Validate(this); + + OnPropertyChanged(); } } diff --git a/ucalc/Pages/SideBar.xaml.cs b/ucalc/Pages/SideBar.xaml.cs index 021d511..42802ac 100644 --- a/ucalc/Pages/SideBar.xaml.cs +++ b/ucalc/Pages/SideBar.xaml.cs @@ -50,7 +50,7 @@ namespace UCalc.Pages private void OnAboutClick(object sender, RoutedEventArgs e) { - MessageBox.Show("MietRechner Version 2.0\n\nCopyright © 2020 by Tobias Erbshäußer", "Über MietRechner", + MessageBox.Show("MietRechner Version 2.1\n\nCopyright © 2020-2021 by Tobias Erbshäußer", "Über MietRechner", MessageBoxButton.OK, MessageBoxImage.Information); } }