Úprava viditelnosti pojmenovaných XAML elementů

WinUI XAML WPF

6 years ago

Atribut x:Name v XAMLu vytváří pro elementy členské položky třídy, které lze použít pro přístup k ovladacím prvkům z kódu. Narozdíl od WPF však v UWP jsou tyto položky třídy definovány jako private, což znamená, že k nim je možné přistupovat pouze z třídy samotné. Pokud vezmeme v potaz, že z hlediska architektury by to byl špatný nápad, je možné toto chování změnit?

Výchozí chování

Definujme v XAMLu jednoduchý TextBlock.

<TextBlock x:Name="InaccessibleTextBlock" />

Co se nyní stane, pokud vytvoříme novou třídu, která bude chtít změnit na jí předané stránce text tohoto ovladacího prvku?

public static class OutsideAccess
{
    public static void ChangeTexts(MainPage page)
    {
        page.InaccessibleTextBlock.Text = "Accessed"; //does not work!
    }
}

Aplikaci takto nelze zkompilovat, protože položka není přístupná kvůli její viditelnosti. Otevřme automaticky generovaný soubor MainPage.g.i.cs (ve složce obj), abychom se podívali, k čemu skutečně dochází:

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks"," 14.0.0.0")]
private global::Windows.UI.Xaml.Controls.TextBlock InaccessibleTextBlock;

Vidíme, že položka je deklarována jako private.

Direktiva x:FieldModifier

Pro změnu výchozího chování XAML generátoru kódu můžete použít direktivu x:FieldModifier. Ta vám umožňuje specifikovat přesně který modifikátor přístupu by položka měla mít.

<TextBlock x:FieldModifier="public" x:Name="AccessibleTextBlock" />

Nyní již je snadno možné přistoupit k položce i z jiné třídy:

public static class OutsideAccess
{
    public static void ChangeTexts(MainPage page)
    {
        page.AccessibleTextBlock.Text = "Accessed";
    }
}

Kromě voleb public a private můžete hodnotu direktivy nastavit i jako internal nebo protected. Výsledek naší snahy můžeme ověřit i ve vygenerovaném zdrojovém kódu:

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks"," 14.0.0.0")]
public global::Windows.UI.Xaml.Controls.TextBlock AccessibleTextBlock;

WPF

Pokud přemítate, jaké je výchozí chování ve WPF, odpověď je nasnadě! Konvence WPF nastavuje všechny pojmenované položky jako internal:

[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBlock InaccessibleTextBlock;

I zde však můžete použít direktivu x:FieldModifier pro úpravu viditelnosti stejně jako v UWP.

Zdrojový kód

Ukázkový zdrojový kód k tomuto článku najdete na mém GitHubu.

Shrnutí

x:FieldModifier je zajímavá a nepříliš známá direktiva XAMLu. Přesto by její použití mělo být spíše výjimečné, protože členské proměnné viditelnosti jiné než private nejsou nikdy dobrým nápadem. O to překvapivější je, že v případě WPF byla jako výchozí zvolena viditelnost internal.