2009-05-03 68 views
156

似乎WPF应用程序启动时,没有任何焦点。WPF和初始焦点

这真的很奇怪。我用过的其他所有框架都完全符合您的期望:首先将注意力集中在Tab选项中的第一个控件上。但我已经确认它是WPF,不仅仅是我的应用程序 - 如果我创建一个新窗口,并且只是放置一个TextBox并运行应用程序,那么TextBox就没有焦点,直到我点击它或按Tab 。呸。

我的实际应用程序比TextBox更复杂。我在UserControls内有几层UserControls。其中一个UserControls拥有Focusable =“True”和KeyDown/KeyUp处理程序,并且我希望它在我的窗口打开后立即拥有焦点。不过,我仍然是WPF新手,而且我没有太多的运气来搞清楚如何做到这一点。

如果我启动我的应用程序并按下Tab键,然后焦点转到我的可对焦控件上,然后按我想要的方式开始工作。但是我不希望我的用户在开始使用该窗口之前不得不点击Tab。

我已经玩过FocusManager.FocusedElement,但我不确定将其设置在哪个控件上(顶层Window?包含可聚焦控件的父级?可聚焦控件本身?将其设置为。

当窗口打开后,我需要做什么才能让我的深层嵌套控件具有初始焦点?或者更好的是,按照Tab键顺序聚焦第一个可调焦控件?

回答

141

我有一个聪明的想法,通过反射器来查看Focusable属性的使用位置,并找到了解决方案。我只需要下面的代码添加到我的窗口的构造函数:

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

这会自动选择Tab键顺序的第一控制,所以它是一个通用的解决方案,应该是能够被丢弃到任何窗口,只是工作。

+19

加入转变为行为。 <窗口 FocusBehavior.FocusFirst =“真”> ... – wekempf 2009-05-08 16:08:19

+4

@wekempf,我不熟悉行为的想法,但我看着它,这不是一个坏主意。如果其他人(如我)不熟悉附加行为,请参考以下解释:http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx – 2009-05-09 13:43:00

+1

此外,如果所需元素是UserControl,包含实际可聚焦元素(即使在深层次结构中)。大! – 2015-05-21 10:08:41

13

我发现了另一种可能的解决方案。 Mark Smith发布了一个用于FocusManager.FocusedElement的FirstFocusedElement markup extension

<UserControl x:Class="FocusTest.Page2" 
    xmlns:FocusTest="clr-namespace:FocusTest" 
    FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}"> 
+0

完全浮油!谢谢! – Andy 2017-05-10 02:13:14

125

这工作,也:

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

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

在UserControls中似乎不起作用。 – Nathan 2012-12-04 16:19:04

7

有“WPF最初的重点梦魇”和基于栈的答案,以下证明了我是最好的解决方案后。

首先,添加您的App.xaml中OnStartup()以下内容:

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

然后还加了 'WindowLoaded' 事件中的App.xaml:

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最初的重点大多因为一些框架竞争条件而失败。

我发现以下解决方案最好,因为它是全局使用整个应用程序。

希望它有帮助...

奥兰

49

基于作为附加的行为实施的接受answer

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"> 
0
<Window FocusManager.FocusedElement="{Binding ElementName=yourControlName}"> 
7

当初同样的问题简单的解决方案解决了它: 在主窗口中:

<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 
     } 
7

您可以很容易地控制自己设定为XAML聚焦元素。

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

我从来没有试过在usercontrol中设置它,看看它是否有效,但它可能。

0

我也遇到了同样的问题。我有三个文本框内的画布容器,并希望第一个文本框被关注时用户控件打开。 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> 
1

Mizipzor's answer的最小版本为C#6+。

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; 
    } 
} 

希望这将帮助作为最后的手段解决

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(); 
     } 

,我不将此行为附加到Window或UserControl,但要控制我最初要关注的内容,例如:

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

噢,对不同的命名我为附加属性使用了InitialFocus名称。

这是为我工作,也许它可以帮助别人。