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

Coproject – demo RIA aplikace krok za krokem, díl 14

Napsáno pro Silverlight od Augustin Šulc [19.12.2011]

V minulém díle jsme si vytvořili LazyScreen, který slouží k načítání view modelu až ve chvíli, kdy ho chce uživatel opravdu zobrazit. Dnes bych chtěl tuto funkcionalitu dotáhnout do konce a vytvořit Conductor, který bude podporovat toto zpožděné načítání view modelů a bude je také správně zavírat.

LazyConductor

Ve složce Framework vytvořte soubor LazyConductorWithCollectionOneActive.cs a vložte do něj tento kód:

public partial class LazyConductor
{
        public partial class Collection
        {
                public class OneActive : Conductor>.Collection.OneActive
                {
                }
        }
}

Když budu dále zmiňovat LazyConductor, budu tím myslet třídu OneActive.

Hlavní rozdíl mezi Conductorem a LazyConductorem je v tom, jakým způsobem zavírají vnořené view modely. Původní Conductor view model jednoduše odstraní ze své kolekce Items. Tím by však došlo k tomu, že view model zmizí z našeho menu. LazyConductor by místo toho měl tedy pouze zavolat funkci Reset() na vnořeném prvku (bude to LazyScreen) a případně přepnout na jiný již otevřený view model. Budeme tedy muset přepsat funkce pro zavírání/deaktivaci vnořených view modelů.

Přidejte do LazyConductoru následující funkci:

public override void DeactivateItem(LazyScreen item, bool close)
{
        if (item == null)
        {
                return;
        }

        if (close)
        {
                CloseStrategy.Execute(new[] { item }, (canClose, closable) =>
                        {
                                if (canClose)
                                {
                                        CloseItemCore(item);
                                }
                        });
        }
        else
        {
                ScreenExtensions.TryDeactivate(item, false);
        }
}

Když porovnáte tuto funkci s tou původní z Caliburn.Micro, zjistíte, že nedošlo k žádně změně. My totiž potřebujeme změnit hlavně funkci CloseItemCore, která je ale private, a tím pádem musíme přepsat i funkci, která ji volá. Naše nová CloseItemCore pak bude vypadat následovně:

private void CloseItemCore(LazyScreen item)
{
        if (item.Equals(ActiveItem))
        {
                var next = DetermineNextItemToActivate(item);
                ChangeActiveItem(next, true);
        }
        else
        {
                ScreenExtensions.TryDeactivate(item, true);
        }

        item.Reset();
}

Zde je důležitý poslední řádek – v původní funkci je “Items.Remove(item)”. Tím zajistíme, že vnořený view model nebude odstraněn z menu, ale dojde pouze k jeho zavření.

Aby šla solution opět zkompilovat, musíme přidat ještě tuto funkci:

protected LazyScreen DetermineNextItemToActivate(
        LazyScreen currentItem)
{
        var next = Items.FirstOrDefault(x => x != currentItem && x.IsScreenCreated);
        return next;
}

Na závěr ještě upravíme některé pomocné funkce z původního Conductoru.

public override void CanClose(Action<bool> callback)
{
        var openedItems = Items.Where(x => x.IsScreenCreated);
        CloseStrategy.Execute(openedItems, (canClose, closable) =>
                {
                        closable.Apply(CloseItemCore);
                        callback(canClose);
                });
}

protected override LazyScreen EnsureItem(LazyScreen newItem)
{
        var node = newItem as IChild;
        if (node != null && node.Parent != this)
        {
                node.Parent = this;
        }

        return newItem;
}

Důležitý je hlavně první řádek v CanClose – chceme brát v úvahu pouze již otevřené view modely.

ShellViewModel

Teď už jen zbývá upravit ShellViewModel. Upravte jeho definici takto:

public class ShellViewModel : LazyConductor.Collection.OneActive, IShell

Aby bylo možné zavírat moduly kliknutím na zavírací tlačítko v ShellView, musíme přidat potřebné funkce do view modelu:

public bool CanCloseActiveItem
{
        get
        {
                return ActiveItem != null;
        }
}

public void CloseActiveItem()
{
        DeactivateItem(ActiveItem, true);
}

Aby se správně aktualizovala hodnota CanCloseActiveItem, přidejte ještě toto do konstruktoru:

this.PropertyChanged += (s, e) =>
        {
                if (e.PropertyName == "ActiveItem")
                {
                        NotifyOfPropertyChange(() => CanCloseActiveItem);

                }
        };

ShellView

Vlastní zavírací tlačítko do ShellView už přidáme snadno. Otevřete ShellView.xaml a nejprve přidejte tuto definici:

xmlns:local="clr-namespace:Coproject.Controls"

Tlačítko pak vložte na konec gridu LayoutRoot:

"CloseActiveItem" ImageName="Close" ToolTipService.ToolTip="Close current module"
    Margin="20" HorizontalAlignment="Right" VerticalAlignment="Top" />

A to je vše! Když teď otevřete modul To Do, upravíte nějakou položku a před uložením se pokusíte view model zavřít, uvidíte dokonce hlášku o neuložených změnách:

image_4

Komentáře

ukládám komentář, vyčkejte prosím..
  1. Při publikování došlo k mírné deformaci zdrojových kódů - originál najdete zde: www.baud.cz/.../coproject-ria-c

    19.12.2011 @ 10:01

@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