オルタナティブ・ブログ > IT's my business >

IT業界のコメントマニアが始めるブログ。いつまで続くのか?

(XAML#02)「[Alt]なしのショートカット動作を無効化する」

»

Xaml2

WPF では、コントロールに文字列を割り当てるときに、アルファベットの直前にアンダーバー(_)をつけることで、そのコントロールのショートカット文字を指定できます。たとえば、次のようにボタンを定義すると、ボタンにフォーカスがない場合でも [Alt]+[A] で直接ボタンの機能を呼び出すことができます。

<Button Content="_Alpha">

WPF のショートカット機能は、入力フォーカスのあるコントロールが通常のキー入力を処理してしまうのでなければ、 [Alt] キーを押さなくても呼び出せます。つまり、上記のボタンは [A] を押すだけでも呼び出せます。もちろん、入力フォーカスが、テキストボックスのように普通のキー入力を処理するコントロールにある場合は、そのコントロールの入力として扱われます。

WPF では、重複してショートカットが定義されていた場合に直接どちらかを呼び出す代わりに、重複するものを順に選択する機能があります。たとえば、次のように定義すると、[Alt]+[A] または [A] を押すたびにコントロールの選択が切り替えられます。

<Button Content="_Alpha">
<Button Content="_Action">
<Button Content="_Ace">

しかし、従来の Windows フォームでは、[Alt] を押さずに英字キーを押してもショートカットとしては使われませんでした。ユーザーは「[Alt] を押していないのに」機能が呼び出されてしまった(おかしな動作)と感じてしまうかもしれません。

この動作を抑止するには、AccessKeyPressed というイベント(AccessKeyManager の添付イベント)で [Alt] キーが押されていないことを判断して、機能を無効化すればよいのですが、ウィンドウごとに実装するのは効率的ではありません。そこで、特定のルーティングイベントを処理できるようにする EventManager.RegisterClassHandler を使います。この呼び出しは、アプリケーション起動後に一度だけ行えばよいので、メインウィンドウの初期化処理中(コンストラクタ)、あるいはアプリケーションの起動時(App クラスの OnStartup メソッド)で行うとよいでしょう。

■メインウィンドウで実装する場合

using System.Windows.Input; // この文を追加すること

...

public MainWindow()

{
    InitializeComponent();

    // OnAccessKeyPressed をクラスイベントハンドラとして登録する
    EventManager.RegisterClassHandler(typeof(UIElement),
            AccessKeyManager.AccessKeyPressedEvent,
            new AccessKeyPressedEventHandler(OnAccessKeyPressed));
}

/// <summary>
/// [Alt] を使わないときのショートカット呼び出しを無効化する
/// </summary>
private static void OnAccessKeyPressed(object sender, AccessKeyPressedEventArgs e)
{
    if (Keyboard.IsKeyDown(Key.Enter) || Keyboard.IsKeyDown(Key.Escape)) return;
    if (!e.Handled && e.Scope == null && e.Target == null)
    {
        if ((Keyboard.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt)
        {
            e.Target = null;
            e.Handled = true;
         }
    }
}

■App.xaml.cs で実装する場合

using System.Windows.Input; // この文を追加すること

...

namespace WpfApplication1
{
    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            // OnAccessKeyPressed をクラスイベントハンドラとして登録する
            EventManager.RegisterClassHandler(typeof(UIElement),
                    AccessKeyManager.AccessKeyPressedEvent,
                    new AccessKeyPressedEventHandler(OnAccessKeyPressed));
        }

        /// <summary>
        /// [Alt] を使わないときのショートカット呼び出すを無効化する
        /// </summary>
        private static void OnAccessKeyPressed(object sender, AccessKeyPressedEventArgs e)
        {
            if (Keyboard.IsKeyDown(Key.Enter) || Keyboard.IsKeyDown(Key.Escape)) return;
            if (!e.Handled && e.Scope == null && e.Target == null)
            {
                if ((Keyboard.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt)
               {
                    e.Target = null;
                    e.Handled = true;
               }
            }
        }
    }
}

Comment(0)