Add RenewContext

This commit is contained in:
2023-05-25 18:03:38 +02:00
parent 27abf559e8
commit 7468af3970
9 changed files with 177 additions and 43 deletions

View File

@ -40,6 +40,9 @@ namespace Elwig.Helpers {
public DbSet<DeliveryPartModifier> DeliveryPartModifiers { get; private set; }
private readonly StreamWriter? LogFile = null;
public static DateTime LastWriteTime => File.GetLastWriteTime(App.Config.DatabaseFile);
public DateTime SavedLastWriteTime { get; private set; }
public bool HasBackendChanged => SavedLastWriteTime != LastWriteTime;
public AppDbContext() {
if (App.Config.DatabaseLog != null) {
@ -52,6 +55,9 @@ namespace Elwig.Helpers {
MessageBox.Show($"Unable to open database log file:\n\n{e.Message}", "Database Log", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
SavedLastWriteTime = LastWriteTime;
SavedChanges += OnSavedChanges;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
@ -67,6 +73,10 @@ namespace Elwig.Helpers {
GC.SuppressFinalize(this);
}
private void OnSavedChanges(object? sender, SavedChangesEventArgs evt) {
SavedLastWriteTime = LastWriteTime;
}
protected void Log(string msg) {
LogFile?.WriteLine(msg);
}

View File

@ -10,6 +10,7 @@ using System.Windows.Controls.Primitives;
using System.Text.RegularExpressions;
using System.IO.Ports;
using System.Net.Sockets;
using System.Collections;
namespace Elwig.Helpers {
public static partial class Utils {
@ -129,6 +130,40 @@ namespace Elwig.Helpers {
return null;
}
public static void RenewItemsSource(Selector selector, IEnumerable? source, Func<object?, object?> getId) {
var selectedId = getId(selector.SelectedItem);
selector.ItemsSource = source;
if (selectedId != null && source != null)
selector.SelectedItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
}
public static void RenewItemsSource(Xceed.Wpf.Toolkit.Primitives.Selector selector, IEnumerable? source, Func<object?, object?> getId) {
var selectedIds = selector.SelectedItems.Cast<object>().Select(i => getId(i)).ToList();
selector.ItemsSource = source;
if (source != null) {
foreach (var i in source.Cast<object>().Where(i => selectedIds.Contains(getId(i))))
selector.SelectedItems.Add(i);
}
}
public static void RenewItemsSource(DataGrid dataGrid, IEnumerable? source, Func<object?, object?> getId, bool keepSort = true) {
var column = dataGrid.CurrentCell.Column;
var sortColumns = dataGrid.Columns.Select(c => c.SortDirection).ToList();
var sort = dataGrid.Items.SortDescriptions.ToList();
var selectedId = getId(dataGrid.SelectedItem);
dataGrid.ItemsSource = source;
if (keepSort) {
for (int i = 0; i < dataGrid.Columns.Count; i++)
dataGrid.Columns[i].SortDirection = sortColumns[i];
foreach (var s in sort)
dataGrid.Items.SortDescriptions.Add(s);
}
if (selectedId != null && source != null)
dataGrid.SelectedItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
if (dataGrid.SelectedItem != null)
dataGrid.CurrentCell = new(dataGrid.SelectedItem, column);
}
public static int Modulo(string a, int b) {
if (a.Length == 0 || !a.All(char.IsAsciiDigit)) {
throw new ArgumentException("First argument has to be a decimal string");

View File

@ -1,27 +1,56 @@
using Elwig.Helpers;
using Elwig.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Xceed.Wpf.Toolkit;
namespace Elwig.Windows {
public abstract class AdministrationWindow : ContextWindow {
protected Control[] ExemptInputs { private get; set; }
protected Control[] RequiredInputs { private get; set; }
private bool _IsEditing;
private bool _IsCreating;
protected bool IsEditing {
get { return _IsEditing; }
set {
_IsEditing = value;
LockContext = IsEditing || IsCreating;
}
}
protected bool IsCreating {
get { return _IsCreating; }
set {
_IsCreating = value;
LockContext = IsEditing || IsCreating;
}
}
private TextBox[] TextBoxInputs;
private TextBox[] PlzInputs;
private ComboBox[] ComboBoxInputs;
private ComboBox[] PlzOrtInputs;
private CheckComboBox[] CheckComboBoxInputs;
private CheckBox[] CheckBoxInputs;
private RadioButton[] RadioButtonInputs;
private readonly Dictionary<Control, bool> Valid;
private readonly Dictionary<Control, object?> OriginalValues;
public AdministrationWindow() : base() {
IsEditing = false;
IsCreating = false;
ExemptInputs = Array.Empty<Control>();
RequiredInputs = Array.Empty<Control>();
TextBoxInputs = Array.Empty<TextBox>();
PlzInputs = Array.Empty<TextBox>();
ComboBoxInputs = Array.Empty<ComboBox>();
CheckComboBoxInputs = Array.Empty<CheckComboBox>();
PlzOrtInputs = Array.Empty<ComboBox>();
CheckBoxInputs = Array.Empty<CheckBox>();
RadioButtonInputs = Array.Empty<RadioButton>();
Valid = new();
@ -33,14 +62,30 @@ namespace Elwig.Windows {
TextBoxInputs = Utils.FindAllChildren<TextBox>(this, ExemptInputs).ToArray();
ComboBoxInputs = Utils.FindAllChildren<ComboBox>(this, ExemptInputs).ToArray();
CheckBoxInputs = Utils.FindAllChildren<CheckBox>(this, ExemptInputs).ToArray();
CheckComboBoxInputs = Utils.FindAllChildren<CheckComboBox>(this, ExemptInputs).ToArray();
RadioButtonInputs = Utils.FindAllChildren<RadioButton>(this, ExemptInputs).ToArray();
PlzInputs = TextBoxInputs.Where(tb => "PLZ".Equals(tb.Tag)).ToArray();
PlzOrtInputs = PlzInputs.Select(tb => Utils.FindNextSibling<ComboBox>(tb) ?? throw new MissingMemberException()).ToArray();
foreach (var tb in TextBoxInputs)
Valid[tb] = true;
foreach (var cb in ComboBoxInputs)
cb.SelectionChanged += ComboBox_SelectionChanged;
foreach (var cb in CheckComboBoxInputs)
cb.ItemSelectionChanged += ComboBox_SelectionChanged;
ValidateRequiredInputs();
}
private ComboBox GetPlzOrtInput(TextBox input) {
return PlzOrtInputs[Array.IndexOf(PlzInputs, input)];
}
abstract protected void UpdateButtons();
protected override async Task RenewContext() {
for (int i = 0; i < PlzInputs.Length; i++)
UpdatePlz(PlzInputs[i], PlzOrtInputs[i]);
}
protected void ValidateInput(Control input, bool valid) {
Valid[input] = valid;
}
@ -122,6 +167,10 @@ namespace Elwig.Windows {
protected bool IsValid => Valid.All(kv => kv.Value);
protected bool GetInputValid(Control input) {
return Valid[input];
}
protected bool InputHasChanged(Control input) {
if (!OriginalValues.ContainsKey(input)) {
return false;
@ -145,14 +194,13 @@ namespace Elwig.Windows {
CheckBoxInputs.Any(InputHasChanged) ||
RadioButtonInputs.Any(InputHasChanged);
protected void UpdatePlz(TextBox plzInput, bool plzInputValid, ComboBox ortInput) {
protected void UpdatePlz(TextBox plzInput, ComboBox ortInput) {
var plzInputValid = GetInputValid(plzInput);
var item = ortInput.SelectedItem;
var list = plzInputValid && plzInput.Text.Length == 4 ? Context.Postleitzahlen.Find(int.Parse(plzInput.Text))?.Orte.ToList() : null;
ortInput.ItemsSource = list;
ortInput.SelectedItem = (list != null && item != null && list.Contains(item)) ? item : null;
Utils.RenewItemsSource(ortInput, list, i => (i as AT_PlzDest)?.Id);
if (list != null && ortInput.SelectedItem == null && list.Count == 1)
ortInput.SelectedItem = list[0];
ComboBox_SelectionChanged(ortInput, new());
}
protected bool InputTextChanged(TextBox input, Func<TextBox, bool, ValidationResult> checker) {
@ -188,7 +236,7 @@ namespace Elwig.Windows {
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
if (!res.IsValid)
MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
System.Windows.MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
return res.IsValid;
}
@ -270,14 +318,14 @@ namespace Elwig.Windows {
protected void PlzInput_TextChanged(object sender, RoutedEventArgs evt) {
var plz = (TextBox)sender;
var valid = InputTextChanged(plz, Validator.CheckPlz);
UpdatePlz(plz, valid, Utils.FindNextSibling<ComboBox>(plz));
InputTextChanged(plz, Validator.CheckPlz);
UpdatePlz(plz, GetPlzOrtInput(plz));
}
protected void PlzInput_LostFocus(object sender, RoutedEventArgs evt) {
var plz = (TextBox)sender;
var valid = InputLostFocus(plz, Validator.CheckPlz);
UpdatePlz(plz, valid, Utils.FindNextSibling<ComboBox>(plz));
InputLostFocus(plz, Validator.CheckPlz);
UpdatePlz(plz, GetPlzOrtInput(plz));
}
protected void EmailInput_TextChanged(object sender, RoutedEventArgs evt) {

View File

@ -39,6 +39,10 @@ namespace Elwig.Windows {
CultInput.ItemsSource = Context.WineCultivations.OrderBy(c => c.Name).ToList();
}
protected override async Task RenewContext() {
await base.RenewContext();
}
private async Task RefreshContractList() {
/*
await Context.Contracts.LoadAsync();

View File

@ -1,4 +1,6 @@
using System.Threading.Tasks;
namespace Elwig.Windows {
public partial class ClientParamWindow : AdministrationWindow {
public ClientParamWindow() {
@ -8,5 +10,9 @@ namespace Elwig.Windows {
protected override void UpdateButtons() {
}
protected override async Task RenewContext() {
}
}
}

View File

@ -1,19 +1,43 @@
using Elwig.Helpers;
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace Elwig.Windows {
public abstract class ContextWindow : Window {
protected readonly AppDbContext Context;
protected AppDbContext Context { get; private set; }
protected bool LockContext { get; set; } = false;
private readonly DispatcherTimer ContextRenewTimer;
private static readonly int ContextRenewSec = 10;
public ContextWindow() : base() {
ContextRenewTimer = new DispatcherTimer();
ContextRenewTimer.Tick += new EventHandler(OnRenewContext);
ContextRenewTimer.Interval = new TimeSpan(0, 0, ContextRenewSec);
ContextRenewTimer.Start();
Context = new();
Loaded += OnLoaded;
}
private void OnRenewContext(object? sender, EventArgs evt) {
if (LockContext || !Context.HasBackendChanged) return;
Context.Dispose();
Context = new();
RenewContext();
}
private void OnLoaded(object sender, RoutedEventArgs evt) {
RenewContext();
}
protected override void OnClosed(EventArgs evt) {
base.OnClosed(evt);
Context.Dispose();
}
abstract protected Task RenewContext();
}
}

View File

@ -1,8 +1,10 @@
using Elwig.Helpers;
using Elwig.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
@ -25,7 +27,6 @@ namespace Elwig.Windows {
WineOriginInput, WineKgInput
};
ExemptInputs = new Control[] {
MgNrInput, MemberInput,
LsNrInput, DateInput, TimeInput, BranchInput,
MemberAddressField
};
@ -37,15 +38,7 @@ namespace Elwig.Windows {
}
private void Window_Loaded(object sender, RoutedEventArgs evt) {
MemberInput.ItemsSource = Context.Members.OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToList();
BranchInput.ItemsSource = Context.Branches.OrderBy(b => b.Name).ToList();
BranchInput.SelectedItem = BranchInput.ItemsSource.Cast<Branch>().First(b => b.ZwstId == App.ZwstId);
WineVarietyInput.ItemsSource = Context.WineVarieties.OrderBy(v => v.Name).ToList();
AttributesInput.ItemsSource = Context.WineAttributes.OrderBy(a => a.Name).ToList();
WineQualityLevelInput.ItemsSource = Context.WineQualityLevels.ToList();
ModifiersInput.ItemsSource = Context.Modifiers.Where(m => m.Season.Year == 2022).OrderBy(m => m.Name).ToList();
WineOriginInput.ItemsSource = Context.WineOrigins.ToList().OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId);
WineKgInput.ItemsSource = Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToList();
}
private void OnSecondPassed(object? sender, EventArgs evt) {
@ -58,6 +51,21 @@ namespace Elwig.Windows {
}
protected override async Task RenewContext() {
await base.RenewContext();
Utils.RenewItemsSource(MemberInput, await Context.Members.OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr);
Utils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId);
BranchInput.SelectedItem = BranchInput.ItemsSource.Cast<Branch>().First(b => b.ZwstId == App.ZwstId);
Utils.RenewItemsSource(WineVarietyInput, await Context.WineVarieties.OrderBy(v => v.Name).ToListAsync(), i => (i as WineVar)?.SortId);
Utils.RenewItemsSource(AttributesInput, await Context.WineAttributes.OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId);
Utils.RenewItemsSource(WineQualityLevelInput, await Context.WineQualityLevels.ToListAsync(), i => (i as WineQualLevel)?.QualId);
Utils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Season.Year == 2022).OrderBy(m => m.Name).ToListAsync(), i => (i as Modifier)?.ModId);
Utils.RenewItemsSource(WineOriginInput, (await Context.WineOrigins.ToListAsync()).OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId), i => (i as WineOrigin)?.HkId);
Utils.RenewItemsSource(WineKgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr);
UpdateWineQualityLevels();
UpdateRdInput();
}
private void MgNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
var valid = InputTextChanged((TextBox)sender, Validator.CheckMgNr);
MemberInput.SelectedItem = valid ? Context.Members.Find(int.Parse(MgNrInput.Text)) : null;
@ -177,17 +185,20 @@ namespace Elwig.Windows {
UpdateWineOrigin();
}
private void WineKgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
UpdateWineOrigin();
var kg = (AT_Kg)WineKgInput.SelectedItem;
if (kg != null) {
private void UpdateRdInput() {
if (WineKgInput.SelectedItem is AT_Kg kg) {
var list = Context.WbRde.Where(r => r.KgNr == kg.KgNr).OrderBy(r => r.Name).Cast<object>().ToList();
list.Insert(0, new NullItem());
WineRdInput.ItemsSource = list;
WineRdInput.SelectedIndex = 0;
Utils.RenewItemsSource(WineRdInput, list, i => ((i as WbRd)?.KgNr, (i as WbRd)?.RdNr));
if (WineRdInput.SelectedItem == null) WineRdInput.SelectedIndex = 0;
} else {
WineRdInput.ItemsSource = null;
}
}
private void WineKgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
UpdateWineOrigin();
UpdateRdInput();
}
}
}

View File

@ -152,9 +152,8 @@
<Label Content="PLZ/Ort:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="PlzInput" Margin="0,160,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus"/>
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
<ComboBox x:Name="OrtInput" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
SelectionChanged="ComboBox_SelectionChanged"
Margin="47,160,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
</Grid>
</GroupBox>
@ -240,9 +239,8 @@
<Label Content="PLZ/Ort:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingPlzInput" Margin="0,70,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus"/>
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
<ComboBox x:Name="BillingOrtInput" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
SelectionChanged="ComboBox_SelectionChanged"
Margin="47,70,10,0" Grid.Column="1"/>
</Grid>
</GroupBox>
@ -284,12 +282,10 @@
<Label Content="StammZwst.:" Margin="10,130,0,0" Grid.Column="0"/>
<ComboBox x:Name="BranchInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
SelectionChanged="ComboBox_SelectionChanged"
Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Content="Stammgemeinde:" Margin="10,160,0,0" Grid.Column="0"/>
<ComboBox x:Name="DefaultKgInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
SelectionChanged="ComboBox_SelectionChanged"
Margin="0,160,10,0" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/>

View File

@ -12,8 +12,7 @@ using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace Elwig.Windows {
public partial class MemberAdminWindow : AdministrationWindow {
private bool IsEditing = false;
private bool IsCreating = false;
private List<string> TextFilter = new();
private readonly RoutedCommand CtrlF = new();
@ -35,8 +34,6 @@ namespace Elwig.Windows {
private void Window_Loaded(object sender, RoutedEventArgs evt) {
ActiveMemberInput.IsChecked = true;
BranchInput.ItemsSource = Context.Branches.OrderBy(b => b.Name).ToList();
DefaultKgInput.ItemsSource = Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToList();
}
private async Task RefreshMemberList() {
@ -44,7 +41,7 @@ namespace Elwig.Windows {
await RefreshMemberListQuery();
}
private async Task RefreshMemberListQuery() {
private async Task RefreshMemberListQuery(bool updateSort = false) {
IQueryable<Member> memberQuery = Context.Members;
if (ActiveMemberInput.IsChecked == true)
memberQuery = memberQuery.Where(m => m.IsActive);
@ -67,7 +64,7 @@ namespace Elwig.Windows {
.ToList();
}
MemberList.ItemsSource = members;
Utils.RenewItemsSource(MemberList, members, i => (i as Member)?.MgNr, !updateSort);
if (members.Count == 1)
MemberList.SelectedIndex = 0;
@ -104,6 +101,13 @@ namespace Elwig.Windows {
ValidateRequiredInputs();
}
protected override async Task RenewContext() {
await base.RenewContext();
Utils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId);
Utils.RenewItemsSource(DefaultKgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr);
await RefreshMemberList();
}
private void MemberList_SelectionChanged(object sender, RoutedEventArgs evt) {
RefreshInputs();
}
@ -195,7 +199,7 @@ namespace Elwig.Windows {
private async void SearchInput_TextChanged(object sender, RoutedEventArgs evt) {
TextFilter = SearchInput.Text.ToLower().Split(" ").ToList().FindAll(e => e.Length > 0);
await RefreshMemberListQuery();
await RefreshMemberListQuery(true);
}
private void Menu_Member_SendEmail_Click(object sender, RoutedEventArgs evt) {
@ -358,11 +362,9 @@ namespace Elwig.Windows {
AT_PlzDest? p = m.PostalDest.AtPlz;
if (p != null) {
PlzInput.Text = p.Plz.ToString();
OrtInput.ItemsSource = p.AtPlz.Orte;
OrtInput.SelectedItem = p;
} else {
PlzInput.Text = null;
OrtInput.ItemsSource = null;
OrtInput.SelectedItem = null;
}
@ -385,14 +387,12 @@ namespace Elwig.Windows {
AT_PlzDest? b = billingAddr.PostalDest.AtPlz;
if (b != null) {
BillingPlzInput.Text = b.Plz.ToString();
BillingOrtInput.ItemsSource = b.AtPlz.Orte;
BillingOrtInput.SelectedItem = b;
}
} else {
BillingNameInput.Text = "";
BillingAddressInput.Text = "";
BillingPlzInput.Text = "";
BillingOrtInput.ItemsSource = null;
BillingOrtInput.SelectedItem = null;
}