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.
This commit is contained in:
Tobias Erbshäußer
2021-04-06 13:41:41 +02:00
parent 5c96fced55
commit 17ff9d0c0b
10 changed files with 188 additions and 11 deletions
+3
View File
@@ -0,0 +1,3 @@
# UCalc
UCalc oder MietRechner ist eine Anwendung zur Berechnung der Nebenkosten.
+11
View File
@@ -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);
}
}
}
+106
View File
@@ -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;
}
}
}
}
}
}
+5 -1
View File
@@ -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();
}
+7
View File
@@ -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)
+9 -3
View File
@@ -663,9 +663,15 @@ namespace UCalc.Data
new Tuple<decimal, decimal, decimal>(0, 0, 0),
(sum, tenantResult) =>
{
var costResult = tenantResult.Costs[cost];
return new Tuple<decimal, decimal, decimal>(sum.Item1 + costResult.TotalAmount,
sum.Item2 + costResult.PastAmount, sum.Item3 + costResult.FutureAmount);
if (tenantResult.Costs.TryGetValue(cost, out var costResult))
{
return new Tuple<decimal, decimal, decimal>(sum.Item1 + costResult.TotalAmount,
sum.Item2 + costResult.PastAmount, sum.Item3 + costResult.FutureAmount);
}
else
{
return sum;
}
});
str.Append("Auf Mieter umgelegt:\n");
+36 -4
View File
@@ -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)
+8 -1
View File
@@ -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)
+2 -1
View File
@@ -106,11 +106,12 @@ namespace UCalc.Models
}
_value = value;
OnPropertyChanged();
using var validator = Model.BeginValidation();
Modified = true;
validator.Validate(this);
OnPropertyChanged();
}
}
+1 -1
View File
@@ -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);
}
}