WPF ve ilk odak


191

Bir WPF uygulaması başladığında, hiçbir şeyin odağı yok gibi görünüyor.

Bu gerçekten garip. Kullandığım diğer tüm çerçeveler beklediğiniz şeyi yapar: sekme sırasındaki ilk kontrole ilk odak noktasını koyar. Ancak WPF olduğunu onayladım, sadece uygulamam değil - yeni bir Pencere oluşturup içine bir TextBox koyar ve uygulamayı çalıştırırsam, üzerine tıklayana veya Tab tuşuna basana kadar TextBox'ın odağı yoktur . Yuck.

Benim gerçek uygulama sadece bir TextBox daha karmaşık. UserControls içinde birkaç UserControls katmanları var. Bu UserControls biri Odaklanabilir = "True" ve KeyDown / KeyUp işleyicileri vardır ve ben pencere açılır açılmaz odak olmasını istiyorum. Yine de bir WPF acemisiyim ve bunu nasıl yapacağımı anlayamıyorum.

Uygulamamı başlatıp Sekme tuşuna basarsam, odak odaklanabilir kontrolüme gider ve istediğim gibi çalışmaya başlar. Ancak kullanıcılarımın pencereyi kullanmaya başlamadan önce Sekme tuşuna basmasını istemiyorum.

FocusManager.FocusedElement ile oynadım, ancak hangi kontrolü (üst düzey Pencere? Odaklanabilir denetimi içeren üst öğe? Odaklanabilir denetimin kendisi?) Veya ne ayarlayacağından emin değilim.

Pencere açılır açılmaz başlangıçta odaklanabilmek için derin yuvalanmış kontrolümü elde etmek için ne yapmam gerekir? Ya da daha iyisi, ilk odaklanabilir kontrolü sekme sırasında odaklamak için mi?

Yanıtlar:


165

Bu da işe yarıyor:

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

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

4
Buna ilk yorumu yapan kişi olduğuma şaşırdım. Bunun nereye gittiğine dair şaşkındım çünkü neredeyse her kontrole gidebilirdi. Bu özel soruya cevap olarak, pencerede devam edeceğini düşünüyorum, ancak msdn.microsoft.com/en-us/library/… ' deki açıklamaları okuyabilirsiniz .
Joel McBeth

Bu yaklaşımı bir yığın panelde başarıyla kullandım. İlgileniyorsanız, stackoverflow.com/a/2872306/378115
Julio Nobre'de

Bu benim için kabul edilen cevaptan çok daha iyi çalıştı, çünkü önce olan öğeye odaklanmam gerekiyor.
Puterdo Borato

163

Odaklanabilir özelliğin nerede kullanıldığını görmek için Reflektör'ü kazmak için parlak bir fikrim vardı ve bu çözüme giden yolu buldum. Sadece benim pencere yapıcısına aşağıdaki kodu eklemeniz gerekir:

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

Bu, sekme sırasındaki ilk kontrolü otomatik olarak seçecektir, bu nedenle herhangi bir pencereye ve Just Work'e bırakılabilmesi gereken genel bir çözümdür.


21
Bunu bir davranışa dönüştürün. <Window FocusBehavior.FocusFirst = "true"> ... </Window>
wekempf

6
@ wekempf, davranışlar fikrine aşina değildim, ama ona baktım ve bu hiç de kötü bir fikir değil. Başka biri (benim gibi) zaten ekli davranışlara aşina değilse, bir açıklama: codeproject.com/KB/WPF/AttachedBehaviors.aspx
Joe White,

1
Ayrıca, istenen öğe gerçek odaklanabilir öğeyi (derin hiyerarşilerde bile) içeren bir UserControl ise çalışır. Harika!
Daniel Albuschat

1
Harika bir fikir, ancak bazen odağı kabul edecek kontrol bir ise işe yaramaz Button. Bunu düzeltmek için, MoveFocusçağrıyı ContextIdleöncelikli olarak dağıtıcıya çevirdim ( Backgroundveya üstü çalışmıyor). Ayrıca, FocusNavigationDirection.Firstniyetle daha iyi eşleşen ve bu durumda aynı şeyi yapan bir var.
Anton Tykhyy

bu varsayılan davranış olmalı! Yuck (orijinal gönderide) doğru!
NH.

61

Ekli davranış olarak uygulanan kabul edilen cevaba dayanarak :

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

Şöyle kullanın:

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

6
Bence, bu kadar bulduğum en iyi çözüm. Teşekkürler!
Shion

1
Çağrısındaki bu yanıtta kodda bir hata var DependencyProperty.RegisterAttached. Üçüncü parametre olmamalı typeof(FocusBehavior), olmamalıdır typeof(Control). Bu değişikliğin yapılması tasarımcının 'Kontrol' hataları tarafından önceden kaydedilmiş olan 'FocusFirst' özelliğini bildirmesini önleyecektir.
Tony Vitabile

@TonyVitabile Düzeltildi. Mümkünse cevapları düzenlemek ve geliştirmekte her zaman özgürsünüz. :)
Mizipzor

Yüklü olay işleyicisi, kaldırma işlemi sırasında kaydı silinsin mi?
andreapier

@andreapier Eğer umursuyorsanız, ancak kayıt defterini atlamak bir bellek sızıntısına veya başka bir şeye neden olmaz. Yalnızca, kısa ömürlü bir nesnenin uzun ömürlü bir nesnedeki bir olaya eklenmiş bir yöntemi varsa bellek sızıntısına neden olan olaylar hakkında endişelenmeniz gerekir. Bu durumda, yaşam süresi pencerenin ömrüdür, bu yüzden iyisinizdir.
Joe White

14

Başka bir olası çözüm buldum. Mark Smith , FocusManager.FocusedElement ile kullanım için bir FirstFocusedElement biçimlendirme uzantısı yayınladı .

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

Tamamen kaygan! Teşekkür ederim!
Andy

9

Aynı sorun basit bir çözümle çözüldü: Ana pencerede:

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

Kullanıcı kontrolünde:

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

3
Yalnızca denetim doğrudan pencerenin içindeyse çalışır, bir UserControl içinde yuvalanmışsa değil.
Joe White

8

'WPF İlk Odak Kabusu' yaptıktan ve yığınla ilgili bazı cevaplara dayanarak, aşağıdakiler benim için en iyi çözüm olduğunu kanıtladı.

İlk olarak, App.xaml OnStartup () öğenizi ekleyin:

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

Sonra 'WindowLoaded' olayını App.xaml dosyasına da ekleyin:

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 başlangıç ​​odağı çoğunlukla bazı çerçeve yarış koşulları nedeniyle başarısız olduğundan, iş parçacığı sorunu kullanılmalıdır.

Tüm uygulama için küresel olarak kullanıldığı için aşağıdaki çözümü en iyi buldum.

Umarım yardımcı olur...

Oran


5
BeginInvokeBu korkutucu Sleep(100)ifade yerine kullanın .
l33t

8

Kontrolün kendisini XAML'de odaklanmış öğe olarak ayarlamasını sağlayabilirsiniz.

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

Ben asla bir usercontrol içinde bu ayar ve çalışıp çalışmadığını görmek denedim, ama olabilir.


Kulağa ilginç geliyor, çünkü sadece odak sorunu için kontrolü isimlendirmiyorsunuz. Öte yandan, kullanıcı kontrolü ile testim işe yaramadı.
Heringer

Bu beni hiç şaşırtmıyor ... bir <border> veya benzer etkileşimli olmayan bir kontrol üzerinde odaklanmaya çalışmak gibi bir şey. Bu FocusedElement özniteliğini usercontrol içindeki etkileşimli bir denetime uygulamayı deneyebilirsiniz. Ancak bu bir seçenek olmayabilir.
Simon Gillbee

Form yüklendikten sonra hangi alt düğmeyi odaklamak istediğimi ayarlamak için bu yaklaşımı bir yığın panelinde kullandım. Çok teşekkürler
Julio Nobre

Dikkatli olun, bağlamaları tamamen bozabilir. stackoverflow.com/questions/30676863/…
Der_Meister

2

Mizipzor'un C # 6+ için cevabının minimal bir versiyonu .

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'nizde kullanın:

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

1

Benim gibiyseniz ve bir şekilde temel odak davranışlarını bozan ve yukarıdaki tüm çözümleri alakasız hale getiren bazı çerçeveler kullanıyorsanız, yine de bunu yapabilirsiniz:

1 - Odağı alan öğeye dikkat edin (ne olursa olsun!)

2 - Bunu xxx.xaml.cs arkasındaki kodunuza ekleyin

private bool _firstLoad;

3 - Bunu ilk odağı alan öğeye ekleyin:

GotFocus="Element_GotFocus"

4 - Arkadaki koda Element_GotFocus yöntemini ekleyin ve ilk odağa ihtiyaç duyan WPF adlı öğeyi belirtin:

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

5 - Yüklenen etkinliği yönetme

XAML içinde

Loaded="MyWindow_Loaded"   

xaml.cs içinde

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

Bu son çare çözümü olarak yardımcı olacağını umuyoruz


0

Aynı problemle de karşılaştım. Tuval konteynerinin içinde üç metin kutusu vardı ve kullanıcı denetimi açıldığında ilk metin kutusunun odaklanmasını istedim. WPF kodu MVVM modelini izledi. Öğeyi odaklamak için ayrı bir davranış sınıfı oluşturdum ve onu böyle görüyorum.

Tuval davranış kodu

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

    }

}

Görüntüleme kodu

<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

Yukarıdaki çözüm benim için beklendiği gibi çalışmıyor, Mizipzor tarafından önerilen davranışı biraz değiştirdim:

Bu kısımdan

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

Buna

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

AND ben bu davranışı Pencere veya UserControl ekliyorum, ama kontrol etmek için ben başlangıçta odaklanmak istiyorum, örneğin:

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

Oh, farklı adlandırma için üzgünüm ekli özellik için InitialFocus adı kullanıyorum.

Ve bu benim için çalışıyor, belki başka birine yardımcı olabilir.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.