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

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

(XAML#11)「フォーカスの設定」

»

Windows フォームでは、ウィンドウを表示すると、通常、先頭のコントロールにフォーカスがあります。WPF では、デフォルトではそのような動作になっていません。初期フォーカスを特定のコントロールに設定したい場合、Loaded イベントハンドラーなどで明示的に指定する必要があります。たとえば、次のように記述できます。

[XAML]
<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="325" Width="525"
    Loaded="Window_Loaded">
    <StackPanel>
        <TextBox x:Name="tbox1" />
        <TextBox x:Name="tbox2" />
        <TextBox x:Name="tbox3" />
    </StackPanel>
</Window>

[コード]
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    tbox1.Focus();
}

次のように、FocusManager を使うことで、Loaded イベントを使わず XAML だけで記述することもできます。

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="325" Width="525"
    FocusManager.FocusedElement="{Binding ElementName=tbox1}">
    <StackPanel>
        <TextBox x:Name="tbox1" />
        <TextBox x:Name="tbox2" />
        <TextBox x:Name="tbox3" />
    </StackPanel>
</Window>

ウィンドウを表示するときに「最初のコントロールにフォーカスを設定する」という処理は、次のように記述することもできます。

[XAML]
<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="325" Width="525"
    Loaded="Window_Loaded">
    <StackPanel>
        <TextBox x:Name="tbox1" />
        <TextBox x:Name="tbox2" />
        <TextBox x:Name="tbox3" />
    </StackPanel>
</Window>

[コード]
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}

MoveFocus はフォーカスを移動させるためのメソッドです。このメソッドを使って、引数となる TraversalRequest オブジェクトの内容によってフォーカスを先頭/末尾/前後/上下左右に移動します。アプリケーション中の、すべてのウィンドウでこの処理を適用させたい場合は、ウィンドウごとに記述する代わりに、これまでに紹介した「スタイル」を使うことができます。ただし、ウィンドウのスタイルは明示的に割り当てる必要があります。

[App.xaml]
<Application x:Class="WpfApp1.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="MainWindow.xaml">
    <Application.Resources>
        <Style TargetType="{x:Type Window}">
            <EventSetter Event="Loaded" Handler="WindowLoaded" />
        </Style>
    </Application.Resources>
</Application>

[App.xaml.cs]
public partial class App : Application
{
    private void WindowLoaded(object sender, RoutedEventArgs e)
    {
        var wnd = sender as Window;
        if (wnd != null)
            wnd.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
    }
}

[MainWindow.xaml]
<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Style="{StaticResource ResourceKey={x:Type Window}}"
    Title="MainWindow" Height="325" Width="525">
    ...

※スタイルにキー(x:Key)を設定して、利用することもできます。

このように設定した場合、スタイルで設定した Loaded イベントハンドラーは、ウィンドウのイベントハンドラーよりも後で呼び出されます。つまり、個々のウィンドウの FocusManager.FocusedElement や Loaded イベントで初期フォーカスを設定するコントロールを指定しても、それらは Application クラスの WindowLoaded の中で上書きされてしまうということです。そのような場合は、スタイルを分けたり、ウィンドウに設定しないようにして回避してください。

Xaml11

Comment(0)