using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using Pf2eModel; using Pf2eModel.Model.Base; using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; using Timer = System.Windows.Forms.Timer; namespace Pf2eViewer.DatabaseViewer; public partial class DatabaseViewerControl : UserControl { public readonly Pf2eDbContext Context = new(); [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int PerPage { get => _perPage; set { _perPage = value; UpdateView(); } } private int _perPage = 30; [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int Page { get => _page; set { _page = value; UpdateView(); } } private int _page = 1; [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public EntityType Type { get => _type; set { _page = 1; _type = value; UpdateView(); } } private EntityType _type; [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int Count { get; private set; } public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage)); [AllowNull] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public EntityView CurrentEntity { get; private set; } private bool _isBusy = true; private DatabaseQueryPerformer? _performer; private Timer? _typingTimer; private string _lastSearch = ""; public DatabaseViewerControl() { InitializeComponent(); BindingNavigator.BindingSource = DbBindingSource; //DataGridView.AutoGenerateColumns = true; DataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect; _isBusy = false; } // Control events private void BindingNavigator_RefreshItems(object sender, EventArgs e) { if (_isBusy) { return; } Page = (int)(BindingNavigator?.BindingSource?.Current ?? 1); UpdateView(); } private void DataGridView_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e) { if (_isBusy) { return; } UpdateEntity(); } private void SearchTextBox_Leave(object sender, EventArgs e) { if (_isBusy || SearchTextBox.Text == _lastSearch) { return; } UpdateView(); } private void SearchTextBox_TextChanged(object sender, EventArgs e) { if (_isBusy) { return; } if (_typingTimer == null) { _typingTimer = new(); _typingTimer.Interval = 3000; _typingTimer.Tick += HandleTypingTimerTimeout; } _typingTimer.Stop(); // Resets the timer _typingTimer.Tag = (sender as TextBox)!.Text; // This should be done with EventArgs _typingTimer.Start(); } private void HandleTypingTimerTimeout(object? sender, EventArgs e) { if (_isBusy || sender is not Timer timer) { return; } UpdateView(); timer.Stop(); } // Updater private void SetBusyStatus(bool busy) { if (InvokeRequired) { Invoke(SetBusyStatus, busy); return; } _isBusy = busy; BindingNavigator.Enabled = !busy; DataGridView.Enabled = !busy; SearchTextBox.Enabled = !busy; DataGridView.ColumnHeadersHeightSizeMode = !busy ? DataGridViewColumnHeadersHeightSizeMode.AutoSize : DataGridViewColumnHeadersHeightSizeMode.DisableResizing; } public void UpdateView(bool force = false) { if (_isBusy && !force) { return; } SetBusyStatus(true); _performer?.Abort(); _performer = new() { Type = Type, Page = Page, PerPage = PerPage, Search = SearchTextBox.Text, }; _performer.OnQueryCompleted += HandleQueryCompleted; _performer.Query(); } private void HandleQueryCompleted(DatabaseQueryPerformer sender, DatabaseQueryResult result) { if (sender != _performer) { return; } Count = result.Count; _lastSearch = SearchTextBox.Text; InvokeControl( BindingNavigator, b => { PagesBindingSource.DataSource = Enumerable.Range(1, MaxPage).ToList(); b.BindingSource = PagesBindingSource; } ); InvokeControl( DataGridView, view => { view.DataSource = result .Entities.Select(e => new EntityGridRow(e)) .OrderBy(r => r.Id) .ToList(); SetBusyStatus(false); if (view.Rows.Count > 0) { view.Rows[0].Selected = true; UpdateEntity(); } } ); } private void InvokeControl(T control, Action action) where T : Control { if (control.InvokeRequired) { control.Invoke(action, control); } else { action(control); } } private void UpdateEntity() { if (DataGridView.SelectedRows.Count == 0) { return; } EntityGridRow? data = (EntityGridRow?)DataGridView.SelectedRows[0].DataBoundItem; try { CurrentEntity = data is not null ? new(Context, Type, data.Id) : null; } catch (Exception) { // ignored } EntityChange?.Invoke(CurrentEntity); } // Output events public delegate void EntityChangeHandler(EntityView newEntity); public event EntityChangeHandler? EntityChange; internal class EntityGridRow(Entity e) { public int Id { get; set; } = e.Id; public string Name { get; set; } = e.Name[LocaleType.English]; public EntityRarity Rarity { get; set; } = e.Rarity; } }