XAML.cz Magazín moderních technologií založených na XAML

Novinky ve WPF 4 – Binding na DynamicObject

Napsáno pro WPF od Jarda Jirava  [18.05.2010]

Blížím se ke konci povídání o novinkách ve WPF 4, které jsem věnoval bindingu. V minulém článku jsem zmiňoval, že se dnes podívám na novinku nejen co se týká WPF samotného, ale jedná se i o novinku v samotném .NET Frameworku verze 4.0.

DynamicObject

O tom, že se v této nové verzi frameworku objeví nový typ dynamic určitě slyšel snad každý, kdo se alespoň maličko zajímá o dění a vývoj v .NET Frameworku. Nejsem si však už tak jist, že se tolik prezentovala možnost vytvořit si vlastní dynamický objekt a pomocí přepsání příslušných metod tak reagovat na zprávy, které směrem k takovému námi vytvořenému objektu přicházejí. Takový nými vytvořený objekt musí dědit od třídy DynamicObject.

Pro potřeby WPF a možnosti bindingu na dynamické objekty je pak dostačující, pokud je implementováno rozhraní IDynamicMetaObjectProvider.

Binding s DynamicObject

Jak potom takový binding vypadá a co je třeba udělat? Jako první si musíme vytvořit nějaký náš vlastní objekt, který bude dědit právě od třídy DynamicObject. Pro samotnou ukázku si vytvořím velice jednoduchý objekt, který bude pouze uchovávat hodnoty vlastností, které si “vymyslíme” a které bude mít uživatel možnost vložit do našeho objektu.

Dovolím si malé odbočení, které se týká právě možnosti použití takovéhoto scénáře, kdy je vhodné použít DynamicObject. Pokud jste nebyli na posledním neformálním setkání, a že vás bude asi většina, tak jsme právě tam diskutovali možnosti využití obdobného principu, kdy není v době vývoje aplikace jasné, které vlastnosti vlastně bude daný objekt mít a tyto se tvoří skutečně dynamicky až za běhu aplikace. Pro to se samozřejmě musí nějakým způsobem upravit i uživatelské prostředí, ale to už je také o metadatech, která ke každé vlastnosti máme a to je v tuto chvíli za hranicí článku.

Nyní tedy ukáži, jak takový jednoduchý objekt vytvořit, aby nám dokázal uchovat jakoukoliv hodnotu pro vlastnost, kterou předem neznáme.

public class DynObject: DynamicObject {

    private Dictionary<string, object> _values = new Dictionary<string, object>();

    public override bool TrySetMember(SetMemberBinder binder, object value) {
        var prop = binder.Name.ToLowerInvariant();
        _values[prop] = value;
        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        var prop = binder.Name.ToLowerInvariant();

        return _values.TryGetValue(prop, out result);
    }
}

Jak vidíte, mám poděděno od třídy DynamicObject a přepsány dvě metody TrySetMember a TryGetMember. První nám zajišťuje interakci s objektem při setování vlastnosti ta druhá, jak již jistě tušíte, při požadavku na získání hodnoty vlastnosti. Na tomto místě by asi bylo dobré říci, že volání těchto metod se provede až v okamžiku, kdy se zjistí, že na takovémto objektu nejsou definovány statické vlastnosti stejného jména.

Samotné hodnoty si pak uchovávám v Dictionary<TKey, TValue> a provádím menší zásah, kdy vlastnost převádím na malá písmena, čímž samozřejmě mohu narušit konzistenci programu a je třeba takovou situaci vést v patrnosti při dokumentaci. Na druhou stranu, pokud budu používat Binding, který není silně typový v návrháři, tak si mohu ušetřit nějaký čas při dohledávání špatně zapsaných vlastností.

V tuto chvíli tedy mám objekt, který mohu použít, pro jednoduchost si vytvořím instanci této třídy v codebehindu okna a předám ji jako DataContext, abych mohl využít možností Bindingu.

public partial class MainWindow : Window {

    private dynamic _dynamic = new DynObject();

    public MainWindow() {
        InitializeComponent();
        Loaded += new RoutedEventHandler(MainWindow_Loaded);
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e) {
        DataContext = _dynamic;
    }

    private void Button_Click(object sender, RoutedEventArgs e) {
        string s = string.Format("{0}, {1}", _dynamic.Jmeno, _dynamic.Prijmeni);
        Debug.WriteLine(s);
    }
}

V této ukázce tak mám definovánu členskou proměnnou a obsluhu dvou událostí. V obsluze události Loaded pouze předám instanci této proměnné do DataContextu okna. V obsluze události kliku na tlačítko si pak vypíši hodnoty, které zadal uživatel pomocí TextBoxů.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Button Content="Save" Click="Button_Click" Grid.Row="0" />
    <Border Grid.Row="1">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="150" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <TextBlock Text="Jméno" Grid.Row="0" Grid.Column="0" />
            <TextBox Text="{Binding Jmeno}" Grid.Row="0" Grid.Column="1" />
            <TextBlock Text="Příjmení" Grid.Row="1" Grid.Column="0" />
            <TextBox Text="{Binding Prijmeni}" Grid.Row="1" Grid.Column="1" />
        </Grid>
    </Border>
</Grid>

 

Z přiložené ukázky můžete vidět, že ač jsme nikde nenadefinovali vlastnosti na které ve výsledku bindujeme, tento binding se provede a bude plně funkční. Pokud bychom tak v deklaraci XAMLu přidali další TextBox a nabindovali na další vlastnost, hodnota pro tuto vlastnost se taktéž uloží do instance třídy poděděné od DynamicObject a my budeme schopni si tuto hodnotu kdekoliv v programu následně přečíst.

Závěrem

Tímto článkem jsem tak uzavřel krátký seriál o nových možnostech bindingu ve WPF 4.0, v některém příštím článku se pak budu věnovat dalším novinkám, které můžete ve WPF využít.

Komentáře

ukládám komentář, vyčkejte prosím..
  1. Buďte první, kdo napíše komentář.

@xamlcz

  • RT @jvanrhyn: XAML, It's a bit like olives. Takes a while to get used to. But once you're used to it. It is actually pretty good. <3 XAML
  • RT @moser_christian: WPF Inspector 0.9.7 is released. It supports .NET 3.5 and 4.0 The project is now open source and available on CodeP ...
  • Jeff Handley oznámil vydání WCF RIA Services v.1.0 SP1 RTM http://bit.ly/gOgckn ke stažení na http://bit.ly/gVAXdK
  • jedna výzva pro Brno. Byl někdo z vás na přednášce o RIA v MS Akvárku? Dejte o sobě vědět. Děkuji
  • také jste uvažovali o tom, že zkusíte na projekt použít Caliburn Micro nebo naopak Prism 4? A co tak obojí, šlo by to nebo ne? Již brzy