Wygląda na to, że po uruchomieniu aplikacji WPF nic nie jest skupione.
To jest naprawdę dziwne. Każdy inny framework, którego użyłem, robi dokładnie to, czego można się spodziewać: początkowo skupia się na pierwszej kontrolce w kolejności tabulacji. Ale potwierdziłem, że jest to WPF, a nie tylko moja aplikacja - jeśli utworzę nowe okno i po prostu umieszczę w nim TextBox i uruchomię aplikację, TextBox nie jest aktywny, dopóki go nie kliknę lub nie naciśnę Tab . Fuj.
Moja aktualna aplikacja jest bardziej skomplikowana niż zwykły TextBox. Mam kilka warstw UserControls w ramach UserControls. Jedna z tych kontrolek UserControls ma moduły obsługi Focusable = "True" i KeyDown / KeyUp i chcę, aby była aktywna, gdy tylko otworzy się moje okno. Nadal jestem nowicjuszem w WPF i nie mam szczęścia, aby dowiedzieć się, jak to zrobić.
Jeśli uruchomię aplikację i naciśnę klawisz Tab, fokus przejdzie do mojej kontrolki, na której można ustawić fokus, i zacznie działać tak, jak chcę. Ale nie chcę, aby moi użytkownicy musieli naciskać Tab, zanim będą mogli rozpocząć korzystanie z okna.
Bawiłem się z FocusManager.FocusedElement, ale nie jestem pewien, którą kontrolkę ustawić (okno najwyższego poziomu? Rodzic, który zawiera kontrolkę, którą można ustawić?) Ani na co ją ustawić.
Co muszę zrobić, aby moja głęboko zagnieżdżona kontrola miała początkową fokus zaraz po otwarciu okna? Albo jeszcze lepiej, aby skupić się na pierwszej kontrolce, którą można ustawić w kolejności tabulacji?
Wpadłem na świetny pomysł, aby przekopać się przez Reflector, aby zobaczyć, gdzie jest używana właściwość Focusable, i znalazłem drogę do tego rozwiązania. Muszę tylko dodać następujący kod do konstruktora mojego okna:
Loaded += (sender, e) => MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
Spowoduje to automatyczne wybranie pierwszej kontrolki w kolejności zakładek, więc jest to ogólne rozwiązanie, które powinno być możliwe do upuszczenia w dowolnym oknie i Just Work.
źródło
Button
. Aby to naprawić, przerzucamMoveFocus
połączenie przez dyspozytora zContextIdle
priorytetem (Background
lub wyższym nie działa). Istnieje również element,FocusNavigationDirection.First
który lepiej pasuje do intencji i robi to samo w tym przypadku.Na podstawie zaakceptowanej odpowiedzi zaimplementowanej jako załączone zachowanie:
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)); } } } }
Użyj tego w ten sposób:
<Window xmlns:Behaviors="clr-namespace:UI.Behaviors" Behaviors:FocusBehavior.FocusFirst="true">
źródło
DependencyProperty.RegisterAttached
. Trzeci parametr powinien byćtypeof(FocusBehavior)
, nietypeof(Control)
. Wprowadzenie tej zmiany uniemożliwi projektantowi zgłaszanie właściwości „FocusFirst” już zarejestrowanej przez błędy „Control”.Znalazłem inne możliwe rozwiązanie. Mark Smith opublikował rozszerzenie znaczników FirstFocusedElement do użytku z FocusManager.FocusedElement.
<UserControl x:Class="FocusTest.Page2" xmlns:FocusTest="clr-namespace:FocusTest" FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">
źródło
Ten sam problem rozwiązał go prostym rozwiązaniem: W głównym oknie:
<Window .... FocusManager.FocusedElement="{Binding ElementName=usercontrolelementname}" ... />
W kontroli użytkownika:
private void UserControl_GotFocus_1(object sender, RoutedEventArgs e) { targetcontrol.Focus(); this.GotFocus -= UserControl_GotFocus_1; // to set focus only once }
źródło
Po „WPF Initial Focus Nightmare” i na podstawie kilku odpowiedzi na stosie, poniższe okazały się najlepszym rozwiązaniem.
Najpierw dodaj swoją App.xaml OnStartup (), co następuje:
EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent, new RoutedEventHandler(WindowLoaded));
Następnie dodaj zdarzenie „WindowLoaded” również w 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)); })); }
Problem z wątkami musi być używany, ponieważ początkowy fokus WPF najczęściej kończy się niepowodzeniem z powodu pewnych warunków wyścigu platformy.
Uważam, że poniższe rozwiązanie jest najlepsze, ponieważ jest używane globalnie dla całej aplikacji.
Mam nadzieję, że to pomoże...
Oran
źródło
BeginInvoke
zamiast tego przerażającegoSleep(100)
stwierdzenia.Kontrolkę można łatwo ustawić jako skoncentrowany element w języku XAML.
<Window> <DataGrid FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"> ... </DataGrid> </Window>
Nigdy nie próbowałem ustawiać tego w kontroli użytkownika i sprawdzać, czy to działa, ale może.
źródło
Minimalna wersja odpowiedzi Mizipzora na 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)); }
Użyj w swoim XAML:
<Window local:FocusBehavior.GiveInitialFocus="True" />
źródło
Jeśli jesteś podobny do mnie i używasz ram, które w jakiś sposób psują podstawowe zachowania związane z fokusem i sprawiają, że wszystkie powyższe rozwiązania są nieistotne, nadal możesz to zrobić:
1 - Zwróć uwagę na element, który ma fokus (cokolwiek to jest!)
2 - Dodaj to w swoim kodzie za xxx.xaml.cs
private bool _firstLoad;
3 - Dodaj to do elementu, który ma pierwszeństwo:
GotFocus="Element_GotFocus"
4 - Dodaj metodę Element_GotFocus w kodzie za nim i określ nazwany element WPF, który potrzebuje pierwszego fokusu:
private void Element_GotFocus(object sender, RoutedEventArgs e) { if(_firstLoad) { this.MyElementWithFistFocus.Focus(); _firstLoad = false; } }
5 - Zarządzaj zdarzeniem Loaded
w języku XAML
Loaded="MyWindow_Loaded"
w xaml.cs
private void MyWindow_Loaded(object sender, RoutedEventArgs e) { _firstLoad = true; this.Element_GotFocus(null, null); }
Mam nadzieję, że to pomoże w ostateczności
źródło
Ja też stanąłem przed tym samym problemem. Miałem trzy pola tekstowe w kontenerze kanwy i chciałem, aby pierwsze pole tekstowe było aktywne po otwarciu kontrolki użytkownika. Kod WPF był zgodny ze wzorcem MVVM. Stworzyłem oddzielną klasę zachowań, aby skupić się na elemencie i powiązałem go z moim widokiem w ten sposób.
Kod zachowania płótna
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); } } }
Kod do przeglądania
<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>
źródło
Powyższe rozwiązanie nie działało zgodnie z oczekiwaniami dla mnie, nieznacznie zmieniłem zachowanie zaproponowane przez Mizipzora w następujący sposób:
Z tej części
if ((bool)args.NewValue) { control.Loaded += (sender, e) => control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); }
Do tego
if ((bool)args.NewValue) { control.Loaded += (sender, e) => control.Focus(); }
I nie dołączam tego zachowania do Window lub UserControl, ale do sterowania chcę się początkowo skupić, np:
<TextBox ui:FocusBehavior.InitialFocus="True" />
Och, przepraszam za inne nazewnictwo, używam nazwy InitialFocus dla dołączonej właściwości.
I to działa dla mnie, może mogłoby pomóc komuś innemu.
źródło
<Window FocusManager.FocusedElement="{Binding ElementName=yourControlName}">
źródło