using Elwig.Helpers; using Elwig.Models; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Threading; 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; } } protected bool IsCreating { get { return _IsCreating; } set { _IsCreating = value; LockContext = IsEditing; } } protected bool IsClosing { get; private set; } private TextBox[] TextBoxInputs; private TextBox[] PlzInputs; private ComboBox[] ComboBoxInputs; private ComboBox[] PlzOrtInputs; private CheckComboBox[] CheckComboBoxInputs; private CheckBox[] CheckBoxInputs; private RadioButton[] RadioButtonInputs; private readonly Dictionary Valid; private readonly Dictionary OriginalValues; public AdministrationWindow() : base() { IsEditing = false; IsCreating = false; ExemptInputs = Array.Empty(); RequiredInputs = Array.Empty(); TextBoxInputs = Array.Empty(); PlzInputs = Array.Empty(); ComboBoxInputs = Array.Empty(); CheckComboBoxInputs = Array.Empty(); PlzOrtInputs = Array.Empty(); CheckBoxInputs = Array.Empty(); RadioButtonInputs = Array.Empty(); Valid = new(); OriginalValues = new(); Closing += OnClosing; Loaded += OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs evt) { TextBoxInputs = ControlUtils.FindAllChildren(this, ExemptInputs).ToArray(); ComboBoxInputs = ControlUtils.FindAllChildren(this, ExemptInputs).ToArray(); CheckBoxInputs = ControlUtils.FindAllChildren(this, ExemptInputs).ToArray(); CheckComboBoxInputs = ControlUtils.FindAllChildren(this, ExemptInputs).ToArray(); RadioButtonInputs = ControlUtils.FindAllChildren(this, ExemptInputs).ToArray(); PlzInputs = TextBoxInputs.Where(tb => "PLZ".Equals(tb.Tag)).ToArray(); PlzOrtInputs = PlzInputs.Select(tb => ControlUtils.FindNextSibling(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; } private void OnClosing(object? sender, CancelEventArgs evt) { IsClosing = true; } 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; } protected bool SenderIsRequired(object sender) { return (sender is Control c) && RequiredInputs.Contains(c); } protected void ClearInputStates() { foreach (var tb in TextBoxInputs) ControlUtils.ClearInputState(tb); foreach (var cb in ComboBoxInputs) ControlUtils.ClearInputState(cb); foreach (var ccb in CheckComboBoxInputs) ControlUtils.ClearInputState(ccb); foreach (var cb in CheckBoxInputs) ControlUtils.ClearInputState(cb); foreach (var rb in RadioButtonInputs) ControlUtils.ClearInputState(rb); } protected void ValidateRequiredInputs() { foreach (var input in RequiredInputs) { if (input is TextBox tb && tb.Text.Length == 0) { ControlUtils.SetInputInvalid(input); Valid[input] = false; } else if (input is ComboBox cb && cb.SelectedItem == null && cb.ItemsSource != null) { ControlUtils.SetInputInvalid(input); } } } protected void LockInputs() { foreach (var tb in TextBoxInputs) { tb.IsReadOnly = true; tb.Focusable = false; } foreach (var cb in ComboBoxInputs) cb.IsEnabled = false; foreach (var ccb in CheckComboBoxInputs) ccb.IsEnabled = false; foreach (var cb in CheckBoxInputs) cb.IsEnabled = false; foreach (var rb in RadioButtonInputs) rb.IsEnabled = false; } protected void UnlockInputs() { foreach (var tb in TextBoxInputs) { tb.IsReadOnly = false; tb.Focusable = true; } foreach (var cb in ComboBoxInputs) cb.IsEnabled = true; foreach (var ccb in CheckComboBoxInputs) ccb.IsEnabled = true; foreach (var cb in CheckBoxInputs) cb.IsEnabled = true; foreach (var rb in RadioButtonInputs) rb.IsEnabled = true; } protected void ClearOriginalValues() { OriginalValues.Clear(); } protected void FillOriginalValues() { foreach (var tb in TextBoxInputs) OriginalValues[tb] = tb.Text; foreach (var cb in ComboBoxInputs) OriginalValues[cb] = cb.SelectedItem; foreach (var ccb in CheckComboBoxInputs) OriginalValues[ccb] = ccb.SelectedItems.Cast().ToArray(); foreach (var cb in CheckBoxInputs) OriginalValues[cb] = (cb.IsChecked ?? false) ? bool.TrueString : null; foreach (var rb in RadioButtonInputs) OriginalValues[rb] = (rb.IsChecked ?? false) ? bool.TrueString : null; } protected void SetOriginalValue(Control input, object? value) { OriginalValues[input] = value; } protected void UnsetOriginalValue(Control input) { OriginalValues.Remove(input); } protected void ClearInputs(bool validate = true) { foreach (var tb in TextBoxInputs) tb.Text = ""; foreach (var cb in ComboBoxInputs) cb.SelectedItem = null; foreach (var ccb in CheckComboBoxInputs) ccb.SelectedItems.Clear(); foreach (var cb in CheckBoxInputs) cb.IsChecked = false; foreach (var rb in RadioButtonInputs) rb.IsChecked = false; if (validate) ValidateRequiredInputs(); } 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; } else if (input is TextBox tb) { return OriginalValues[tb]?.ToString() != tb.Text; } else if (input is ComboBox sb) { return OriginalValues[sb] != sb.SelectedItem; } else if (input is CheckComboBox ccb) { return !ccb.SelectedItems.Cast().ToArray().SequenceEqual(((object[]?)OriginalValues[ccb]) ?? Array.Empty()); } else if (input is CheckBox cb) { return (OriginalValues[cb] != null) != (cb.IsChecked ?? false); } else if (input is RadioButton rb) { return (OriginalValues[rb] != null) != (rb.IsChecked ?? false); } else { return false; } } protected bool HasChanged => !IsValid || TextBoxInputs.Any(InputHasChanged) || ComboBoxInputs.Any(InputHasChanged) || CheckComboBoxInputs.Any(InputHasChanged) || CheckBoxInputs.Any(InputHasChanged) || RadioButtonInputs.Any(InputHasChanged); 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; ControlUtils.RenewItemsSource(ortInput, list, i => (i as AT_PlzDest)?.Id); if (list != null && ortInput.SelectedItem == null && list.Count == 1) ortInput.SelectedItem = list[0]; UpdateComboBox(ortInput); } protected static void InitializeDelayTimer(TextBox tb, Action handler) { var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) }; timer.Tick += (object? sender, EventArgs evt) => { timer.Stop(); var (oSender, oEvent) = ((object, TextChangedEventArgs))timer.Tag; handler(oSender, oEvent); }; tb.TextChanged += (object sender, TextChangedEventArgs evt) => { timer.Stop(); timer.Tag = (sender, evt); timer.Start(); }; } protected bool InputTextChanged(TextBox input) { return InputTextChanged(input, new ValidationResult(true, null)); } protected bool InputTextChanged(TextBox input, Func checker) { return InputTextChanged(input, (tb, required, ctx) => checker(tb, required)); } protected bool InputTextChanged(TextBox input, Func checker) { return InputTextChanged(input, checker(input, SenderIsRequired(input), Context)); } protected bool InputTextChanged(TextBox input, ValidationResult res) { ValidateInput(input, res.IsValid); if (res.IsValid) { if (InputHasChanged(input)) { ControlUtils.SetInputChanged(input); } else { ControlUtils.ClearInputState(input); } } else { ControlUtils.SetInputInvalid(input); } UpdateButtons(); return res.IsValid; } protected bool InputLostFocus(TextBox input, Func checker, string? msg = null) { return InputLostFocus(input, (tb, requiered, ctx) => checker(tb, requiered), msg); } protected bool InputLostFocus(TextBox input, Func checker, string? msg = null) { return InputLostFocus(input, checker(input, SenderIsRequired(input), Context), msg); } protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) { if (!res.IsValid && !IsClosing) System.Windows.MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning); return res.IsValid; } protected void CheckBox_Changed(object sender, RoutedEventArgs evt) { var input = (CheckBox)sender; if (SenderIsRequired(input) && input.IsChecked != true) { ControlUtils.SetInputInvalid(input); } else if (InputHasChanged(input)) { ControlUtils.SetInputChanged(input); } else { ControlUtils.ClearInputState(input); } UpdateButtons(); } protected void RadioButton_Changed(object sender, RoutedEventArgs evt) { var input = (RadioButton)sender; if (SenderIsRequired(input) && input.IsChecked != true) { ControlUtils.SetInputInvalid(input); } else if (InputHasChanged(input)) { ControlUtils.SetInputChanged(input); } else { ControlUtils.ClearInputState(input); } UpdateButtons(); } protected void TextBox_TextChanged(object sender, RoutedEventArgs evt) { var input = (TextBox)sender; if (SenderIsRequired(input) && input.Text.Length == 0) { ValidateInput(input, false); ControlUtils.SetInputInvalid(input); } else { ValidateInput(input, true); if (InputHasChanged(input)) { ControlUtils.SetInputChanged(input); } else { ControlUtils.ClearInputState(input); } } UpdateButtons(); } private void UpdateComboBox(Control input) { bool valid = false; if (input is ComboBox cb) { valid = cb.ItemsSource == null || cb.SelectedItem != null || !RequiredInputs.Contains(input); } else if (input is CheckComboBox ccb) { valid = ccb.ItemsSource == null || ccb.SelectedItem != null || !RequiredInputs.Contains(input); } if (valid) { ValidateInput(input, true); if (InputHasChanged(input)) { ControlUtils.SetInputChanged(input); } else { ControlUtils.ClearInputState(input); } } else { ValidateInput(input, false); ControlUtils.SetInputInvalid(input); } UpdateButtons(); } protected void ComboBox_SelectionChanged(object sender, RoutedEventArgs evt) { UpdateComboBox((Control)sender); } protected void IntegerInput_TextChanged(object sender, RoutedEventArgs evt) { InputTextChanged((TextBox)sender, Validator.CheckInteger); } protected void PartialDateInput_TextChanged(object sender, RoutedEventArgs evt) { InputTextChanged((TextBox)sender, Validator.CheckPartialDate); } protected void PartialDateInput_LostFocus(object sender, RoutedEventArgs evt) { InputLostFocus((TextBox)sender, Validator.CheckPartialDate); } protected void DateInput_TextChanged(object sender, RoutedEventArgs evt) { InputTextChanged((TextBox)sender, Validator.CheckDate); } protected void DateInput_LostFocus(object sender, RoutedEventArgs evt) { InputLostFocus((TextBox)sender, Validator.CheckDate); } protected void PlzInput_TextChanged(object sender, RoutedEventArgs evt) { var plz = (TextBox)sender; InputTextChanged(plz, Validator.CheckPlz); UpdatePlz(plz, GetPlzOrtInput(plz)); } protected void PlzInput_LostFocus(object sender, RoutedEventArgs evt) { var plz = (TextBox)sender; InputLostFocus(plz, Validator.CheckPlz); UpdatePlz(plz, GetPlzOrtInput(plz)); } protected void EmailInput_TextChanged(object sender, RoutedEventArgs evt) { InputTextChanged((TextBox)sender, Validator.CheckEmailAddress); } protected void EmailInput_LostFocus(object sender, RoutedEventArgs evt) { InputLostFocus((TextBox)sender, Validator.CheckEmailAddress); } protected void PhoneNrInput_TextChanged(object sender, RoutedEventArgs evt) { InputTextChanged((TextBox)sender, Validator.CheckPhoneNumber); } protected void PhoneNrInput_LostFocus(object sender, RoutedEventArgs evt) { InputLostFocus((TextBox)sender, Validator.CheckPhoneNumber); } protected void IbanInput_TextChanged(object sender, RoutedEventArgs evt) { InputTextChanged((TextBox)sender, Validator.CheckIban); } protected void IbanInput_LostFocus(object sender, RoutedEventArgs evt) { InputLostFocus((TextBox)sender, Validator.CheckIban); } protected void BicInput_TextChanged(object sender, RoutedEventArgs evt) { InputTextChanged((TextBox)sender, Validator.CheckBic); } protected void BicInput_LostFocus(object sender, RoutedEventArgs evt) { InputLostFocus((TextBox)sender, Validator.CheckBic); } protected void UstIdNrInput_TextChanged(object sender, RoutedEventArgs evt) { InputTextChanged((TextBox)sender, Validator.CheckUstIdNr); } protected void UstIdNrInput_LostFocus(object sender, RoutedEventArgs evt) { InputLostFocus((TextBox)sender, Validator.CheckUstIdNr); } protected void LfbisNrInput_TextChanged(object sender, RoutedEventArgs evt) { InputTextChanged((TextBox)sender, Validator.CheckLfbisNr); } protected void LfbisNrInput_LostFocus(object sender, RoutedEventArgs evt) { InputLostFocus((TextBox)sender, Validator.CheckLfbisNr); } } }