Přizpůsobení položky nastavení v UWP NavigationView

Development WinUI XAML

5 years ago

V tomto článku se podíváme, jak můžeme přizpůsobit vzhled položky nastavení v ovladacím prvku NavigationView v UWP aplikacích.

Custom settings item

Vlastní vzhled položky settings

"Zadrátované" settings

Fakt, že položka nastavení je v NavigationView zabudována "natvrdo" jsem si poprvé uvědomil, když jsem se pokoušel zodpovědět tuto otázku od @Xinpeng na Stack Overflow a zaskočilo mě to. Tato konkrétní položka není součástí NavigationView.PaneFooter, nýbrž je umístně ještě pod ním. A přesto, že vývojář může rozhodnout zda je zobrazena, není možné přímo manipulovat s jejími vlastnostmi. Prvním nápadem, který jsem dostal, bylo jednoduše vytvořit vlastní položku v sekci PaneFooter. Tím však ztrácíme důležitou funkci - indikátor vybraného prvku v NavigationView a s ním spojenou animaci. Podíval jsem se tedy místo toho do výchozí šablony NavigationView a našel následující:

<NavigationViewItem x:Name="SettingsNavPaneItem" Grid.Row="5">
    <NavigationViewItem.Icon>
        <SymbolIcon Symbol="Setting"/>
    </NavigationViewItem.Icon>
</NavigationViewItem>

Položka nastavení je zde definována spolu se svou ikonou, takže její upravení je snadné. To samé se však nedá říci o textu. Ukazuje se, že ovladací prvek nastavuje vlastnost NavigationViewItem.Content v kódu na lokalizovaný text Settings.

Vlastní NavigationView

Ideální řešení problému vyžaduje zdědit vlastní třídu od NavigationView. Systém aplikuje výchozí šablonu v OnApplyTemplate a v té samé chvíli nastaví Content položky nastavení na lokalizovaný text. Zde přicházíme na řadu my. Nejprve vytvořme tříduCustomizableNavigationView:

public class CustomizableNavigationView : NavigationView
{
   ...
}

Přidáme dvě dependency properties - jednu pro ikonu a jednu pro požadovaný text. Kód zatím nelze zkompilovat, protože používáme metodu SettingsItemChanged kterou implementujeme později.

public static readonly DependencyProperty SettingsItemTextProperty = DependencyProperty.Register(
       nameof(SettingsItemText), typeof(string), typeof(CustomizableNavigationView), new PropertyMetadata(default(string), SettingsItemChanged));

public static readonly DependencyProperty SettingsItemIconProperty = DependencyProperty.Register(
    nameof(SettingsItemIcon), typeof(IconElement), typeof(CustomizableNavigationView), new PropertyMetadata(default(IconElement), SettingsItemChanged));

public IconElement SettingsItemIcon
{
    get => (IconElement)GetValue(SettingsItemIconProperty);
    set => SetValue(SettingsItemIconProperty, value);
}

public string SettingsItemText
{
    get => (string)GetValue(SettingsItemTextProperty);
    set => SetValue(SettingsItemTextProperty, value);
}

Nyní můžeme přepsat virtuální metodu OnApplyTemplate. Nejdříve necháme pracovat samotný ovladací prvek voláním base implementace a pak z šablony vezmeme položku dle jména a modifikujeme ho.

private const string SettingsNavigationViewItemName = "SettingsNavPaneItem";
private NavigationViewItem _settingsItem;

protected override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    _settingsItem = (NavigationViewItem)GetTemplateChild(SettingsNavigationViewItemName);
    UpdateSettingsItemText();
}

Aktualizace položky je přímočará, pomocí aktuálních hodnot našich dependency properties.

private readonly SymbolIcon _defaultIcon = new SymbolIcon(Symbol.Setting);

private void UpdateSettingsItemText()
{
    if (_settingsItem != null)
    {
        _settingsItem.Content = SettingsItemText ?? "";
        _settingsItem.Icon = SettingsItemIcon ?? _defaultIcon;
    }
}

Nakonec pro plnohodnotnou podporu dependency properties implementujeme také property changed callback a aktualizujeme naši položku i zde.

private static void SettingsItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
    if (dependencyObject is CustomizableNavigationView navigationView)
    {
        navigationView.UpdateSettingsItemText();
    }
}

Použití výsledného ovladacího prvku je snadné. Přidáme deklaraci XML namespace do hlavičky stránky:

<Page
    ...
    xmlns:controls="using:MyApp.Controls">

A nyní použijeme místo NavigationView:

<controls:CustomizableNavigationView SettingsItemText="My text" IsSettingsVisible="True">
    <controls:CustomizableNavigationView.SettingsItemIcon>
        <SymbolIcon Symbol="Edit" />
    </controls:CustomizableNavigationView.SettingsItemIcon>
</controls:CustomizableNavigationView>

Zdrojový kód

Zdrojový kód tohoto článku je k dispozici na mém GitHubu.

Shrnutí

I když NavigationView obsahuje zabudovanou položku pro nastavení, není jednoduché její vzhled přizpůsobit. Stejného výsledku však naštěstí můžeme dosáhnout modifikací samotného ovladacího prvku.