DeskTopで、自作ComboBox!

Desktopアプリ開発で、一番悩むのが、dataGridView関係とcomboBoxかと思います。今回は、コンボボックスの一例を紹介したいと思います。WinFormsには、有料のコンポーネントが数多くありますが、結構なお値段なのと、私の長年の経験として、意外とバグがあったりして、それを補完しながらの開発も結構、工数が増えます。

今回は、コンボボックスについて1パターンを紹介したいと思います。3列のコンボボックスで、データをList<DataCombo>形式で渡して、戻り値として、選択したID(1列目)とNAME(二列目)を取得するユーザーコントロールを作りました。(下記イメージ)

CustomCombo.cs (ユーザーコントロール)クラスを一つ作成します。

↓ユーザーコントロール(コンボボックス)のClassソース

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace WinForms01
{
    /// --------------------------------------------------------------------------------
    /// <summary>
    /// 作成: モバイルステーション 2024.02.23 M.SAGARA
    /// </summary>
    /// --------------------------------------------------------------------------------
    public partial class CustomCombo : UserControl
    {
        private System.Windows.Forms.TextBox textBox;
        private System.Windows.Forms.Button dropDownButton;
        private Form dropDownForm;
        private DataGridView dataGridView;

        public string sNAME = "";

        // カスタムイベントの定義
        public event EventHandler TextBoxTextChanged;

        /// --------------------------------------------------------------------------------
        /// <summary>
        /// コンストラクター
        /// </summary>
        /// --------------------------------------------------------------------------------
        public CustomCombo()
        {
            InitializeComponent();
            InitializeCustomComboBox();
            InitializeDropDownFormWithDataGridView();
            // dataGridView.CellClickイベントを購読
            this.dataGridView.CellClick += MyTextBox_TextChanged;
        }

        /// --------------------------------------------------------------------------------
        /// <summary>
        /// 外部からデータを受け取るためのメソッド 
        /// ※DataCombo (Id,Name,Descripton)
        /// </summary>
        /// <param name="items"></param>
        /// --------------------------------------------------------------------------------
        public void SetData(List<DataCombo> items)
        {
            dataGridView.DataSource = items;
            dataGridView.Refresh();
        }

        // TextBoxのTextChangedイベントハンドラ
        private void MyTextBox_TextChanged(object sender, EventArgs e)
        {
            // カスタムイベントを発火
            TextBoxTextChanged?.Invoke(sender, e);
        }
        // TextBoxのテキストを外部から取得するためのプロパティ
        public string TextBoxText
        {
            get { return textBox.Text; }
            set { textBox.Text = value; }
        }

        private void InitializeCustomComboBox()
        {
            // TextBoxの設定
            textBox = new System.Windows.Forms.TextBox
            {
                Dock = DockStyle.Fill,
                ReadOnly = true
            };
            this.Controls.Add(textBox);

            // ドロップダウンボタンの設定
            dropDownButton = new System.Windows.Forms.Button
            {
                Dock = DockStyle.Right,
                Text = "▼",
                Width = 24,  // ボタンの幅を24ピクセルに設定
                Height = 24
            };
            this.Controls.Add(dropDownButton);
            dropDownButton.Click += (s, e) => ShowDropDownForm();

            // コントロールの高さ調整
            // ボタンの高さを調整する場合は、CustomComboBoxControlのサイズ調整を行う
            this.Height = textBox.Height; // コントロールの高さを調整
        }

        private void InitializeDropDownFormWithDataGridView()
        {
            // dataGridViewの設定
            dataGridView = new DataGridView
            {
                Dock = DockStyle.Fill,
                SelectionMode = DataGridViewSelectionMode.FullRowSelect,
                AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells,
                AllowUserToResizeRows = false,
                RowHeadersVisible = false, // 行ヘッダーを非表示に設定
                ReadOnly = true,
                AllowUserToAddRows = false
            };
            // DataGridViewの行のデフォルトの高さを設定
            dataGridView.RowTemplate.Height = 18;

            dropDownForm = new Form
            {
                FormBorderStyle = FormBorderStyle.None,
                StartPosition = FormStartPosition.Manual,
                Size = new Size(300, 300),  // 初期サイズ設定
                TopMost = true
            };
            dropDownForm.Controls.Add(dataGridView);
            dropDownForm.Deactivate += (s, e) => dropDownForm.Hide();
            dataGridView.CellClick += DataGridView_CellClick;
        }

        /// --------------------------------------------------------------------------------
        /// <summary>
        /// dataGridViewの部分が表示されるときの、イベント
        /// </summary>
        /// --------------------------------------------------------------------------------
        private void ShowDropDownForm()
        {
            // 現在のスクリーンの作業領域を取得(タスクバーなどを除いた領域)
            Rectangle screenWorkingArea = Screen.GetWorkingArea(this);

            // ComboBoxのスクリーン上の位置を取得
            Point comboBoxScreenLocation = this.textBox.PointToScreen(Point.Empty);

            // Formの予定表示位置(ComboBoxの直下)を計算
            int formX = comboBoxScreenLocation.X;
            int formY = comboBoxScreenLocation.Y + textBox.Height;

            // Formの予定表示位置が画面下からはみ出るかどうかを確認
            bool isBelowScreen = (formY + dropDownForm.Height) > screenWorkingArea.Bottom;

            if (dropDownForm.Visible)
            {
                dropDownForm.Hide();
            }
            else
            {
                if (isBelowScreen)
                {
                    // 画面下からはみ出る場合、FormをComboBoxの上に表示
                    formY = comboBoxScreenLocation.Y - dropDownForm.Height;
                }

                // Formの位置を設定して表示
                dropDownForm.Location = new Point(formX, formY);
                dropDownForm.Show();
            }

            // dataGridViewの列幅の計算
            int totalWidth = 0;
            foreach (DataGridViewColumn column in dataGridView.Columns)
            {
                totalWidth += column.Width;
            }
            dropDownForm.Width = totalWidth + 20;
        }   // EOF ShowDropDownForm

        /// --------------------------------------------------------------------------------
        /// <summary>
        /// コンボボックスの行選択時、イベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// --------------------------------------------------------------------------------
        private void DataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex >= 0)
            {
                if (dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value != null)
                {
                    // テキストボックスに選択値のセット
                    //textBox.Text = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
                    textBox.Text = dataGridView.Rows[e.RowIndex].Cells[0].Value.ToString();

                    sNAME = dataGridView.Rows[e.RowIndex].Cells[1].Value.ToString();
                }
                dropDownForm.Hide();
            }
        }
    }   // EOF Class
}   // EOF namespace

Form(コンボボックスを配置した)のソース

        /// --------------------------------------------------------------------------------
        /// <summary>
        /// フォーム読み込み時、イベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// --------------------------------------------------------------------------------
        private void FrmMain_Load(object sender, EventArgs e)
        {
            // コンボボックスのデータの準備
            List<DataCombo> items = new List<DataCombo>();
            for (int i = 0; i < 100; i++)
            {
                DataCombo row = new DataCombo
                {
                    Id = i.ToString("D4"),
                    Name = "Item " + i.ToString(),
                    Description = "Description " + i.ToString()
                };
                items.Add(row);
            };
            customCombo1.SetData(items);

            boLoading = false;
        }

        /// --------------------------------------------------------------------------------
        /// <summary>
        /// コンボボックスで、行選択時イベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// --------------------------------------------------------------------------------
        private void customCombo1_TextBoxTextChanged(object sender, EventArgs e)
        {
            // コンボボックスで、選択された行の、2列目の取得
            lblComboName.Text = customCombo1.sNAME;
        }

開発環境の選択について

■開発言語を学ぶ以前の問題として、開発環境として
一体どれを選べば良い? 🤔という疑問があります。

・参考に2024年02月現在、私が業務系アプリ開発で使用している主な開発環境を紹介させて頂きます。
(私が昭和生まれの Microsoft信者であることを先に言っておきます!)
 多分、下記の環境は現在のメジャーかと思います。
 細かい説明は省略しますが、目的によって、それぞれ使い分けています。

①Windows Desktop用: <–デスクトップで、レスポンス重視
 •C# .Net framework (Win Forms) <–従来のシステムサポート
 •C# .Net6(Win Forms)
②Azure Web App用: <–WEBで配布が楽
 •Asp.Net 4.8(Web Forms) <–従来のシステムサポート
 •Asp.Net Core Api(C#) サーバー側 (↓のBack-End 親
 •Blazor Web-Assembly(C#.Net)クライアント側 (↑のFront-End 子

  •Blazor Server(C#) <– (Back-End/Front-Endに分けたくない場合)
③Azure Console App用: 主に夜間バッチ処理
 •C# .Net6
④Azure SQL Database(データベース)
 •現在は、Azure SQLメイン。(その他: Oracle<-ユーザーにより)

↑言語は、現在C#.Net5~C#.Net8へ移行中。
(C#.Net8に手を出すのはまだ、少し早いかも)
.NetFramework4.8は、Windows OSにあらかじめインストールされており、まだまだ現役。ただ今後は、.Net( 旧.Net Core)が最終的に継続サポートされる。

■プログラミング歴30年続けてきて、思う事。
・開発の言語仕様、コンポーネントも毎月のようにバージョンアップしています。サポート切れにならないように、常に新しいものを試して次に繋げていかないと行けません。
・ また、⏳ 時間には限りがあるので、選んで学ばないといけません!
何でも手を出すべきではないです。
今までやってきたことを継続しながら、次に学ぶ物を決めるのが無難。
・そして大事なのは、トライエラーの繰り返しです! 😉