ObservableObject
Je ObservableObject
základní třídou pro objekty, které jsou pozorovatelné implementací INotifyPropertyChanged
a INotifyPropertyChanging
rozhraní. Dá se použít jako výchozí bod pro všechny druhy objektů, které potřebují podporovat oznámení o změnách vlastností.
Rozhraní API platformy:
ObservableObject
,TaskNotifier
TaskNotifier<T>
Jak to funguje
ObservableObject
má následující hlavní funkce:
- Poskytuje základní implementaci pro
INotifyPropertyChanged
aINotifyPropertyChanging
zveřejněníPropertyChanged
událostí aPropertyChanging
událostí. - Poskytuje řadu
SetProperty
metod, které lze použít k snadnému nastavení hodnot vlastností z typů dědění zObservableObject
a k automatickému vyvolání příslušných událostí. - Poskytuje metodu
SetPropertyAndNotifyOnCompletion
, která je analogickáSetProperty
, ale s možností nastavitTask
vlastnosti a vyvolat události oznámení automaticky při dokončení přiřazených úkolů. - Zveřejňuje
OnPropertyChanged
metody aOnPropertyChanging
metody, které je možné přepsat v odvozených typech a přizpůsobit způsob vyvolání událostí oznámení.
Jednoduchá vlastnost
Tady je příklad implementace podpory oznámení pro vlastní vlastnost:
public class User : ObservableObject
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
}
Zadaná SetProperty<T>(ref T, T, string)
metoda zkontroluje aktuální hodnotu vlastnosti a aktualizuje ji, pokud se liší, a pak také vyvolá příslušné události automaticky. Název vlastnosti se automaticky zaznamenává pomocí atributu [CallerMemberName]
, takže není nutné ručně určit, která vlastnost se aktualizuje.
Zabalení nepozorovatelného modelu
Běžným scénářem, například při práci s databázovými položkami, je vytvoření obtékání "bindable" modelu, který předává vlastnosti databázového modelu a v případě potřeby vyvolá oznámení o změně vlastnosti. To je potřeba také v případě, že chcete do modelů vložit podporu oznámení, která rozhraní neimplementují INotifyPropertyChanged
. ObservableObject
poskytuje vyhrazenou metodu, která usnadňuje tento proces. V následujícím příkladu User
je model přímo mapující databázovou tabulku bez dědění z ObservableObject
:
public class ObservableUser : ObservableObject
{
private readonly User user;
public ObservableUser(User user) => this.user = user;
public string Name
{
get => user.Name;
set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
}
}
V tomto případě používáme SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string)
přetížení. Podpis je o něco složitější než předchozí – to je nezbytné k tomu, aby byl kód stále velmi efektivní, i když nemáme přístup k záložnímu poli, jako je v předchozím scénáři. Jednotlivé části tohoto podpisu metody můžeme podrobně projít, abychom porozuměli roli různých komponent:
TModel
je argument typu označující typ modelu, který zabalíme. V tomto případě to bude našeUser
třída. Všimněte si, že toto explicitní zadání nepotřebujeme – kompilátor jazyka C# to automaticky odvodí tím, jak metoduSetProperty
vyvoláváme.T
je typ vlastnosti, kterou chceme nastavit.TModel
Podobně jako , to je odvozeno automaticky.T oldValue
je první parametr a v tomto případě používámeuser.Name
k předání aktuální hodnoty této vlastnosti, kterou zabalíme.T newValue
je nová hodnota nastavená na vlastnost a zde předávámevalue
, což je vstupní hodnota v rámci setter vlastnosti.TModel model
je cílový model, který zabalíme, v tomto případě předáváme instanci uloženouuser
v poli.Action<TModel, T> callback
je funkce, která se vyvolá, pokud se nová hodnota vlastnosti liší od aktuální hodnoty a vlastnost musí být nastavena. To provede tato funkce zpětného volání, která přijímá jako vstup cílový model a novou hodnotu vlastnosti, která se má nastavit. V tomto případě pouze přiřazujeme vstupní hodnotu (kterou jsme volalin
) vlastnostiName
(tím).u.Name = n
Je důležité se zde vyhnout zachycení hodnot z aktuálního oboru a pracovat pouze s hodnotami zadanými jako vstup pro zpětné volání, protože to umožňuje kompilátoru jazyka C# ukládat funkci zpětného volání do mezipaměti a provádět řadu vylepšení výkonu. Důvodem je to, že k poli tadyvalue
nebo parametru v setteru nemáme jen přímý přístupuser
, ale místo toho používáme jenom vstupní parametry pro výraz lambda.
Metoda SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string)
vytváří tyto vlastnosti obtékání velmi jednoduché, protože se postará o načtení a nastavení cílových vlastností při poskytování extrémně kompaktního rozhraní API.
Poznámka:
Ve srovnání s implementací této metody pomocí výrazů LINQ, konkrétně prostřednictvím parametru typu Expression<Func<T>>
místo parametrů stavu a zpětného volání, zlepšení výkonu, které lze dosáhnout tímto způsobem, jsou opravdu významné. Konkrétně je tato verze ~200x rychlejší než ta, která používá výrazy LINQ, a neuvádí vůbec žádné přidělení paměti.
Zpracování Task<T>
vlastností
Je-li vlastnost Task
také nutné vyvolat událost oznámení po dokončení úkolu, aby byly vazby aktualizovány ve správný čas. Např. zobrazení indikátoru načítání nebo jiné informace o stavu operace reprezentované úkolem. ObservableObject
má rozhraní API pro tento scénář:
public class MyModel : ObservableObject
{
private TaskNotifier<int>? requestTask;
public Task<int>? RequestTask
{
get => requestTask;
set => SetPropertyAndNotifyOnCompletion(ref requestTask, value);
}
public void RequestValue()
{
RequestTask = WebService.LoadMyValueAsync();
}
}
SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>, Task<T>, string)
Zde se metoda postará o aktualizaci cílového pole, monitorování nového úkolu, pokud je k dispozici, a vyvolání události oznámení po dokončení úkolu. Tímto způsobem je možné vytvořit vazbu na vlastnost úkolu a upozornit na změny stavu. Jedná se TaskNotifier<T>
o speciální typ vystavený ObservableObject
tím, že zabalí cílovou Task<T>
instanci a povolí potřebnou logiku oznámení pro tuto metodu. Typ TaskNotifier
je také k dispozici přímo, pokud máte pouze obecný Task
.
Poznámka:
Metoda SetPropertyAndNotifyOnCompletion
je určena k nahrazení použití NotifyTaskCompletion<T>
typu z Microsoft.Toolkit
balíčku. Pokud byl tento typ použit, může být nahrazen pouze vnitřní Task
(nebo Task<TResult>
) vlastnost, a pak lze metodu SetPropertyAndNotifyOnCompletion
použít k nastavení jeho hodnoty a vyvolat změny oznámení. Všechny vlastnosti vystavené typem NotifyTaskCompletion<T>
jsou k dispozici přímo na Task
instancích.
Příklady
- Podívejte se na ukázkovou aplikaci (pro více architektur uživatelského rozhraní) a podívejte se na sadu nástrojů MVVM v akci.
- Další příklady najdete také v testech jednotek.