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;
using System.IO; using System.IO;
using System.Windows;
using UCalc.Data; using UCalc.Data;
namespace UCalc namespace UCalc
@@ -7,7 +8,9 @@ namespace UCalc
public partial class App public partial class App
{ {
private readonly RecentlyOpenedList _recentlyOpenedList; private readonly RecentlyOpenedList _recentlyOpenedList;
private readonly Autosaver _autosaver;
public static RecentlyOpenedList RecentlyOpenedList => ((App) Current)._recentlyOpenedList; public static RecentlyOpenedList RecentlyOpenedList => ((App) Current)._recentlyOpenedList;
public static Autosaver Autosaver => ((App) Current)._autosaver;
public App() public App()
{ {
@@ -15,6 +18,14 @@ namespace UCalc
Directory.CreateDirectory(appDataPath); Directory.CreateDirectory(appDataPath);
_recentlyOpenedList = new RecentlyOpenedList($"{appDataPath}/recently.txt"); _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) public BillingWindow(string filePath, Billing billing)
{ {
FilePath = filePath; FilePath = filePath;
Model = new Model(billing); Model = new Model(billing, filePath == null);
SaveCommand = new DelegateCommand(parameter => { Save(); }); SaveCommand = new DelegateCommand(parameter => { Save(); });
SaveAsCommand = new DelegateCommand(parameter => { Save(true); }); SaveAsCommand = new DelegateCommand(parameter => { Save(true); });
PrintCommand = new DelegateCommand(parameter => { Print(); }); PrintCommand = new DelegateCommand(parameter => { Print(); });
@@ -56,6 +56,8 @@ namespace UCalc
Title = Title =
$"MietRechner - Abrechnung von {billing.StartDate.ToString(Constants.DateFormat)} - {billing.EndDate.Date.ToString(Constants.DateFormat)}"; $"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) private void OnClosing(object sender, CancelEventArgs e)
@@ -90,6 +92,8 @@ namespace UCalc
private void OnClosed(object sender, EventArgs e) private void OnClosed(object sender, EventArgs e)
{ {
App.Autosaver.OpenModel = null;
Application.Current.MainWindow?.Show(); Application.Current.MainWindow?.Show();
} }
+7
View File
@@ -46,6 +46,11 @@ namespace UCalc
Details.UnitCount.PropertyChanged += DetailsPropertyChanged; Details.UnitCount.PropertyChanged += DetailsPropertyChanged;
Details.DiscountsInUnits.CollectionChanged += DetailsDiscountChanged; Details.DiscountsInUnits.CollectionChanged += DetailsDiscountChanged;
foreach (var discountInUnits in Details.DiscountsInUnits)
{
discountInUnits.PropertyChanged += DetailsPropertyChanged;
}
DetailsPropertyChanged(null, null); DetailsPropertyChanged(null, null);
if (Details.TotalAmount.ConvertedValue != 0 || Details.UnitCount.ConvertedValue != 0 || if (Details.TotalAmount.ConvertedValue != 0 || Details.UnitCount.ConvertedValue != 0 ||
@@ -91,6 +96,8 @@ namespace UCalc
discount.PropertyChanged -= DetailsPropertyChanged; discount.PropertyChanged -= DetailsPropertyChanged;
discount.PropertyChanged += DetailsPropertyChanged; discount.PropertyChanged += DetailsPropertyChanged;
} }
DetailsPropertyChanged(sender, null);
} }
private void OnCEClick(object sender, RoutedEventArgs e) 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), new Tuple<decimal, decimal, decimal>(0, 0, 0),
(sum, tenantResult) => (sum, tenantResult) =>
{ {
var costResult = tenantResult.Costs[cost]; 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); 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"); 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;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
@@ -10,9 +11,37 @@ namespace UCalc
{ {
public partial class MainWindow public partial class MainWindow
{ {
private bool _showRecover;
public MainWindow() public MainWindow()
{ {
InitializeComponent(); 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) private void OnNewClick(object sender, RoutedEventArgs e)
@@ -67,14 +96,17 @@ namespace UCalc
OpenBilling(recentlyOpenedItem.Path); OpenBilling(recentlyOpenedItem.Path);
} }
private void OpenBilling(string path) private void OpenBilling(string path, bool isRecovery = false)
{ {
try try
{ {
var billing = BillingLoader.Load(path); 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(); Hide();
} }
catch (IOException e) catch (IOException e)
+8 -1
View File
@@ -161,12 +161,19 @@ namespace UCalc.Models
private readonly Validator _validator; private readonly Validator _validator;
public BillingProperty Root { get; } public BillingProperty Root { get; }
public Model(Billing billing) public Model(Billing billing, bool modified)
{ {
_validator = new Validator(this); _validator = new Validator(this);
using var validator = BeginValidation(true); using var validator = BeginValidation(true);
Root = new BillingProperty(billing.StartDate, billing.EndDate, this, null, billing); 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) public Validator BeginValidation(bool postPone = false)
+2 -1
View File
@@ -106,11 +106,12 @@ namespace UCalc.Models
} }
_value = value; _value = value;
OnPropertyChanged();
using var validator = Model.BeginValidation(); using var validator = Model.BeginValidation();
Modified = true; Modified = true;
validator.Validate(this); validator.Validate(this);
OnPropertyChanged();
} }
} }
+1 -1
View File
@@ -50,7 +50,7 @@ namespace UCalc.Pages
private void OnAboutClick(object sender, RoutedEventArgs e) 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); MessageBoxButton.OK, MessageBoxImage.Information);
} }
} }