Aracılığıyla paylaş


Programatik odak navigasyonu

Klavye, uzaktan kumanda ve D-pad

Odağı Windows uygulamanızda program aracılığıyla taşımak için FocusManager.TryMoveFocus yöntemini veya FindNextElement yöntemini kullanabilirsiniz.

TryMoveFocus, o anki odaklanmış öğeden belirtilen yöndeki bir sonraki odaklanabilir öğeye odağı kaydırmaya çalışırken, FindNextElement, belirtilen gezinme yönüne göre odaklanacak öğeyi (DependencyObject olarak) alır (yalnızca yönsel gezinme, sekme gezinmesi için kullanılamaz).

Uyarı

FindNextFocusableElement bir UIElement aldığından FindNextFocusableElement yerine FindNextElement yöntemini kullanmanızı öneririz; çünkü sonraki odaklanılabilir öğe uiElement değilse null döndürür (köprü nesnesi gibi).

Kapsam içinde bir odak adayı bulun

Hem TryMoveFocus hem de FindNextElement için, belirli bir kullanıcı arabirimi ağacında bir sonraki odak adayını aramak veya belirli öğeleri dikkate almamak dahil olmak üzere odak gezinti davranışını özelleştirebilirsiniz.

Bu örnekte TryMoveFocus ve FindNextElement yöntemlerini göstermek için bir TicTacToe oyunu kullanılır.

<StackPanel Orientation="Horizontal"
                VerticalAlignment="Center"
                HorizontalAlignment="Center" >
    <Button Content="Start Game" />
    <Button Content="Undo Movement" />
    <Grid x:Name="TicTacToeGrid" KeyDown="OnKeyDown">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50" />
            <ColumnDefinition Width="50" />
            <ColumnDefinition Width="50" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="50" />
            <RowDefinition Height="50" />
        </Grid.RowDefinitions>
        <myControls:TicTacToeCell 
            Grid.Column="0" Grid.Row="0" 
            x:Name="Cell00" />
        <myControls:TicTacToeCell 
            Grid.Column="1" Grid.Row="0" 
            x:Name="Cell10"/>
        <myControls:TicTacToeCell 
            Grid.Column="2" Grid.Row="0" 
            x:Name="Cell20"/>
        <myControls:TicTacToeCell 
            Grid.Column="0" Grid.Row="1" 
            x:Name="Cell01"/>
        <myControls:TicTacToeCell 
            Grid.Column="1" Grid.Row="1" 
            x:Name="Cell11"/>
        <myControls:TicTacToeCell 
            Grid.Column="2" Grid.Row="1" 
            x:Name="Cell21"/>
        <myControls:TicTacToeCell 
            Grid.Column="0" Grid.Row="2" 
            x:Name="Cell02"/>
        <myControls:TicTacToeCell 
            Grid.Column="1" Grid.Row="2" 
            x:Name="Cell22"/>
        <myControls:TicTacToeCell 
            Grid.Column="2" Grid.Row="2" 
            x:Name="Cell32"/>
    </Grid>
</StackPanel>
private void OnKeyDown(object sender, KeyRoutedEventArgs e)
{
    DependencyObject candidate = null;

    var options = new FindNextElementOptions ()
    {
        SearchRoot = TicTacToeGrid,
        XYFocusNavigationStrategyOverride = XYFocusNavigationStrategyOverride.Projection
    };

    switch (e.Key)
    {
        case Windows.System.VirtualKey.Up:
            candidate = 
                FocusManager.FindNextElement(
                    FocusNavigationDirection.Up, options);
            break;
        case Windows.System.VirtualKey.Down:
            candidate = 
                FocusManager.FindNextElement(
                    FocusNavigationDirection.Down, options);
            break;
        case Windows.System.VirtualKey.Left:
            candidate = FocusManager.FindNextElement(
                FocusNavigationDirection.Left, options);
            break;
        case Windows.System.VirtualKey.Right:
            candidate = 
                FocusManager.FindNextElement(
                    FocusNavigationDirection.Right, options);
            break;
    }
    // Also consider whether candidate is a Hyperlink, WebView, or TextBlock.
    if (candidate != null && candidate is Control)
    {
        (candidate as Control).Focus(FocusState.Keyboard);
    }
}

Odak adaylarının nasıl tanımlandığından daha fazla özelleştirmek için FindNextElementOptions kullanın. Bu nesne aşağıdaki özellikleri sağlar:

  • SearchRoot - Odaklanma gezinti adaylarını aramayı bu DependencyObject nesnesinin alt öğeleriyle sınırlandırın. Null, aramayı görsel ağacının kökünden başlatmayı gösterir.

Önemli

SearchRoot'un alt öğelerine, bunları yön alanının dışına yerleştiren bir veya daha fazla dönüşüm uygulanırsa, bu öğeler yine de aday olarak kabul edilir.

  • ExclusionRect - Odak gezinti adayları, çakışan tüm nesnelerin gezinti odağının dışında tutulduğu "kurgusal" sınırlayıcı dikdörtgen kullanılarak tanımlanır. Bu dikdörtgen yalnızca hesaplamalar için kullanılır ve görsel ağaca hiçbir zaman eklenmez.
  • HintRect - Odak gezinti adayları, odağı alma olasılığı en yüksek öğeleri tanımlayan "kurgusal" sınırlayıcı dikdörtgen kullanılarak tanımlanır. Bu dikdörtgen yalnızca hesaplamalar için kullanılır ve görsel ağaca hiçbir zaman eklenmez.
  • XYFocusNavigationStrategyOverride - Odak alacak en iyi aday öğeyi belirlemek için kullanılan odak gezinti stratejisi.

Aşağıdaki görüntüde bu kavramlardan bazıları gösterilmektedir.

B öğesinde odak olduğunda FindNextElement, sağa doğru ilerlerken odak adayı olarak I'yi tanımlar. Bunun nedenleri şunlardır:

  • A'daki HintRect nedeniyle, başlangıç başvurusu B değil A'dır
  • MyPanel SearchRoot olarak belirtildiğinden C aday değil
  • ExclusionRect çakıştığı için F aday değil

Gezinti ipuçlarını kullanarak özel odak gezinti davranışı

Gezinti ipuçlarını kullanarak özelleştirilmiş odak yönlendirme davranışı

NoFocusCandidateFound olayı

Sekme veya ok tuşlarına basıldığında ve belirtilen yönde odak adayı olmadığında UIElement.NoFocusCandidateFound olayı tetiklenir. Bu olay TryMoveFocus için tetiklenmez.

Bu, yönlendirilmiş bir olay olduğundan, odaklanmış öğeden ardışık üst nesnelerden geçerek nesne ağacının köküne kadar yukarı doğru yayılır. Bu, olayı uygun olan her yerde işlemenizi sağlar.

Burada, kullanıcı odağı en soldaki odaklanabilir denetimin soluna taşımaya çalıştığında Grid'in SplitView'i nasıl açtığını göstereceğiz (bkz. Xbox ve TV için Tasarlama).

<Grid NoFocusCandidateFound="OnNoFocusCandidateFound">
...
</Grid>
private void OnNoFocusCandidateFound (
    UIElement sender, NoFocusCandidateFoundEventArgs args)
{
    if(args.NavigationDirection == FocusNavigationDirection.Left)
    {
        if(args.InputDevice == FocusInputDeviceKind.Keyboard ||
        args.InputDevice == FocusInputDeviceKind.GameController )
            {
                OpenSplitPaneView();
            }
        args.Handled = true;
    }
}

GotFocus ve LostFocus olayları

Bir öğe sırasıyla odağı aldığında veya kaybettiğinde UIElement.GotFocus ve UIElement.LostFocus olayları tetiklenir. Bu olay TryMoveFocus için tetiklenmez.

Bunlar yönlendirilmiş olaylar olduğundan, odaklanmış öğeden başlayarak sıradaki üst nesnelere ve nesne ağacının köküne doğru yayılırlar. Bu, olayı uygun olan her yerde işlemenizi sağlar.

GettingFocus ve LosingFocus olayları

UIElement.GettingFocus ve UIElement.LosingFocus olayları, ilgili UIElement.GotFocus ve UIElement.LostFocus olaylarından önce tetiklenir.

Yönlendirilmiş olaylar olduklarından, odaklanmış öğeden başlayarak ardışık üst nesneler aracılığıyla nesne ağacının köküne kadar yukarıya doğru iletilirler. Odak değişikliği gerçekleşmeden önce bu durum gerçekleştiğinde, odak değişikliğini yeniden yönlendirebilir veya iptal edebilirsiniz.

GettingFocus ve LosingFocus zaman uyumlu olaylardır, bu nedenle bu olaylar kabarcık oluştururken odak taşınmaz. Ancak , GotFocus ve LostFocus zaman uyumsuz olaylardır, yani işleyici yürütülmeden önce odağın yeniden taşınmayacağı garanti değildir.

Odak Control.Focus çağrısında hareket ederse, çağrı sırasında GettingFocus oluşturulurken, çağrıdan sonra GotFocus oluşturulur.

Odak gezinti hedefi GettingFocus ve LosingFocus olayları sırasında (odak taşınmadan önce) GettingFocusEventArgs.NewFocusedElement özelliği aracılığıyla değiştirilebilir. Hedef değiştirilse bile, olay yine kabarcıklar oluşturur ve hedef yeniden değiştirilebilir.

Yeniden giriş sorunlarını önlemek için, bu olaylar yükselirken TryMoveFocus veya Control.Focus kullanarak odağı taşımaya çalışırsanız bir istisna fırlatılır.

Bu olaylar odağın hareket nedenlerinden bağımsız olarak tetiklenir (sekme gezintisi, yön gezintisi ve programlı gezinti dahil).

Odak olayları için yürütme sırası aşağıdadır:

  1. LosingFocus Odak, kayıp odak öğesine geri sıfırlanırsa veya TryCancel başarılı olursa, başka hiçbir olay tetiklenmez.
  2. GettingFocus Odak, kayıp odak öğesine geri sıfırlanırsa veya TryCancel başarılı olursa, başka hiçbir olay tetiklenmez.
  3. LostFocus
  4. GotFocus

Aşağıdaki görüntüde, A'dan sağa doğru ilerlerken XYFocus'un B4'i aday olarak nasıl seçtiği gösterilmektedir. Ardından B4, ListView'un odağı B3'e yeniden atama fırsatına sahip olduğu GettingFocus olayını başlatır.

GettingFocus olayında odak gezinti hedeflerini değiştirme

GettingFocus olayında odak gezinti hedeflerini değiştirme

Burada GettingFocus olayının nasıl işleneceğini ve odağın nasıl yönlendirileceğini göstereceğiz.

<StackPanel Orientation="Horizontal">
    <Button Content="A" />
    <ListView x:Name="MyListView" SelectedIndex="2" GettingFocus="OnGettingFocus">
        <ListViewItem>LV1</ListViewItem>
        <ListViewItem>LV2</ListViewItem>
        <ListViewItem>LV3</ListViewItem>
        <ListViewItem>LV4</ListViewItem>
        <ListViewItem>LV5</ListViewItem>
    </ListView>
</StackPanel>
private void OnGettingFocus(UIElement sender, GettingFocusEventArgs args)
{
    //Redirect the focus only when the focus comes from outside of the ListView.
    // move the focus to the selected item
    if (MyListView.SelectedIndex != -1 && 
        IsNotAChildOf(MyListView, args.OldFocusedElement))
    {
        var selectedContainer = 
            MyListView.ContainerFromItem(MyListView.SelectedItem);
        if (args.FocusState == 
            FocusState.Keyboard && 
            args.NewFocusedElement != selectedContainer)
        {
            args.TryRedirect(
                MyListView.ContainerFromItem(MyListView.SelectedItem));
            args.Handled = true;
        }
    }
}

Burada bir CommandBar için LosingFocus olayının nasıl işleneceğini ve menü kapatıldığında odağın nasıl ayarlandığını göstereceğiz.

<CommandBar x:Name="MyCommandBar" LosingFocus="OnLosingFocus">
     <AppBarButton Icon="Back" Label="Back" />
     <AppBarButton Icon="Stop" Label="Stop" />
     <AppBarButton Icon="Play" Label="Play" />
     <AppBarButton Icon="Forward" Label="Forward" />

     <CommandBar.SecondaryCommands>
         <AppBarButton Icon="Like" Label="Like" />
         <AppBarButton Icon="Share" Label="Share" />
     </CommandBar.SecondaryCommands>
 </CommandBar>
private void OnLosingFocus(UIElement sender, LosingFocusEventArgs args)
{
    if (MyCommandBar.IsOpen == true && 
        IsNotAChildOf(MyCommandBar, args.NewFocusedElement))
    {
        if (args.TryCancel())
        {
            args.Handled = true;
        }
    }
}

İlk ve son odaklanabilir öğeyi bulma

FocusManager.FindFirstFocusableElement ve FocusManager.FindLastFocusableElement yöntemleri, odağı nesne kapsamındaki ilk veya son odaklanabilir öğeye taşır (UIElement öğe ağacı veya TextElement'in metin ağacı). Kapsam çağrısında belirtilir (parametre null ise, kapsam şu anki penceredir).

Kapsamda hiçbir odak adayı tanımlanamıyorsa null döndürülür.

Burada, CommandBar düğmelerinin yönlü kaydırma davranışına sahip olduğunu nasıl belirtebileceğimizi göstereceğiz (bkz . Klavye Etkileşimleri).

<CommandBar x:Name="MyCommandBar" LosingFocus="OnLosingFocus">
    <AppBarButton Icon="Back" Label="Back" />
    <AppBarButton Icon="Stop" Label="Stop" />
    <AppBarButton Icon="Play" Label="Play" />
    <AppBarButton Icon="Forward" Label="Forward" />

    <CommandBar.SecondaryCommands>
        <AppBarButton Icon="Like" Label="Like" />
        <AppBarButton Icon="ReShare" Label="Share" />
    </CommandBar.SecondaryCommands>
</CommandBar>
private void OnLosingFocus(UIElement sender, LosingFocusEventArgs args)
{
    if (IsNotAChildOf(MyCommandBar, args.NewFocussedElement))
    {
        DependencyObject candidate = null;
        if (args.Direction == FocusNavigationDirection.Left)
        {
            candidate = FocusManager.FindLastFocusableElement(MyCommandBar);
        }
        else if (args.Direction == FocusNavigationDirection.Right)
        {
            candidate = FocusManager.FindFirstFocusableElement(MyCommandBar);
        }
        if (candidate != null)
        {
            args.NewFocusedElement = candidate;
            args.Handled = true;
        }
    }
}