Top / .NET備忘録 / 07.Form / 04.TabIndex

考え方

アップグレードウィザードは、TabIndex をそのままの値でコンバートしてくれているので、そのとおりに動かせばよいです。

TabIndex 順にコントロールを格納するリストを用意します。

Form にコントロールか追加されたら、コントロールをリストに追加します。

TabIndex を変更したときのために、TabIndexChanged イベントを登録しておき、TabIndex が変更されたら、それより後ろの TabIndex を塗り替えるようにします。

フォーカスの初期位置は、作成されたリストの先頭から探していくことにします。

Tab キーが押されたら、リストから ActiveControl を探し、次の位置へ移動します。

コントロールが Form から削除されたら、リストからの削除とイベント解除を行います。

リストを作成

 private List<Control> m_TabIndexList = new List<Control>();
 
// コントロールが追加されるとき
 private void ControlAddedEvent(object sender, ControlEventArgs e)
 {
     EnumRegistTabIndex(e.Control);          // TabIndex 順にリストへ追加
 }
 
 private void EnumRegistTabIndex(Control con)
 {
     RegistTabIndex(con, false);
     foreach (Control child in con.Controls)
     {
         EnumRegistTabIndex(child);
     }
 }
 
 private void RegistTabIndex(Control con, bool renumber)
 {
     con.TabIndexChanged += OnChildTabIndexChanged;
     con.ControlAdded += ControlAddedEvent;
     con.ControlRemoved += ControlRemovedEvent;
 
     int start = 0;
     int end = m_TabIndexList.Count - 1;
     int current = con.TabIndex;
 
     while ((end - start) > 1)
     {
         int m = (start + end) / 2;
         int mIndex = m_TabIndexList[m].TabIndex;
         if (current > mIndex)
             start = m;
         else 
             end = m;
     }
 
     for (int i = start; i <= end; i++)
     {
         if (m_TabIndexList[i].TabIndex >= current)
         {
             m_TabIndexList.Insert(i, con);
             if (renumber)
             {
                 int newTabIndex = current + 1;
                 for (int j = i + 1; j < m_TabIndexList.Count; j++)
                 {
                     m_TabIndexList[j].TabIndex = newTabIndex;
                     newTabIndex = newTabIndex + 1;
                 }
             }
             return;
         }
     }
     m_TabIndexList.Add(con);
 }

TabIndex が変更されたとき

TabIndex を塗り替えるときに、イベントが連鎖しないようにしておきます。

 private bool m_EnterOnChildTabIndexChanged = false;
 
 private void OnChildTabIndexChanged(object sender, EventArgs e)
 {
     if (m_EnterOnChildTabIndexChanged) return;
     m_EnterOnChildTabIndexChanged = true;
     
     Control con = (Control)sender;
 
     RemoveTabIndex(con);
     RegistTabIndex(con, true);
 
     m_EnterOnChildTabIndexChanged = false;
 }
 
 private void RemoveTabIndex(Control con)
 {
     if (m_TabIndexList.Contains(con)
     {
         con.TabIndexChanged -= OnChildTabIndexChanged;
         con.ControlAdded -= ControlAddedEvent;
         con.ControlRemoved -= ControlRemovedEvent;
         m_TabIndexList.Remove(con); 
     }
 }

フォーカスの初期位置

フォーカスの初期位置は、WM_SHOWWINDOW をキャッチしてセットします。

const int WM_SHOWWINDOW = 0x0018;

protected override void WndProc(ref Message m) {
    switch (m.Msg) {
        case WM_SHOWWINDOW:
            base.WndProc(ref m);
            if (m.WParam != IntPtr.Zero) {
                if (this.ActiveControl == null && !this.DesignMode) {
                    this.ActiveControl = GetNextTabIndexFocus(null, true, true);
                }
            }
            break;
        default:
            base.WndProc(ref m);
            break;
    }
}

現在のコントロールから、次に移動すべきコントロールを探す部分です。
前方移動、後方移動に対応しています。

 public Control GetNextTabIndexFocus(Control con, bool forward, bool tabStopOnly)
 {
     Control start = GetNextTabIndexControl(con, forward);
     while (start != null)
     {
         if (start.CanFocus && start.CanSelect && (!tabStopOnly || start.TabStop))
             return start;
         start = GetNextTabIndexControl(start, forward);
     };
 
     if (con == null)
         return null;
     else
         return GetNextTabIndexFocus(null, forward, tabStopOnly);
 }
 
 public Control GetNextTabIndexControl(Control con, bool forward)
 {
     int nextIndex;
     if (con == null)
         nextIndex = forward ? -1 : m_TabIndexList.Count;
     else
         nextIndex = m_TabIndexList.IndexOf(con);
 
     if (forward)
     {
         nextIndex++;
         if (nextIndex < m_TabIndexList.Count)
             return m_TabIndexList[nextIndex];
         else
             return null;
     }
     else
     {
         nextIndex--;
         if (nextIndex >= 0)
             return m_TabIndexList[nextIndex];
         else
             return null;
     }
 }

TAB キーが押されたとき

 protected override bool ProcessTabKey(bool forward)
 {
     Control nextControl = GetNextTabIndexFocus(this.ActiveControl, forward, true);
     if (nextControl != null)
     {
         this.ActiveControl = nextControl;
         return true;
     }
     return base.ProcessTabKey(forward);
 }

コントロールが削除されたとき

 private void ControlRemovedEvent(object sender, ControlEventArgs e)
 {
     EnumRemoveTabIndex(e.Control);          // TabIndex 順リストから削除
 }
 
 private void EnumRemoveTabIndex(Control con)
 {
     RemoveTabIndex(con);
     foreach (Control child in con.Controls)
     {
         EnumRemoveTabIndex(child);
     }
 }

 



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   最終更新のRSS
Last-modified: 2013-03-21 (木) 22:07:28 (1556d)