# Wednesday, October 28, 2009

Sydney .NET User Group Fit Walk

Last week I had the privilege of speaking at the Sydney .NET User’s Group while in Sydney teaching a WCF Master Class. It was an outstanding group and I spoke on WF 4 and Prism (Composite Application Guidance for WPF and Sliverlight). Had some great questions and discussion with the attendees, which is always what makes a group stand out in my mind.

If you want the slides and demos for the talks, you can find them here:

First Look at WF 4:   Slides   (no pre-built demo code, all on the fly demos in VS 2010 Beta 2)

Building Composite WPF and Silverlight Applications:   Slides    Demos

The other thing that made this group stand out for me (besides the above and the fact that it is run by friend and fellow RD Adam Cogan from SSW Software) was that instead of going out for beers or something else unhealthy afterwards, a small group of us went for a short power walk for some exercise afterwards around the area of the user group meeting. You can see the route we walked here:

http://www.mapmyride.com/route/australia/earlwood/465125612609191527

Nice variation for someone who is trying to lose weight and good chance to keep the technology discussion going with some of the attendees afterwards. Thanks to Adam for organizing!





Wednesday, October 28, 2009 4:41:32 PM (GMT Standard Time, UTC+00:00)
Comments [0]  | 


 # Sunday, October 11, 2009

Australia Tour

I’m spending the next two weeks in Australia to teach some classes and speak at some user groups. This week, 12-16 Oct, I am teaching our Architect Master Class through our training partner Readify in Melbourne. Next week I’ll be teaching our WCF Master Class in Sydney 19-23 Oct, also through Readify.

The timing worked out perfectly that I also get to be the guest speaker at a user group in each city as well. On 13 October, I’ll be presenting a talk on Prism at the Victoria .NET User Group. On Wed 21 October, I’ll be giving two sessions at the Sydney .NET User’s Group on Prism and WF 4.

It’s always fun to give talks to user groups, but it is extra special fun to get to give user group talks in other countries. Really looking forward to both, as well as the classes. And spending two weeks in Australia doesn’t suck one bit either. :)





Sunday, October 11, 2009 6:02:10 AM (GMT Daylight Time, UTC+01:00)
Comments [0]  | 


 # Monday, October 5, 2009

Avoiding Memory Leaks with CompositeCommands

I had some good follow on discussions from my recent post on Graceful Shutdown with Ward Bell and Jeremy Miller about how “graceful” my shutdown example really was. They argued a point that I agreed was a big weakness with the approach: sending information back to the shell about whether to shutdown through a command parameter is not a very clean path of communication. Also, my arguments about using a command vs a pub-sub event were difficult to defend. Either one would work fine, but both suffer this same problem of obscure communication paths.

Jeremy also pointed out that the use of Prism CompositeCommands is always a little dirty and hazardous for two reasons. First, they are typically used as globals – a static singleton instance of the CompositeCommand that any control can source and any other part of the app can hook up to through the CompositeCommand.RegisterCommand method. And we all know that globals are evil.

The other problem with CompositeCommands is that it is too easy to have some chunk of code register a child command with the RegisterCommand method and then forget that now the CompositeCommand holds a reference chain back to the child command, the object on which the child command is a member, and the object on which the child command handler method exists.

In my simple example, there was the smell of a potential memory leak – an app that lets you open multiple documents, each document gets its own view and view model, and the view models are the command handlers for composite commands. Since those views are being dynamically created (in this case in response to another composite command for “New”), the registration is being done on the fly as their view models are initialized. So this begs the question of when do they get unregistered so that you don’t leak memory (in the form of residual command references into view models that are no longer being used). Prism commands do not currently use weak references like the Prism events do, although you could modify them to do so without too much difficulty.

You can get the updated code for this post here.

With the original sample code from my last post, it turned out this was not really a problem because the sample provided no way to close a view once opened. But it was just a sample focusing on the communication path for the Closing event after all. But say it was a real app and you were going to allow the user to close a document. I’ve modified the sample to allow closing a document through an X button on the tabs that the documents get opened in.

10-5-2009 8-40-21 AM

I didn’t take the time to beautifully style it, so forgive me my artistic ineptitude. But now that there is a mechanism to close the views, presumably the references to the views and view models are released when their document is closed, but if we don’t make sure we call UnregisterCommand in that process, we will have an effective memory leak – the CompositeCommands for Shutdown and Save will be holding references to the view models keeping them from being garbage collected. Each new document we open gets a new view and ViewModel instance, registers it, and over time we would see the memory of this app grow and grow.

So the direct solution is to ensure through whatever means practical to call UnregisterCommand before all refs to the ViewModel go away. Lets take a quick look at the code for this sample to get that done.

I added a data template to the shell where the TabControl is defined to add the X buttons into the tab headers:

<DataTemplate x:Key="ButtonTabItem">
    <DockPanel>
        <Button DockPanel.Dock="Right" 
          Command="{Binding Path=Content.DataContext.CloseCommand, 
          RelativeSource={RelativeSource AncestorType=TabItem}}">X
        </Button>
        <TextBlock Text="{Binding Path=Content.DataContext.DocumentTitle,
           RelativeSource={RelativeSource AncestorType=TabItem}}" VerticalAlignment="Center"/>
    </DockPanel>
</DataTemplate>

 

You can see the binding code is expecting a CloseCommand to be exposed from the ViewModel which is the DataContext of the view, and the view is the Content of the TabItem. The DocumentEditorView did not have to be modified at all, but the CloseCommand was added to the ViewModel as a DelegateCommand, as well as a Closed event to notify the controller:

public DocumentEditorViewModel()
{
    SaveCommand = new DelegateCommand<object>(OnSave, OnCanSave);
    SaveCommand.IsActive = true;
    Commands.SaveCommand.RegisterCommand(SaveCommand);

    ShutdownCommand = new DelegateCommand<CancelEventArgs>(OnShutdown);
    Commands.ShutdownCommand.RegisterCommand(ShutdownCommand);

     ...
    CloseCommand = new DelegateCommand<object>(OnClose);
 }

public DelegateCommand<object> CloseCommand { get; set; }
public event Action<DocumentEditorViewModel> Closed = delegate { };

private void OnClose(object obj)
{
    // Prompt to save, save document, etc.
    Commands.ShutdownCommand.UnregisterCommand(ShutdownCommand);
    Commands.ShutdownCommand.UnregisterCommand(SaveCommand);
    Closed(this); // Notify controller through an event, could also use a Prism event if others might care...
}

 

In this case, since the command handling for closing can be collocated with the code that did the registration, it is easy to make sure that we UnregisterCommand as part of the closing process. The controller is the one that takes care of presenting the views in this case, so the event is fired so the controller can choose to do what is appropriate, which in this case is to just remove the view from the region. That takes a bit of convoluted code to locate the view that corresponds to the ViewModel since the two are loosely coupled.

private void OnViewClosed(DocumentEditorViewModel vm)
{
    vm.Closed -= OnViewClosed;
    IRegion region = m_RegionManager.Regions[Constants.DOCUMENTREGION];
    DocumentEditorView viewToRemove = null;
    foreach (var view in region.Views)
    {
        DocumentEditorView docView = view as DocumentEditorView;
        if (docView == null) continue;
        if (docView.DataContext == vm)
        {
            viewToRemove = docView;
            break;
        }
    }
    if (viewToRemove != null)
        region.Remove(viewToRemove);
}

 

So for this simple example, it was not too terribly hard to make sure a memory leak did not happen with the use of the CompositeCommand once I added the functionality to close a view. However, what you can see emerging here is that the complexity is growing disproportionate to the work that this little sample is really doing. With a large app with many views/ViewModels, lots of commands and cross module communications, etc. this could get out of control pretty quick, and it would be easy to miss a place where you are not unregistering correctly.

The net result of all this is that the use of CompositeCommand works, but is not completely satisfying here, and especially not in a large complex  app. Modifying CompositeCommand to use weak references would be one choice, or switching to Prism events for the communications would be another choice.

Coupled with this challenge of potential memory leaks is the scenario of the previous post – graceful shutdown communications – is a another responsibility relative to view management that really cries out for some centralized management. Then you have the view activation/deactivation that I will talk about in my next post that also requires some common handling, and it turns out that what you really need is a pattern/abstraction that Prism does not yet really provide – you need a screen conductor / director of some sort that keeps track of what views are being presented, when they are activated/deactivated, whether they can close, etc.

Jeremy is working on a chapter for his upcoming book  that will cover an approach to this problem. I and others are trying to wrap our heads around the best way to tackle this problem. Ward Bell has suggested a “Close Service” that is responsible for coordinating the shutdown and he spiked an implementation of this that definitely felt cleaner than doing the back-channel communications through command or event arguments. I’ll be sure to blog more about this as my thoughts gel on it and if I can get a decent implementation put together, or will link to whoever beats me to it publicly first.

But before I get there, I do want to explain the IActiveAware mechanism of Prism, since it was important in getting the application to work correctly as well and is severely under-documented in the Prism docs. So that will be my next post, stay tuned…

You can get the updated code for this post here.





Monday, October 5, 2009 2:22:17 PM (GMT Daylight Time, UTC+01:00)
Comments [0]  | 


















May, 2013 (2)
April, 2013 (2)
March, 2013 (2)
February, 2013 (2)
January, 2013 (2)
December, 2012 (3)
November, 2012 (1)
October, 2012 (1)
August, 2012 (2)
June, 2012 (2)
May, 2012 (3)
April, 2012 (1)
March, 2012 (2)
February, 2012 (2)
January, 2012 (1)
November, 2011 (4)
October, 2011 (1)
September, 2011 (2)
August, 2011 (1)
July, 2011 (1)
May, 2011 (5)
March, 2011 (4)
February, 2011 (2)
January, 2011 (3)
November, 2010 (4)
October, 2010 (1)
September, 2010 (5)
August, 2010 (5)
July, 2010 (6)
June, 2010 (8)
May, 2010 (2)
April, 2010 (2)
January, 2010 (1)
December, 2009 (3)
November, 2009 (2)
October, 2009 (3)
September, 2009 (3)
August, 2009 (2)
July, 2009 (3)
May, 2009 (3)
April, 2009 (2)
March, 2009 (1)
February, 2009 (2)
January, 2009 (2)
December, 2008 (1)
November, 2008 (2)
October, 2008 (5)
September, 2008 (4)
August, 2008 (2)
July, 2008 (1)
June, 2008 (2)
May, 2008 (2)
April, 2008 (3)
February, 2008 (6)
January, 2008 (3)
December, 2007 (1)
November, 2007 (1)
October, 2007 (5)
September, 2007 (1)
July, 2007 (3)
June, 2007 (8)
April, 2007 (2)
March, 2007 (4)
February, 2007 (1)
December, 2006 (2)
November, 2006 (9)
October, 2006 (5)
September, 2006 (3)
August, 2006 (2)
July, 2006 (4)
June, 2006 (5)
May, 2006 (10)
April, 2006 (4)
March, 2006 (2)
February, 2006 (12)
January, 2006 (7)
December, 2005 (2)
November, 2005 (15)
October, 2005 (6)
September, 2005 (7)
August, 2005 (3)
July, 2005 (10)
June, 2005 (11)
May, 2005 (7)
April, 2005 (8)
March, 2005 (6)
February, 2005 (2)
January, 2005 (6)
December, 2004 (3)
November, 2004 (5)
October, 2004 (2)
September, 2004 (5)
August, 2004 (13)
July, 2004 (6)
June, 2004 (14)
May, 2004 (17)
April, 2004 (12)
March, 2004 (8)
February, 2004 (10)
January, 2004 (14)
December, 2003 (9)
November, 2003 (13)
October, 2003 (3)

Sign In
Copyright © 2006-2012 Brian Noyes. All rights reserved.

designed by NUKEATION STUDIOS