using InABox.Core; using InABox.Wpf; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; namespace InABox.DynamicGrid { /// /// Interaction logic for EmbeddedDynamicEditorForm.xaml /// public partial class EmbeddedDynamicEditorForm : UserControl, IDynamicEditorForm { public event OnBeforeLoad? OnBeforeLoad; public void BeforeLoad() => OnBeforeLoad?.Invoke(this); public event OnAfterLoad? OnAfterLoad; public void AfterLoad() => OnAfterLoad?.Invoke(this); public event IDynamicEditorForm.OnReloadEventHandler? OnReload; public event EventHandler OnChanged; private bool bChanged = false; private bool _validated = false; public void DoChanged() { bChanged = true; _validated = false; //OKButton.IsEnabled = true; //CancelButton.IsEnabled = true; OnChanged?.Invoke(this, EventArgs.Empty); UpdateButtonEnabled(); } public delegate void OKEvent(); public delegate void CancelEvent(); public DynamicEditorPages Pages { get; private set; } = []; private BaseObject[] _items; public BaseObject[] Items { get => _items; set { _items = value; DynamicEditorFormModel.Slug = Items?.FirstOrDefault()?.GetType().EntityName().Split('.').Last() ?? ""; Editor.Load(Pages); bChanged = _items.Any(x => x is not Entity e || e.ID == Guid.Empty); UpdateButtonEnabled(); foreach (var page in Pages) page.OnChanged += (sender, args) => DoChanged(); } } public bool ReadOnly { get => Editor.ReadOnly; set { Editor.ReadOnly = value; UpdateButtonEnabled(); } } private bool _disableIfUnchanged = false; public bool DisableOKIfUnchanged { get => _disableIfUnchanged; set { _disableIfUnchanged = value; UpdateButtonEnabled(); } } private bool _hideButtons = false; public bool HideButtons { get => _hideButtons; set { _hideButtons = value; OKButton.Visibility = value ? Visibility.Collapsed : Visibility.Visible; CancelButton.Visibility = value ? Visibility.Collapsed : Visibility.Visible; } } private bool _highlightButtons = false; public bool HighlightButtons { get => _highlightButtons; set { _highlightButtons = value; UpdateButtonHighlight(OKButton, Colors.DarkGreen, Colors.LimeGreen, Colors.White); UpdateButtonHighlight(CancelButton, Colors.Firebrick, Colors.Red, Colors.White); } } public double ContentWidth => Editor.TotalWidth; public double ContentHeight => Editor.TotalHeight; private void UpdateButtonEnabled() { OKButton.IsEnabled = !ReadOnly && (!DisableOKIfUnchanged || bChanged); CancelButton.IsEnabled = !ReadOnly; } private void UpdateButtonHighlight(Button button, Color border, Color background, Color foreground) { button.BorderBrush = _highlightButtons ? new SolidColorBrush(border) : new SolidColorBrush(Colors.Gray); button.Background = _highlightButtons ? new SolidColorBrush(background) : new SolidColorBrush(Colors.Gainsboro); button.Foreground = _highlightButtons ? new SolidColorBrush(foreground) : new SolidColorBrush(Colors.Black); button.FontWeight = _highlightButtons ? FontWeights.Bold : FontWeights.Normal; } public static readonly DependencyProperty ButtonsVisibleProperty = DependencyProperty.Register( nameof(ButtonsVisible), typeof(bool), typeof(EmbeddedDynamicEditorForm), new UIPropertyMetadata(true) ); public bool ButtonsVisible { get => (bool)GetValue(ButtonsVisibleProperty); set { SetValue(ButtonsVisibleProperty, value); UpdateButtonsRowVisibility(); } } private void UpdateButtonsRowVisibility() { ButtonRow.Height = ButtonsVisible ? new GridLength(1, GridUnitType.Auto) : new GridLength(0, GridUnitType.Pixel); } public static readonly DependencyProperty TabsVisibleProperty = DependencyProperty.Register( nameof(TabsVisible), typeof(bool), typeof(EmbeddedDynamicEditorForm), new UIPropertyMetadata(true) ); public bool TabsVisible { get => (bool)GetValue(TabsVisibleProperty); set { SetValue(TabsVisibleProperty, value); UpdateTabsVisibility(); } } private void UpdateTabsVisibility() { Editor.TabStripVisible = TabsVisible; } #region Events public event OnValidateData? OnValidateData; public OnCustomiseColumns? OnCustomiseColumns { get; set; } public OnDefineLookupFilter? OnDefineFilter { get; set; } public OnDefineLookup? OnDefineLookups { get; set; } public DefineEditorEventHandler? OnDefineEditor { get; set; } public event OnFormCustomiseEditor? OnFormCustomiseEditor; public OnReconfigureEditors? OnReconfigureEditors { get; set; } public event OnCreateEditorControl? OnCreateEditorControl; public event EditorValueChangedHandler? OnEditorValueChanged; public event OnAfterEditorValueChanged? OnAfterEditorValueChanged; public event OnSelectPage? OnSelectPage; public DynamicGridSaveEvent? OnSaveItem { get; set; } public DynamicEditorGrid.EditorCreatedHandler? OnEditorCreated; public event OKEvent? OnOK; public event CancelEvent? OnCancel; #endregion public EmbeddedDynamicEditorForm() { InitializeComponent(); ReadOnly = false; Editor.Form = this; } public override void OnApplyTemplate() { base.OnApplyTemplate(); UpdateButtonsRowVisibility(); UpdateTabsVisibility(); } public EmbeddedDynamicEditorForm(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null, Func? PageDataHandler = null, bool PreloadPages = false): this() { Setup(type, pages, buttons, PageDataHandler, PreloadPages); } private IFilter? Editor_OnDefineFilter(Type type, string column) { return OnDefineFilter?.Invoke(type, column); } private void ClearEvents() { // These are events that are set by the grid/host. OnCreateEditorControl = null; OnEditorValueChanged = null; OnFormCustomiseEditor = null; OnAfterEditorValueChanged = null; OnSelectPage = null; OnValidateData = null; } public void Setup(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null, Func? PageDataHandler = null, bool PreloadPages = false) { ClearEvents(); Editor.UnderlyingType = type; Editor.OnLoadPage = page => { page.Load(Items.First(), PageDataHandler); }; Editor.PreloadPages = PreloadPages; Pages = pages ?? new DynamicEditorPages(); Buttons.Children.Clear(); if (buttons != null) foreach (var button in buttons) { var btn = new Button(); UpdateButton(btn, button.Image, button.Name); btn.Tag = button; btn.Margin = new Thickness(0, 0, 5, 0); btn.Padding = new Thickness(5, 0, 5, 0); btn.Click += Btn_Click; Buttons.Children.Add(btn); button.Button = btn; button.Form = this; } } public void UnloadEditorPages(bool saved) { Editor.UnloadPages(saved); } protected void UpdateButton(Button button, BitmapImage? image, string text) { var stackPnl = new StackPanel(); stackPnl.Orientation = Orientation.Horizontal; //stackPnl.Margin = new Thickness(2); if (image != null) { var img = new Image(); img.Source = image; img.Margin = new Thickness(2); stackPnl.Children.Add(img); } if (!string.IsNullOrEmpty(text)) { var lbl = new Label(); lbl.Content = text; lbl.VerticalAlignment = VerticalAlignment.Stretch; lbl.VerticalContentAlignment = VerticalAlignment.Center; lbl.Margin = new Thickness(2, 0, 5, 0); stackPnl.Children.Add(lbl); } button.Content = stackPnl; } private Dictionary EditorValueChanged(IDynamicEditorForm sender, string name, object value) { if (OnEditorValueChanged != null) return OnEditorValueChanged(sender, name, value); return DynamicGridUtils.UpdateEditorValue(_items, name, value); } private void Editor_OnEditorCreated(object sender, double height, double width) { OnEditorCreated?.Invoke(sender, height, width); Editor.VerticalAlignment = VerticalAlignment.Stretch; Editor.HorizontalAlignment = HorizontalAlignment.Stretch; } private void Editor_OnCustomiseColumns(object sender, DynamicGridColumns columns) { columns.Clear(); if (_items != null && _items.Any()) columns.ExtractColumns(_items.First().GetType()); OnCustomiseColumns?.Invoke(this, columns); } private void Btn_Click(object sender, RoutedEventArgs e) { var button = (Button)sender; var deb = (DynamicEditorButton)button.Tag; deb.Click(); } public bool Validate() { var errors = OnValidateData?.Invoke(this, Items); if (errors != null && errors.Any()) { MessageWindow.ShowMessage( string.Format("The following errors have been found with your data!\nPlease correct them and try again.\n\n- {0}", string.Join("\n- ", errors)), "Validation Error"); return false; } _validated = true; return true; } private void OKButton_Click(object sender, RoutedEventArgs e) { if (!Validate()) { return; } OnOK?.Invoke(); //OKButton.IsEnabled = false; //CancelButton.IsEnabled = false; // Don't Commit the changes here, because we want to refer back to thos changes when we save the item // to trigger specific processes in the database //Close(); } private void CancelButton_Click(object sender, RoutedEventArgs e) { // However, if we cancel the edits, then we can safely revert the items back to their original (loaded) state foreach (var item in _items) item.CancelChanges(); foreach(var page in Editor.Pages) { page.Cancel(); } OnCancel?.Invoke(); //OKButton.IsEnabled = false; //CancelButton.IsEnabled = false; //Close(); } public void SaveItem(CancelEventArgs e) { if(!_validated && !Validate()) { e.Cancel = true; return; } OnSaveItem?.Invoke(this, e); _validated = false; } public bool TryFindEditor(string columnname, [NotNullWhen(true)] out IDynamicEditorControl? editor) { return Editor.TryFindEditor(columnname, out editor); } public object? GetEditorValue(string columnName) => this.FindEditor(columnName).GetValue(columnName); public void SetEditorValue(string columnName, object? value) => this.FindEditor(columnName).SetValue(columnName, value); public void SetLayoutType() where T : DynamicEditorGridLayout => Editor.SetLayoutType(); public void SetLayoutType(Type t) => Editor.SetLayoutType(t); public void SetLayout(DynamicEditorGridLayout layout) => Editor.SetLayout(layout); private void Editor_OnSelectPage(DynamicEditorGrid sender, BaseObject[] items) { OnSelectPage?.Invoke(sender, items); } private void Editor_OnUnloadPage(IDynamicEditorPage page, bool saved) { if (!saved) page.BeforeSave(Items.First()); else page.AfterSave(Items.First()); } private Dictionary? Editor_OnAfterEditorValueChanged(DynamicEditorGrid sender, AfterEditorValueChangedArgs args) { if(args.ChangedValues.Count > 0) { DoChanged(); } return OnAfterEditorValueChanged?.Invoke(sender, args); } private void Editor_OnReconfigureEditors(DynamicEditorGrid sender) { OnReconfigureEditors?.Invoke(sender); } private void Editor_OnGridCustomiseEditor(DynamicEditorGrid sender, DynamicGridColumn column, BaseEditor editor) { OnFormCustomiseEditor?.Invoke(this, Items, column, editor); } private decimal Editor_OnGetSequence(DynamicGridColumn column) { if (_items.Any()) return CoreUtils.GetPropertySequence(_items.First().GetType(), column.ColumnName); return 0.0M; } private void Editor_OnDefineLookups(ILookupEditorControl editor) { OnDefineLookups?.Invoke(editor); } private BaseObject[] Editor_GetItems() { return _items; } private BaseEditor Editor_OnGetEditor(DynamicGridColumn column) { if (_items != null && _items.Any()) { var property = DatabaseSchema.Property(Editor.UnderlyingType, column.ColumnName); if (property == null) return new NullEditor(); if (property.Editor is NullEditor) return property.Editor.CloneEditor(); BaseEditor editor; if (property is CustomProperty) { editor = property.Editor.CloneEditor(); } else { editor = OnDefineEditor?.Invoke(_items[0], column) ?? column.Editor.CloneEditor(); var propEditor = property.Editor; editor.Page = propEditor.Page; editor.Caption = propEditor.Caption; } //if (ReadOnly && editor.Editable.IsEditable()) // editor.Editable = Editable.Disabled; return editor; } return new NullEditor(); } private object? Editor_OnGetPropertyValue(object sender, string column) { if (!_items.Any()) return null; object? result; try { result = CoreUtils.GetPropertyValue(_items.First(), column); } catch { result = _items.First().UserProperties.ContainsKey(column) ? _items.First().UserProperties[column] : null; } if (result == null) return null; foreach (var _item in _items) { object? curvalue; try { curvalue = CoreUtils.GetPropertyValue(_item, column); } catch { curvalue = _item.UserProperties.ContainsKey(column) ? _item.UserProperties[column] : null; } if (curvalue == null) return null; if (!curvalue.Equals(result)) return null; } return result; } private void Editor_OnSetPropertyValue(object sender, string column, object value) { foreach (var _item in _items) if (_item.UserProperties.ContainsKey(column)) _item.UserProperties[column] = value; else CoreUtils.SetPropertyValue(_item, column, value); } //public void EditLayout() => Editor.EditLayout(); //public void ResetLayout() => Editor.ResetLayout(); public void AddButton(Button button) { Buttons.Children.Add(button); } private Dictionary Editor_OnEditorValueChanged(DynamicEditorGrid sender, string name, object value) { return EditorValueChanged(this, name, value); } private void Editor_OnReload(DynamicEditorGrid sender) { OnReload?.Invoke(this); } private void Editor_OnCreateEditorControl(string column, BaseEditor editor, IDynamicEditorControl control) { OnCreateEditorControl?.Invoke(column, editor, control); } } }