WPF и первоначальный фокус
кажется, что при запуске приложения WPF ничто не имеет фокуса.
Это очень странно. Все остальные рамки, которые я использовал, делают именно то, что вы ожидаете: помещает начальный фокус на первый элемент управления в порядке табуляции. Но я подтвердил, что это WPF, а не только мое приложение-если я создаю новое окно и просто помещаю в него текстовое поле и запускаю приложение, текстовое поле не имеет фокуса, пока я не нажму на него или не нажму вкладку. Фу.
мое фактическое приложение сложнее, чем просто текстовый. У меня есть несколько слоев элементы управления UserControl в элементы управления UserControl. Один из этих UserControls имеет Focusable="True" и обработчики KeyDown/KeyUp, и я хочу, чтобы он имел фокус, как только откроется мое окно. Тем не менее, я все еще немного новичок WPF, и мне не очень повезло выяснить, как это сделать.
Если я запускаю свое приложение и нажимаю клавишу Tab, то фокус переходит на мой фокусируемый элемент управления, и он начинает работать так, как я хочу. Но я не хочу, чтобы мои пользователи должны были нажать вкладку, прежде чем они можно начать использовать окно.
Я играл вокруг с FocusManager.FocusedElement, но я не уверен, какой элемент управления его установить (окно верхнего уровня? родитель, который содержит фокусируемый элемент управления? сам фокусируемый контроль?) или что его установить.
Что мне нужно сделать, чтобы мой глубоко вложенный элемент управления имел начальный фокус, как только откроется окно? Или еще лучше, чтобы сфокусировать первый фокусируемый элемент управления в порядке табуляции?
12 ответов:
У меня была яркая идея прокопать рефлектор, чтобы увидеть, где используется Фокусируемое свойство, и нашел свой путь к этому решению. Мне просто нужно добавить следующий код в конструктор моего окна:
Loaded += (sender, e) => MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));это автоматически выберет первый элемент управления в порядке табуляции, так что это общее решение, которое должно быть в состоянии быть сброшено в любое окно и просто работать.
Это тоже работает:
<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">
Я нашел другое возможное решение. Марк Смит опубликовал расширение разметки FirstFocusedElement для использования с FocusManager.FocusedElement.
<UserControl x:Class="FocusTest.Page2" xmlns:FocusTest="clr-namespace:FocusTest" FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">
после того, как у меня был "кошмар начального фокуса WPF" и на основе некоторых ответов на стек, следующее оказалось для меня лучшим решением.
во-первых, добавьте свое приложение.xaml OnStartup() следующее:
EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent, new RoutedEventHandler(WindowLoaded));затем добавьте событие "WindowLoaded" также в приложении.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 в основном терпит неудачу из-за некоторых условий гонки фреймворка.
Я нашел следующее решение, как это лучше использовать глобально для всего приложения.
надеюсь, что это помогает...
Оране
была же проблема решена с простым решением: В главном окне:
<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>Я никогда не пытался установить это в usercontrol и посмотреть, если это работает, но это может быть.
минимальная версия 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" />
если вы похожи на меня, и вы используете некоторые фреймворки, которые каким-то образом нарушают основное поведение фокуса и делают все решения выше неуместными, вы все равно можете сделать это :
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-Управление загруженным событием
в XAML
Loaded="MyWindow_Loaded"в xaml.cs
private void MyWindow_Loaded(object sender, RoutedEventArgs e) { _firstLoad = true; this.Element_GotFocus(null, null); }надеюсь, что это поможет в крайнем случае решение
Я тоже столкнулся с той же проблемой. У меня было три текстовых поля внутри контейнера canvas и я хотел, чтобы первое текстовое поле было сфокусировано при открытии пользовательского элемента управления. Код 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>
выше решение не работает, как ожидалось для меня, я немного изменил поведение, предложенное 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 для вложенного свойства.
и это работает для меня, может быть, это может помочь кому-то еще.
Comments