読者です 読者をやめる 読者になる 読者になる

RunningCSharp

.net中心の開発話。記事は個人の見解であり、所属組織を代表するものてはありません。

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

C# 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"/>

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