Minulý týden jsem publikoval článek o x:DefaultBindMode
v UWP XAMLu. Uno Platform, multiplatformní UWP/WinUI bridge pro Android, iOS a WebAssembly, v tu dobu ještě tuto funkci neimplementoval. Co se však stalo potom bylo doslova fascinující. Za pouhý jeden den byla tato funkce nejen implementována, ale i mergnuta (spolu s několika dalšími vylepšeními jako je BindBack
!) a dostupná v novém prerelease balíčku na NuGetu. Wow!
Ukázka na WASM
Použijeme jednu z ukázek z mého minulého článku:
A po spuštění na WebAssembly s Uno Platform to jednoduše funguje – TextBlock
používá OneWay
binding, zatímco TextBox
jej explicitně přepisuje s TwoWay
. To samé na všech ostatních Uno Platform targetech.
Jak to dokázali?
Pro zajímavost se podívejme, jak tým Uno Platform dokázal za tak krátkou dobu x:DefaultBindMode
implementovat. Vše, co tato funkčnost vyžaduje, je zahrnuto v třídě XamlFileGenerator, která zajišťuje generování C# kódu z XAMLu. x:DefaultBindMode
funguje v jako "scope". To znamená, že deklarovaný mód na elementu platí pro něj a všechny jeho potomky, pokud není explicitně přepsán někde níže. Protože XAML soubor je parsován postupem "do hloubky", nejvhodnější datovou strukturou pro reprezentaci scope je zásobník. A to také Uno Platform používá pro udržování stavu zde:
Všimněme si, že je zásobník inicializován s hodnotou OneTime
– to odpovídá výchozímu módu x:Bind
v UWP. Takže pokud nikde nepoužijeme x:DefaultBindMode
, OneTime
je výchozí "fallback". Jak vložíme nový "výchozí mód" na zásobník? Zde přichází v činnost metoda TrySetDefaultBindMode
:
Nejprve metoda přečte hodnotu atributu DefaultBindMode
z definice elementu. Pokud atribut na elementu není deklarován, metoda skončí a vrací null
. Pokud deklarování je, proběhne jeho validace pomocí metody IsValid
. Pokud je hodnota DefaultBindMode
validní, vložíme ji na vrchol zásobníku (vstupujeme do nového scope). Metoda pak vrací instanci DisposableAction
, která implementuje rozhraní IDisposable
a při jejím Dispose()
vykoná předanou lambda funkci. V tomto případě se odebere vrchní hodnota ze zásobníku. Tento způsob implementace skvěle napomáhá čitelnosti kódu, protože to umožňuje jednoznačně ukázat, že metoda vytváři nový "scope", když ji voláme uvnitř using
bloku:
Poznamenám ještě, že v případě že metoda vrátí null
(pro případ kdy DefaultBindMode
není nastaven), je prostě ignorován a using
se nepokouší na konci bloku volat Dispose()
. Získat aktuálního mód je možné přes jednoduchou metodu která volá pouze Peek
aby vrátila vrchol zásobníku:
Metoda GetDefaultBindMode()
je pak volána při stavbě vyhodnocovací funkce x:Bind
:
Jednoduše řečeno, pokud výraz x:Bind
explicitně určí Mode
, má přednost. V opačném případě použije aktuálně platný DefaultBindMode
. A to je vše, přátelé! Jak jsme mohli vidět, celá funkce byla implementována velmi elegantně a díky Uno Platform máme opravdu #WinUIEverywhere!