Coproject - a RIA Caliburn.Micro demo, part 14

In the last part, we created LazyScreen class so that we can load our view models into memory only when they are requested and not right after the application start. In this part, I want to create LazyConductor – a conductor that will support closing of child LazyScreens and then opening new ones instead of removing them from its Items collection.

Please note that this is just a simple demo of an idea I had about lazy screens/conductors and using it in production might need additional code. But if you use it, please let me know (I plan to use it on a real project too, so this post might be updated later according to my experience).

LazyConductor

In the Framework folder, create a new file called LazyConductorWithCollectionOneActive.cs and put the following into it:

public partial class LazyConductor<TScreen, TMetadata>
{
        public partial class Collection
        {
                public class OneActive : Conductor<LazyScreen<TScreen, TMetadata>>.Collection.OneActive
                {
                }
        }
}

From now, every time I refer to LazyConductor, I mean the OneActive class.

Since the difference between the original conductor and our lazy conductor is in the way they close their children, we will override functions dealing with closing/deactivating stuff. When a child is closed, the original conductor removes it from its children collection. Lazy conductor should only call Reset() on the child and activate another one.

Add overriden DeactivateItem function:

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

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

If you compare it to Caliburn.Micro source code, you will notice that there is no change. The reason for that is that we just need to alter CloseItemCore function and since it is private, there is no other way.

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

        item.Reset();
}

The last line is why we did that – original conductor said ‘Items.Remove(item)’. To make the solution build again, we must add one more function:

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

Finally add these functions:

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<TScreen, TMetadata> EnsureItem(LazyScreen<TScreen, TMetadata> newItem)
{
        var node = newItem as IChild;
        if (node != null && node.Parent != this)
        {
                node.Parent = this;
        }

        return newItem;
}

The first line of CanClose is quite important here – we want to check only already opened children.

ShellViewModel

To use our new lazy conductor, update definition of ShellViewModel as follows:

public class ShellViewModel : LazyConductor<IModule, IModuleMetadata>.Collection.OneActive, IShell

Finally, I want to add the possibility to close opened modules. Add this to ShellViewModel:

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

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

To get the guard property updated, put this code into the class constructor:

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

                }
        };

ShellView

The last thing to do is to add a close button to ShellView. Open ShellView, add this definition into its beginning:

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

and put this control to the end of the LayoutRoot grid:

<local:ImageButton x:Name="CloseActiveItem" ImageName="Close" ToolTipService.ToolTip="Close current module"
                                        Margin="20" HorizontalAlignment="Right" VerticalAlignment="Top" />

And we are done! Open To Do module, edit an item and then try to close the whole module – you will see a warning that you have unsaved changes in the module.

image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值