RunningCSharp

MS系開発者による、雑多な記事。記事は所属企業とは関係のない、個人の見解です。

WPF:タブ操作時は選択チェックボックスのみ遷移、矢印キー操作時は通常通り非選択チェックボックスにも遷移を行うチェックボックス

    public class Att
    {
        public static bool GetSelectedRadioButtonFocus(DependencyObject obj)
        {
            return (bool)obj.GetValue(SelectedRadioButtonFocusProperty);
        }

        public static void SetSelectedRadioButtonFocus(DependencyObject obj, bool value)
        {
            obj.SetValue(SelectedRadioButtonFocusProperty, value);
        }

        // Using a DependencyProperty as the backing store for SelectedRadioButtonFocus.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedRadioButtonFocusProperty =
            DependencyProperty.RegisterAttached("SelectedRadioButtonFocus", typeof(bool), typeof(Att), new PropertyMetadata(false, OnSelectedRadioButtonFocusChanged));

        private static void OnSelectedRadioButtonFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RadioButton radio = d as RadioButton;
            if ((bool)e.NewValue)
            {
                radio.GotFocus += Radio_GotFocus;
            }
        }

        private static void Radio_GotFocus(object sender, RoutedEventArgs e)
        {
            RadioButton radio = sender as RadioButton;
            //チェックされているボタン
            if (radio.IsChecked.Value)
            {
                //チェックされていたら、そこでストップ
                return;
            }
            if (Keyboard.IsKeyDown(Key.Tab) 
                && (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)))
            {
                //逆方向(Shift+Tab)操作時
                var checkedradio = SearchBackCheckedRadio(radio);
                if (checkedradio != null)
                {
                    checkedradio.Focus();
                }
                else
                {
                    //チェックされた同グループのラジオボタンが進行方向にない場合、次のアイテムへ移動
                    var firstitem = GetFirstGroupItem(radio);
                    firstitem.Focus();
                    TraversalRequest prev = new TraversalRequest(FocusNavigationDirection.Previous);
                    firstitem.MoveFocus(prev);
                }
            }
            else if(Keyboard.IsKeyDown(Key.Tab))
            {
                //順方向(Tab)操作時
                var checkedradio = SearchCheckedRadio(radio);
                if (checkedradio != null)
                {
                    checkedradio.Focus();
                }
                else
                {
                    //チェックされた同グループのラジオボタンが進行方向にない場合、次のアイテムへ移動
                    var lastitem = GetLastGroupItem(radio);
                    lastitem.Focus();
                    TraversalRequest next = new TraversalRequest(FocusNavigationDirection.Next);
                    lastitem.MoveFocus(next);
                }
            }
        }

        private static RadioButton GetLastGroupItem(RadioButton radio)
        {
            RadioButton rtn = null;
            var parent = VisualTreeHelper.GetParent(radio);

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var item = VisualTreeHelper.GetChild(parent, i);
                if (item is RadioButton)
                {
                    rtn = item as RadioButton;
                }
            }
            return rtn;
        }

        private static RadioButton GetFirstGroupItem(RadioButton radio)
        {
            var parent = VisualTreeHelper.GetParent(radio);

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var item = VisualTreeHelper.GetChild(parent, i);
                if (item is RadioButton)
                {
                    return (item as RadioButton);
                }
            }
            return null;
        }


        private static RadioButton SearchCheckedRadio(RadioButton radio)
        {
            // 一階層親を取得
            var parent = VisualTreeHelper.GetParent(radio);
            RadioButton checkedradio = null;
            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var item = VisualTreeHelper.GetChild(parent, i);
                if (item is RadioButton && (item as RadioButton).IsChecked.Value)
                {
                    checkedradio = item as RadioButton;
                }
                if (item == radio && checkedradio != null && item != checkedradio)
                {
                    //呼び出し元がチェックされているラジオボタンより後ろにある場合、
                    //値を空にする
                    checkedradio = null;
                }
            }
            return checkedradio;
        }

        private static RadioButton SearchBackCheckedRadio(RadioButton radio)
        {
            // 一階層親を取得
            var parent = VisualTreeHelper.GetParent(radio);
            RadioButton checkedradio = null;
            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = childrenCount - 1; i >= 0; i--)
            {
                var item = VisualTreeHelper.GetChild(parent, i);
                if (item is RadioButton && (item as RadioButton).IsChecked.Value)
                {
                    checkedradio = item as RadioButton;
                }
                if (item == radio && checkedradio != null && item != checkedradio)
                {
                    //呼び出し元がチェックされているラジオボタンより前にある場合、
                    //値を空にする
                    checkedradio = null;
                }
            }
            return checkedradio;
        }

    }

添付プロパティとして作成したため、使用時は

<RadioButton GroupName="1" Content="aaaa" IsChecked="True" local:Att.SelectedRadioButtonFocus="True" />
<RadioButton GroupName="1" Content="bbbb"  local:Att.SelectedRadioButtonFocus="True"/>
<RadioButton GroupName="1" Content="cccc"  local:Att.SelectedRadioButtonFocus="True"/>
<RadioButton GroupName="1" Content="dddd"  local:Att.SelectedRadioButtonFocus="True"/>

上記のような形で使用します。