using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using InABox.Configuration; using InABox.Core; using InABox.DynamicGrid; namespace InABox.Wpf; public abstract class MasterDetailPanel : MasterDetailPanel, IPanel where TMaster : Entity, IRemotable, IPersistent, new() where TMasterGrid : DynamicDataGrid, new() where TSettings : BaseObject, IUserConfigurationSettings, IMasterDetailSettings, new() { private DispatcherTimer? timer; private readonly List> _pages = new(); private DateTime _lastselection = DateTime.MaxValue; private int _currentPage = -1; private readonly TMasterGrid _masterGrid; private IDataModelSource? _modelsource; protected TSettings Settings { get; private set; } = new(); private void LoadSettings() { Settings = new UserConfiguration().Load(); _splitPanel.AnchorWidth = Settings.AnchorWidth; _splitPanel.View = Settings.ViewType; AfterLoadSettings(Settings); } protected void SaveSettings() { BeforeSaveSettings(Settings); Settings.AnchorWidth = _splitPanel.AnchorWidth; Settings.ViewType = _splitPanel.View; Settings.MasterID = _masterGrid.SelectedRows.FirstOrDefault()?.Get(x => x.ID) ?? Guid.Empty; new UserConfiguration().Save(Settings); } protected abstract void CreatePages(); protected abstract void AfterLoadSettings(TSettings settings); protected abstract void BeforeSaveSettings(TSettings settings); protected abstract string MasterColumnsTag { get; } public virtual string SectionName => SelectedPage?.DataModelSource()?.SectionName ?? MasterColumnsTag; public virtual void CreateToolbarButtons(IPanelHost host) { SelectedPage?.CreateToolbarButtons(host); } protected override void DoSplitPanelChanged() { SaveSettings(); var newTag = GetMasterColumnsTag(); if (!String.Equals(_masterGrid.ColumnsTag, newTag)) { _masterGrid.ColumnsTag = newTag; _masterGrid.Refresh(true, true); } } private string GetMasterColumnsTag() { var newTag = Settings.ViewType == DynamicSplitPanelView.Master ? $"{MasterColumnsTag}:Master" : $"{MasterColumnsTag}:Combined"; return newTag; } protected MasterDetailPanel() { _masterGrid = new TMasterGrid(); _masterGrid.OnSelectItem += MasterGrid_OnSelectItem; _masterGrid.BeforeRefresh += MasterGrid_BeforeRefresh; _masterGrid.AfterRefresh += MasterGrid_AfterRefresh; } public void Setup() { LoadSettings(); _masterGrid.ColumnsTag = GetMasterColumnsTag(); _splitPanel.Master = _masterGrid; _splitPanel.MasterCaption = MasterCaption; _splitPanel.DetailCaption = DetailCaption; _header.Content = MasterCaption; CreatePages(); timer = new DispatcherTimer(); timer.Tick += Timer_Tick; timer.Interval = new TimeSpan(0, 0, 0, 0, 100); } protected TPage? CreatePage(Func isAllowed, string caption) where TPage : class, IMasterDetailPage { if (isAllowed()) { var header = new DynamicTabItem() { Header = caption }; _tabControl.Items.Add(header); if (Activator.CreateInstance(typeof(TPage), header) is TPage page) { _pages.Add(page); page.OnUpdateDataModel += Page_OnUpdateDataModel; return page; } } return null; } private void Page_OnUpdateDataModel(string section, DataModel model) { OnUpdateDataModel?.Invoke(section, model); } protected TPage? CreatePage(string caption) where TPage : class, IMasterDetailPage { return CreatePage(() => true, caption); } public void Shutdown(CancelEventArgs? cancel) { foreach(var page in _pages) { page.Shutdown(cancel); if (cancel?.Cancel == true) return; } if (timer != null) timer.IsEnabled = false; } public void Refresh() { _masterGrid.Refresh(timer?.IsEnabled == false, true); _lastselection = DateTime.MinValue; if (timer != null) timer.IsEnabled = true; } public DataModel DataModel(Selection selection) { if (_modelsource == null) { var row = _masterGrid.SelectedRows.FirstOrDefault(); var filter = row != null ? new Filter(x => x.ID).IsEqualTo(row.Get(x => x.ID)) : new Filter().None(); return new AutoDataModel(filter); } return _modelsource.DataModel(selection); } public event DataModelUpdateEvent? OnUpdateDataModel; public bool IsReady { get; set; } protected IMasterDetailPage? SelectedPage => _pages.FirstOrDefault(x => x.Tab == _tabControl.SelectedTab); public Dictionary Selected() { return SelectedPage != null ? SelectedPage.Selected() : new Dictionary { { typeof(TMaster).EntityName(), _masterGrid.SelectedRows } }; } public void Heartbeat(TimeSpan time) { SelectedPage?.Heartbeat(time); } protected override void DoPagesChanged() { _lastselection = DateTime.MinValue; } private void Timer_Tick(object? sender, EventArgs e) { if (_lastselection < DateTime.Now.AddMilliseconds(-500) && !_masterGridRefreshing) { _lastselection = DateTime.MaxValue; var master = _masterGrid.SelectedRows.FirstOrDefault()?.ToObject() ?? new TMaster(); var dataModelSource = SelectedPage?.Refresh(master); if (_tabControl.SelectedIndex != _currentPage) { if(dataModelSource is not null) { _modelsource = dataModelSource; OnUpdateDataModel?.Invoke(dataModelSource.SectionName, dataModelSource.DataModel(Selection.None)); } _currentPage = _tabControl.SelectedIndex; } } } private bool _masterGridRefreshing; private void MasterGrid_BeforeRefresh(object sender, BeforeRefreshEventArgs args) { _masterGridRefreshing = true; } private void MasterGrid_AfterRefresh(object sender, AfterRefreshEventArgs args) { _masterGrid.SelectedRows = Settings.MasterID == Guid.Empty ? Array.Empty() : _masterGrid.Data.Rows.Where(r => r.Get(c => c.ID) == Settings.MasterID).ToArray(); _masterGridRefreshing = false; } private void MasterGrid_OnSelectItem(object sender, DynamicGridSelectionEventArgs e) { if (!_masterGridRefreshing) { _lastselection = DateTime.Now; SaveSettings(); } } } public abstract class MasterDetailPanel : UserControl { protected DynamicSplitPanel _splitPanel; protected Label _header; protected DynamicTabControl _tabControl; protected abstract string MasterCaption { get; } protected abstract string DetailCaption { get; } protected MasterDetailPanel() { _splitPanel = new DynamicSplitPanel { View = DynamicSplitPanelView.Combined, AnchorWidth = 300, MasterCaption = MasterCaption, DetailCaption = DetailCaption }; _splitPanel.OnChanged += SplitPanel_OnChanged; _header = new Label { HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center, VerticalContentAlignment = System.Windows.VerticalAlignment.Center, }; _splitPanel.Header = new Border { BorderBrush = new SolidColorBrush(Colors.Gray), BorderThickness = new System.Windows.Thickness(0.75), Background = new SolidColorBrush(Colors.WhiteSmoke), Height = 25, Child = _header }; _tabControl = new DynamicTabControl(); Grid.SetColumn(_tabControl, 2); Grid.SetRow(_tabControl, 0); Grid.SetRowSpan(_tabControl, 2); _tabControl.SelectionChanged += Pages_OnChanged; _tabControl.SeparatorMargin = 4; _splitPanel.Detail = _tabControl; Content = _splitPanel; } protected abstract void DoSplitPanelChanged(); private void SplitPanel_OnChanged(object sender, DynamicSplitPanelSettings e) { DoSplitPanelChanged(); } protected abstract void DoPagesChanged(); private void Pages_OnChanged(object sender, SelectionChangedEventArgs e) { if (Equals(e.Source, _tabControl)) DoPagesChanged(); } }