WPFと初期フォーカス


190

WPFアプリケーションの起動時には、何もフォーカスされていないようです。

これは本当に奇妙です。私が使用した他のすべてのフレームワークは、期待どおりに機能します。タブオーダーの最初のコントロールに最初のフォーカスを置きます。しかし、私はそれが私のアプリだけでなくWPFであることを確認しました-新しいウィンドウを作成し、それにTextBoxを配置してアプリを実行した場合、クリックするかTabを押すまで、TextBoxはフォーカスを持ちません。ああ。

私の実際のアプリは単なるTextBoxよりも複雑です。UserControls内にUserControlsのいくつかのレイヤーがあります。それらのUserControlsの1つにFocusable = "True"ハンドラーとKeyDown / KeyUpハンドラーがあり、ウィンドウが開いたらすぐにフォーカスを取得したいと考えています。私はまだWPFの初心者ですが、その方法を理解するのにあまり力がありません。

アプリを起動してTabキーを押すと、フォーカスがフォーカス可能なコントロールに移動し、思い通りに機能し始めます。ただし、ユーザーがウィンドウの使用を開始する前にTabキーを押す必要はありません。

私はFocusManager.FocusedElementをいじってみましたが、どのコントロールに設定するか(トップレベルのウィンドウ?フォーカス可能なコントロールを含む親?フォーカス可能なコントロール自体?)、または何に設定するかわかりません。

ウィンドウが開いたらすぐに、入れ子になったコントロールに最初のフォーカスを置くために何をする必要がありますか?あるいは、タブオーダーの最初のフォーカス可能なコントロールをフォーカスするには、

回答:


164

これも機能します:

<Window FocusManager.FocusedElement="{Binding ElementName=SomeElement}">

   <DataGrid x:Name="SomeElement">
     ...
   </DataGrid>
</Window>

4
私がこれについてコメントした最初の人であることに驚いています。それがほとんどすべてのコントロールに行くことができたので、これがどこに行ったかについて私は混乱しました。この特定の質問への回答として、ウィンドウに表示されると思いますが、msdn.microsoft.com / en-us / library / …のコメントを読んで、これを接続するコントロールがどのように重要であるかを理解できます。
Joel McBeth 14

私はこのアプローチをスタックパネルで使用して成功しています。興味がある場合は、stackoverflow.com
Julio Nobre

これは、受け入れられた回答よりもはるかにうまく機能しました。なぜなら、私は最初の後の要素に焦点を当てる必要があるからです。
プテルドボラート2017

163

Focusableプロパティが使用されている場所を確認するためにReflectorを掘り下げるという素晴らしいアイデアがあり、この解決策への道を見つけました。次のコードをWindowのコンストラクターに追加するだけです。

Loaded += (sender, e) =>
    MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

これにより、タブオーダーの最初のコントロールが自動的に選択されます。そのため、これは、任意のウィンドウにドロップしてJust Workを実行できる一般的なソリューションです。


21
これを動作に追加します。<Window FocusBehavior.FocusFirst = "true"> ... </ Window>
wekempf 2009年

6
@wekempf、私は振る舞いの概念に精通していませんでしたが、調べてみましたが、それはまったく悪い考えではありません。他の誰か(私のような)がアタッチされた動作にまだ慣れていない場合は、以下に説明があります:codeproject.com/KB/WPF/AttachedBehaviors.aspx
Joe White

1
さらに、これは、目的の要素が(深い階層でも)実際にフォーカス可能な要素を含むUserControlである場合に機能します。すごい!
Daniel Albuschat 2015年

1
すばらしいアイデアですが、フォーカスを受け入れるコントロールがである場合は機能しないことがありButtonます。これを修正するために、優先度でMoveFocusディスパッチャーを介して呼び出しを切り替えContextIdleます(Background以上は機能しません)。また、FocusNavigationDirection.First意図に一致し、この場合も同じことを行うaがあります。
Anton Tykhyy 2017

これがデフォルトの動作です。(元の投稿の)ヤックは正しいです!
NH。

61

添付された動作として実装された受け入れられた回答に基づいて:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace UI.Behaviors
{
    public static class FocusBehavior
    {
        public static readonly DependencyProperty FocusFirstProperty =
            DependencyProperty.RegisterAttached(
                "FocusFirst",
                typeof(bool),
                typeof(FocusBehavior),
                new PropertyMetadata(false, OnFocusFirstPropertyChanged));

        public static bool GetFocusFirst(Control control)
        {
            return (bool)control.GetValue(FocusFirstProperty);
        }

        public static void SetFocusFirst (Control control, bool value)
        {
            control.SetValue(FocusFirstProperty, value);
        }

        static void OnFocusFirstPropertyChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            Control control = obj as Control;
            if (control == null || !(args.NewValue is bool))
            {
                return;
            }

            if ((bool)args.NewValue)
            {
                control.Loaded += (sender, e) =>
                    control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
            }
        }
    }
}

次のように使用します。

<Window xmlns:Behaviors="clr-namespace:UI.Behaviors"
        Behaviors:FocusBehavior.FocusFirst="true">

6
私の意見では、これは私が見つけた断然最高のソリューションです。ありがとう!
Shion

1
への呼び出しのこの回答のコードにバグがありますDependencyProperty.RegisterAttached。3番目のパラメータはでtypeof(FocusBehavior)はなくtypeof(Control)です。この変更を行うと、デザイナーが「Control」エラーによってすでに登録されている「FocusFirst」プロパティを報告できなくなります。
Tony Vitabile 2016年

@TonyVitabile修正されました。できれば、いつでも自由に回答を編集および改善できます。:)
Mizipzor

アンロード中にcontrol.Loadedイベントハンドラーを登録解除しないでください。
andreapier 2016

@andreapier気にしても構いませんが、登録解除をスキップしてもメモリリークなどは発生しません。存続期間の短いオブジェクトに、存続期間の長いオブジェクトのイベントにアタッチされているメソッドがある場合にのみ、イベントがメモリリークを引き起こすことを心配する必要があります。この場合、寿命はウィンドウの寿命なので、問題ありません。
Joe White


8

「WPFの初期フォーカスの悪夢」を経験し、スタックのいくつかの回答に基づいて、以下が私にとって最良のソリューションであることが証明されました。

まず、App.xaml OnStartup()に以下を追加します。

EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent,
          new RoutedEventHandler(WindowLoaded));

次に、App.xamlにも「WindowLoaded」イベントを追加します。

void WindowLoaded(object sender, RoutedEventArgs e)
    {
        var window = e.Source as Window;
        System.Threading.Thread.Sleep(100);
        window.Dispatcher.Invoke(
        new Action(() =>
        {
            window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

        }));
    }

一部のフレームワークの競合状態により、WPFの初期フォーカスがほとんど失敗するため、スレッドの問題を使用する必要があります。

次の解決策は、アプリ全体でグローバルに使用されているため、最適でした。

それが役に立てば幸い...

オラン


5
BeginInvokeその怖いSleep(100)声明の代わりに使用してください。
l33t 2016年

8

同じ問題が簡単な解決策でそれを解決しました:メインウィンドウで:

  <Window ....
        FocusManager.FocusedElement="{Binding ElementName=usercontrolelementname}"
         ... />

ユーザーコントロールで:

private void UserControl_GotFocus_1(object sender, RoutedEventArgs e)
        {
            targetcontrol.Focus();
            this.GotFocus -= UserControl_GotFocus_1;  // to set focus only once
        }

3
コントロールが直接ウィンドウ内にある場合にのみ機能し、ユーザーコントロール内にネストされている場合には機能しません。
ジョーホワイト

8

XAMLでフォーカスされた要素としてコントロール自体を簡単に設定できます。

<Window>
   <DataGrid FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">
     ...
   </DataGrid>
</Window>

これをユーザーコントロールに設定して、これが機能するかどうかを確認したことはありませんが、機能する可能性があります。


フォーカスの問題のためだけにコントロールに名前を付ける必要がないので、面白そうですね。一方、ユーザーコントロールを使用した私のテストは機能しませんでした。
heringer、2015年

@heringerを驚かせることはありません...これは、<border>または同様の非対話型コントロールにフォーカスを設定しようとするようなものです。このFocusedElement属性を、ユーザーコントロール内のインタラクティブコントロールに適用してみてください。しかし、それはオプションではないかもしれません。
Simon Gillbee、2015年

スタックパネルでこのアプローチを使用して、フォームが読み込まれたときにフォーカスする子ボタンを設定しました。どうもありがとう
Julio Nobre 2016

注意してください、それはバインディングを完全に壊すかもしれません。stackoverflow.com/questions/30676863/...
Der_Meister

2

C#6 +用のMizipzorの回答の最小バージョン。

public static class FocusBehavior
{
    public static readonly DependencyProperty GiveInitialFocusProperty =
        DependencyProperty.RegisterAttached(
            "GiveInitialFocus",
            typeof(bool),
            typeof(FocusBehavior),
            new PropertyMetadata(false, OnFocusFirstPropertyChanged));

    public static bool GetGiveInitialFocus(Control control) => (bool)control.GetValue(GiveInitialFocusProperty);
    public static void SetGiveInitialFocus(Control control, bool value) => control.SetValue(GiveInitialFocusProperty, value);

    private static void OnFocusFirstPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var control = obj as Control;

        if (control == null || !(args.NewValue is bool))
            return;

        if ((bool)args.NewValue)
            control.Loaded += OnControlLoaded;
        else
            control.Loaded -= OnControlLoaded;
    }

    private static void OnControlLoaded(object sender, RoutedEventArgs e) => ((Control)sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}

XAMLで使用:

<Window local:FocusBehavior.GiveInitialFocus="True" />

1

あなたが私のようなものであり、何らかの形で基本的なフォーカス動作を台無しにし、上記のすべてのソリューションを無関係にするいくつかのフレームワークを使用している場合でも、これを行うことができます。

1-フォーカスを取得する要素に注意してください(何であれ!)

2-xxx.xaml.csの背後にあるコードにこれを追加します

private bool _firstLoad;

3-最初のフォーカスを取得する要素にこれを追加します。

GotFocus="Element_GotFocus"

4-Element_GotFocusメソッドをコードビハインドに追加し、最初のフォーカスが必要なWPF名前付き要素を指定します。

private void Element_GotFocus(object sender, RoutedEventArgs e)
{
    if(_firstLoad)
    {
        this.MyElementWithFistFocus.Focus();
        _firstLoad = false;
    }
}

5-Loadedイベントを管理する

XAML

Loaded="MyWindow_Loaded"   

xaml.cs

private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
        _firstLoad = true;
        this.Element_GotFocus(null, null);
}

これが最後の手段として役立つことを願っています


0

私も同じ問題に直面しました。キャンバスコンテナー内に3つのテキストボックスがあり、ユーザーコントロールが開いたときに最初のテキストボックスがフォーカスされるようにしました。WPFコードはMVVMパターンに従いました。要素をフォーカスするための別の動作クラスを作成し、このように私のビューにバインドしました。

キャンバスの動作コード

public  class CanvasLoadedBehavior : Behavior<Canvas>
{
    private Canvas _canvas;
    protected override void OnAttached()
    {
        base.OnAttached();
        _canvas = AssociatedObject as Canvas;
        if (_canvas.Name == "ReturnRefundCanvas")
        {

            _canvas.Loaded += _canvas_Loaded;
        }


    }

    void _canvas_Loaded(object sender, RoutedEventArgs e)
    {
        FocusNavigationDirection focusDirection = FocusNavigationDirection.Next;

        // MoveFocus takes a TraveralReqest as its argument.
        TraversalRequest request = new TraversalRequest(focusDirection);
        UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
        if (elementWithFocus != null)
        {
            elementWithFocus.MoveFocus(request);
        }

    }

}

ビューのコード

<Canvas  Name="ReturnRefundCanvas" Height="200" Width="1466" DataContext="{Binding RefundSearchViewModel}">
                <i:Interaction.Behaviors>
                    <b:CanvasLoadedBehavior />
                </i:Interaction.Behaviors>
                <uc:Keyboard Canvas.Left="973" Canvas.Top="111" ToolTip="Keyboard" RenderTransformOrigin="-2.795,9.787"></uc:Keyboard>
                <Label  Style="{StaticResource Devlbl}" Canvas.Left="28" Content="Return and Refund Search" Canvas.Top="10" />
                <Image Height="30" Width="28" Canvas.Top="6" Canvas.Left="5" Source="pack://application:,,,/HomaKiosk;component/images/searchF.png">
                    <Image.OpacityMask>
                        <ImageBrush ImageSource="pack://application:,,,/HomaKiosk;component/images/searchF.png"/>
                    </Image.OpacityMask>
                </Image>

                <Separator Height="4" Canvas.Left="6" Margin="0" Canvas.Top="35" Width="1007"/>

                <ContentControl Canvas.Top="45" Canvas.Left="21"
                    ContentTemplate="{StaticResource ErrorMsg}"
                    Visibility="{Binding Error, Converter={c:StringNullOrEmptyToVisibilityConverter}}" 
                    Content="{Binding Error}" Width="992"></ContentControl>

                <Label  Style="{StaticResource Devlbl}" Canvas.Left="29" Name="FirstName" Content="First Name" Canvas.Top="90" />
                <wpf:AutoCompleteTextBox  Style="{StaticResource AutoComp}" Height="32" Canvas.Left="33" ToolTip="First Name"  Canvas.Top="120" Width="205"                     Padding="10,5" TabIndex="1001"
                    VerticalAlignment="Top"

                    Watermark=""
                    IconPlacement="Left"
                    IconVisibility="Visible"
                    Delay="100"

                    Text="{Binding FirstName, Mode=TwoWay, TargetNullValue=''}" 
                    Provider="{Binding FirstNameSuggestions}">
                    <wpf:AutoCompleteTextBox.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="5">
                                <StackPanel Orientation="Vertical">
                                    <TextBlock Text="{Binding}"
                   FontWeight="Bold" />
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </wpf:AutoCompleteTextBox.ItemTemplate>
                </wpf:AutoCompleteTextBox>

                <Label Style="{StaticResource Devlbl}" Canvas.Left="250" Content="Last Name" Canvas.Top="90" />
                <wpf:AutoCompleteTextBox  Style="{StaticResource AutoComp}" Height="32" ToolTip="Last Name" Canvas.Left="250"  Canvas.Top="120" Width="205" Padding="10,5"  TabIndex="1002"
                    VerticalAlignment="Top"
                    Watermark=""
                    IconPlacement="Left"
                    IconVisibility="Visible"
                    Delay="100"
                   Text="{Binding LastName, Mode=TwoWay, TargetNullValue=''}" 
                    Provider="{Binding LastNameSuggestions}">
                    <wpf:AutoCompleteTextBox.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="5">
                                <StackPanel Orientation="Vertical">
                                    <TextBlock Text="{Binding}"
                   FontWeight="Bold" />
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </wpf:AutoCompleteTextBox.ItemTemplate>
                </wpf:AutoCompleteTextBox>

                <Label Style="{StaticResource Devlbl}" Canvas.Left="480" Content="Receipt No" Canvas.Top="90" />
                             <wpf:AutoCompleteTextBox  Style="{StaticResource AutoComp}" Height="32" ToolTip="Receipt No" Canvas.Left="480"  Canvas.Top="120" Width="205" Padding="10,5"  TabIndex="1002"
                    VerticalAlignment="Top"
                    Watermark=""
                    IconPlacement="Left"
                    IconVisibility="Visible"
                    Delay="100"
                    Text="{Binding ReceiptNo, Mode=TwoWay, TargetNullValue=''}" 
                    Provider="{Binding ReceiptIdSuggestions}">
                    <wpf:AutoCompleteTextBox.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="5">
                                <StackPanel Orientation="Vertical" >
                                    <TextBlock Text="{Binding}"
                   FontWeight="Bold">

                                    </TextBlock>
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </wpf:AutoCompleteTextBox.ItemTemplate>
                    <i:Interaction.Behaviors>
                        <b:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9]+$" MaxLength="15" />
                    </i:Interaction.Behaviors>
                </wpf:AutoCompleteTextBox>
                <!--<Label Style="{StaticResource Devlbl}" Canvas.Left="710" Content="Duration" Canvas.Top="79" />-->
                <!--<ComboBox AllowDrop="True" Canvas.Left="710" ToolTip="Duration" Canvas.Top="107" Width="205" TabIndex="1004"
                    Style="{StaticResource CommonComboBox}"      
                    ItemsSource="{Binding Durations}" DisplayMemberPath="Description" SelectedValuePath="Id" SelectedValue="{Binding SelectedDate, Mode=TwoWay}">

                </ComboBox>-->

                <Button Content="Search" Style="{StaticResource MyButton}" ToolTip="Search" 
                    Canvas.Top="116" Canvas.Left="710" Cursor="Hand" 
                    Command="{Binding SearchCommand}" TabIndex="2001">
                </Button>
                <Button Content="Clear" Style="{StaticResource MyButton}"  ToolTip="Clear"
                    Canvas.Top="116" Canvas.Left="840" Cursor="Hand" 
                    Command="{Binding ClearCommand}" TabIndex="2002">
                </Button>
                <Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="25" Source="pack://application:,,,/HomaKiosk;component/images/chkpending.png"/>
                <Label  Style="{StaticResource LegendLbl}" Canvas.Left="50" Content="Check Returned and Payment Pending" Canvas.Top="178" />
                <Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="300" Source="pack://application:,,,/HomaKiosk;component/images/chkrepaid.png"/>
                <Label  Style="{StaticResource LegendLbl}" Canvas.Left="325" Content="Repaid" Canvas.Top="178" />
                <Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="395" Source="pack://application:,,,/HomaKiosk;component/images/refund.png"/>
                <Label  Style="{StaticResource LegendLbl}" Canvas.Left="415" Content="Refunded" Canvas.Top="178" />
                 </Canvas>

0

上記の解決策が期待どおりに機能しなかったため、Mizipzorによって提案された動作を次のように少し変更しました。

この部分から

if ((bool)args.NewValue)
        {
            control.Loaded += (sender, e) =>
                   control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        }

これに

if ((bool)args.NewValue)
        {
            control.Loaded += (sender, e) => control.Focus();
        }

ANd私はこの動作をWindowやUserControlにアタッチしていませんが、最初にフォーカスしたいコントロールを以下に示します。

<TextBox ui:FocusBehavior.InitialFocus="True" />

ああ、別の名前で申し訳ありませんが、添付プロパティにInitialFocus名を使用しています。

そしてこれは私のために働いています、多分それは誰かを助けることができるでしょう。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.