UserControls/AbstractBaseMasterDgvControl.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using ChatworkBulkSender.Utils;

namespace ChatworkBulkSender.UserControls
{
    /// <summary>
    /// マスタ画面DGVコントロールの基底クラス。
    /// 検索・更新・ソートの機能を提供する。
    /// </summary>
    /// <typeparam name="TDto"></typeparam>
    /// <typeparam name="TDao"></typeparam>
    /// <typeparam name="TForm"></typeparam>

    [DesignerCategory("")] // デザイナーで表示しない
    public abstract class AbstractBaseMasterDgvControl<TDto, TDao, TForm> : AbstractUserControl
        where TDto : class
        where TDao : class, new()
        where TForm : Form
    {
        private IContainer components = null;

        // Dgv
        protected DataGridView _dgv = null;

        // データリスト
        protected List<TDto> _dataList = null;

        // ソート可能なリスト
        protected SortableBindingListUtil<TDto> _sortableList = null;

        // 編集フォーム
        protected TForm _individualEditForm = null;

        // 検索条件
        protected Dictionary<string, object> _searchCriteria = new Dictionary<string, object>();

        // 編集ボタン列索引
        protected abstract int EditButtonColumnIndex { get; }

        // ソート可能な列数の最大索引
        protected abstract int SortableColumnMaxIndex { get; }

        // 初期ソートの列名
        protected abstract string InitialSortColumnName { get; }

        // 最終更新時刻
        private DateTime? _lastLoadTime = null;

        /// <summary>
        /// 選択行の取得
        /// </summary>
        /// <returns></returns>
        public TDto GetSelectedItem()
        {
            if (_dgv.CurrentRow != null)
            {
                return _dgv.CurrentRow.DataBoundItem as TDto;
            }
            // 選択行がない場合、nullを返す
            return null;
        }

        /// <summary>
        /// セルの選択をクリア
        /// </summary>
        public virtual void ClearSelection()
        {
            if (_dgv != null)
            {
                _dgv.ClearSelection();
                _dgv.CurrentCell = null;

            }
        }

        protected AbstractBaseMasterDgvControl()
        {
            // InitializeComponent();
            // 派生クラスで呼び出し
        }

        /// <summary>
        /// 継承先で実装。
        /// 顧客データを取得する。
        /// </summary>
        /// <returns></returns>
        protected abstract List<TDto> GetDataFromDao();

        /// <summary>
        /// 検索条件を取得し、検索を行う。
        /// </summary>
        /// <param name="criteria"></param>
        public virtual void Search(Dictionary<string, object> criteria)
        {
            _searchCriteria = criteria ?? new Dictionary<string, object>();
            // ソート可能なリストを再作成する
            _sortableList = new SortableBindingListUtil<TDto>(GetDataFromDao());
            _dgv.DataSource = _sortableList;
        }

        protected virtual void LoadData()
        {
            try 
            {
                // データソースが設定されているか
                if (_dgv.DataSource != null)
                {
                    // 最新のデータリストを取得
                    _dataList = GetDataFromDao();

                    // 取得後、現在時刻に更新する
                    _lastLoadTime = DateTime.Now;

                    // ソート可能なリストを再作成する
                    _sortableList = new SortableBindingListUtil<TDto>(_dataList);

                    // 初期ソートを設定
                    if (!string.IsNullOrEmpty(InitialSortColumnName))
                    {
                        SetUpSortColumn(InitialSortColumnName);
                    }

                    _dgv.DataSource = _sortableList;
                }
                else
                {
                    // 初回以降のロード時はソート状態を保持する
                    RefreshData();
                }
            }
            catch(Exception ex)
            {
                MessageBoxUtil.ShowErr($"データの読み込み中にエラーが発生しました。\n\n{ex.Message}", "エラー");
            }
        }

        /// <summary>
        /// 最新のデータを取得した後、最終更新日時を現在時刻に設定する。
        /// </summary>
        protected virtual void RefreshData()
        {
            DataGridViewRefresherUtil.RefreshWithState<TDto>(
                _dgv,
                () => GetDataFromDao(),
                (list) =>
                {
                    _dataList = list;
                    _lastLoadTime = DateTime.Now;
                    _sortableList = new SortableBindingListUtil<TDto>(list);
                    return _sortableList;
                }
            );
        }

        protected virtual bool IsEditFormOpen()
        {
            return _individualEditForm != null &&
                    !_individualEditForm.IsDisposed &&
                        _individualEditForm.Visible;
        }

        protected abstract bool HasUpdateAfter(DateTime? lastLoadTime);

        protected virtual void InitializeDgv()
        {
            if (_dgv == null) { return; }

            // 列の自動生成を行わない
            _dgv.AutoGenerateColumns = false;
            // 行ヘッダ非表示
            _dgv.RowHeadersVisible = false;
            // ユーザーによる行の高さ変更を禁止
            _dgv.AllowUserToResizeRows = false;
            // 編集不可
            _dgv.ReadOnly = true;
            // 行単位で選択する形式とする
            _dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            // 複数選択不可
            _dgv.MultiSelect = false;

            _dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;

            _dgv.CellClick += Dgv_CellClick;

            _dgv.ColumnHeaderMouseDoubleClick += Dgv_ColumnHeaderMouseDoubleClick;
        }

        protected virtual void Dgv_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            // ヘッダー行 または 編集セル列出ない場合はスキップ
            if (e.RowIndex < 0 || e.ColumnIndex != EditButtonColumnIndex) { return; }

            var selectedItem = _dgv.Rows[e.RowIndex].DataBoundItem as TDto;

            if (selectedItem == null)
            {
                MessageBoxUtil.ShowErr("選択されたデータが取得できませんでした。","エラー");
                return;
            }

            ShowEditForm(selectedItem);
        }

        /// <summary>
        /// 編集フォームを表示する。
        /// 継承先で実装。
        /// </summary>
        /// <param name="selectedItem"></param>
        protected abstract void ShowEditForm(TDto selectedItem);

        protected virtual void Dgv_ColumnHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.RowIndex == -1 && e.ColumnIndex >= 0 && e.ColumnIndex <= SortableColumnMaxIndex)
            {
                var propertyName = _dgv.Columns[e.ColumnIndex].DataPropertyName;

                SetUpSortColumn(propertyName);

            }
        }

        protected virtual void SetUpSortColumn(string propertyName)
        {
            if (!string.IsNullOrEmpty(propertyName))
            {
                var pd = TypeDescriptor.GetProperties(typeof(TDto))[propertyName];

                if (pd != null && _sortableList != null)
                {
                    _sortableList.ApplySort(pd);
                }
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                components?.Dispose();

                if (_individualEditForm != null && !_individualEditForm.IsDisposed)
                {
                    _individualEditForm.Close();
                    _individualEditForm.Dispose();
                }
            }
            base.Dispose(disposing);
        }
    }
}