WPF和最初的重点
似乎WPF应用程序启动时,没有任何重点。
这真的很奇怪。 我用过的其他所有框架都只是你所期望的:把最初的焦点放在按Tab键顺序的第一个控件上。 但我已经确认它是WPF,不只是我的应用程序 – 如果我创build一个新的窗口,只是把一个文本框,并运行应用程序,文本框没有焦点,直到我点击它或按Tab 。 呸。
我的实际应用程序比TextBox更复杂。 UserControls中有几个UserControls层。 其中一个UserControls有Focusable =“True”和KeyDown / KeyUp处理程序,并且我希望在我的窗口打开后它就有了焦点。 不过,我仍然是WPF新手,而且我没有太多的运气来搞清楚如何做到这一点。
如果我启动我的应用程序并按Tab键,然后焦点转到我的可调焦控制,它开始按我想要的方式工作。 但是我不希望我的用户在开始使用窗口之前必须点击Tab键。
我已经玩过FocusManager.FocusedElement,但我不确定哪个控件将其设置(顶层Window?包含可调焦控件的父级?可调焦控件本身?)或设置它的内容。
在窗口打开后,我需要做些什么才能让我的深层嵌套控件有初始焦点? 或者更好的是,要按照Tab键顺序聚焦第一个可调焦的控件?
我有一个聪明的想法,通过reflection器来查看Focusable属性的使用位置,并find了解决scheme。 我只需要将下面的代码添加到我的窗口的构造函数:
Loaded += (sender, e) => MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
这将自动select第一个控制button的顺序,所以这是一个通用的解决scheme,应该可以放在任何窗口,只是工作。
这也适用于:
<Window FocusManager.FocusedElement="{Binding ElementName=SomeElement}"> <DataGrid x:Name="SomeElement"> ... </DataGrid> </Window>
基于接受的答案作为附加行为实施:
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">
我发现了另一个可能的解 Mark Smith发布了用于FocusManager.FocusedElement的FirstFocusedElement标记扩展 。
<UserControl x:Class="FocusTest.Page2" xmlns:FocusTest="clr-namespace:FocusTest" FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">
在“WPF初始焦点梦魇”之后,根据堆栈上的一些答案,以下certificate对我来说是最好的解决scheme。
首先,添加你的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初始焦点大多失败。
我发现以下解决scheme是最好的,因为它是全球使用整个应用程序。
希望能帮助到你…
奥兰
同样的问题用简单的解决scheme解决了它:在主窗口中:
<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 }
您可以轻松地将控件设置为XAML中的焦点元素。
<Window> <DataGrid FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"> ... </DataGrid> </Window>
我从来没有尝试在一个用户控件中设置这个,看看这个工程,但它可能。
<Window FocusManager.FocusedElement="{Binding ElementName=yourControlName}">
Mizipzor针对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" />
我也面临同样的问题。 我有三个文本框内的canvas容器,并希望当用户控件打开时,第一个文本框被聚焦。 WPF代码遵循MVVM模式。 我创build了一个单独的行为类来聚焦元素,并将其绑定到我的视图中。
canvas行为代码
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;componenthttp://img.dovov.comsearchF.png"> <Image.OpacityMask> <ImageBrush ImageSource="pack://application:,,,/HomaKiosk;componenthttp://img.dovov.comsearchF.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;componenthttp://img.dovov.comchkpending.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;componenthttp://img.dovov.comchkrepaid.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;componenthttp://img.dovov.comrefund.png"/> <Label Style="{StaticResource LegendLbl}" Canvas.Left="415" Content="Refunded" Canvas.Top="178" /> </Canvas>