【C#】フォーカスの遷移順にコントロールを取得する
個人的に、画面に貼り付けられている全コントロールを、フォーカス遷移順に取得したかった。
調べまくった。
参考サイト
コントロールのTabIndex順で処理したいことってあるよね。階層のTabIndexを表現してみよう。 - Bug Catharsis
ここに記されている方法は、SplitContainerが絡むと、希望する結果にはならない。
試していないけど、結局、階層関係が絡んでくるとダメな気がする。
解決策
結局、APIなんて使わずに自前で取得していった方が確実な気がしたので、それで解決した。
いちいちめんどくさいロジックにもならない。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace TabFocusTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button3_Click(object sender, EventArgs e) { var controls = this.GetAllControls(); foreach (var control in controls) { Console.WriteLine(control.Name); } } /// <summary> /// フォームに存在するすべてのコントロールをフォーカス遷移順に取得します。 /// </summary> /// <param name="excludeInvisible">true:非表示項目を取得しない, false:非表示項目を取得する</param> /// <param name="excludeDisable">true:無効項目を取得しない, false:無効項目を取得する</param> /// <returns>コントロールリスト</returns> public List<Control> GetAllControls(bool excludeInvisible = true, bool excludeDisable = true) { List<Control> allControls; getAllControls(this, this, excludeInvisible, excludeDisable, out allControls); return allControls; } /// <summary> /// フォーム上のすべてのコントロールを取得する。 /// </summary> /// <param name="targetControl">次のコントロールを取得する対象オブジェクト</param> /// <param name="currentControl">手前のコントロールオブジェクト</param> /// <param name="excludeInvisible">true:非表示項目を取得しない, false:非表示項目を取得する</param> /// <param name="excludeDisable">true:無効項目を取得しない, false:無効項目を取得する</param> /// <param name="allControls">取得したすべてのコントロールオブジェクト</param> /// <returns></returns> private Control getAllControls(Control targetControl, Control currentControl, bool excludeInvisible, bool excludeDisable, out List<Control> allControls) { allControls = new List<Control>(); while (true) { var nextControl = targetControl.GetNextControl(currentControl, true); if (nextControl == null) { return currentControl; } if (excludeInvisible && !nextControl.Visible) { currentControl = nextControl; continue; } if (excludeDisable && !nextControl.Enabled) { currentControl = nextControl; continue; } allControls.Add(nextControl); if (nextControl.Controls.Count > 0) { var innerControls = new List<Control>(); nextControl = getAllControls(nextControl, nextControl, excludeInvisible, excludeDisable, out innerControls); if (innerControls.Count > 0) { allControls.AddRange(innerControls); } } currentControl = nextControl; } } } }
Form.GetAllControls()を呼べば、それで済む。
GetNextControl()メソッドを使っているので、恐らく問題ない。
Visibled, Enabledプロパティの影響もあるので、選択できるように。(デフォルトは、操作が可能なコントロールのみ)
MenuStripだのDataGridViewだののItemsプロパティとかで管理されてる中身は取ってこない。
SplitContainer.Panel1オブジェクトとかも取得するので、取得した結果、どうこうするのは呼び元の制御のお話。
あと、再帰の関係でエレガントさに欠けるけど、まぁ別にいいや。
時間があったらもうちょっと考え直すレベル。