I have to say that my last year at Microsoft has been a little strange. Since the Acropolis project, err, took a new direction I've been working on a few different projects which at first glance were all kind-of related but distinctly separate yet, it turns out, all leading to same point in time and space.
I spent the last few months in late 2007 on a team looking at componentization and composition. We spent a lot of time in Acropolis trying to deliver a component model that directly and elegantly supported composition but, unfortunately, a lot of that work didn't see the light of day, though we had most of it implemented and working nicely.
As is the way at Microsoft there are often a number of teams all working in the same area. As well as the Acropolis component model, there was Object Builder (which later evolved into Unity) from the Patterns and Practices folks, the Managed Add-In Framework (part of .Net 3.5) and other internal efforts that were looking at other aspects of composition, including structural type composition (duck typing) and dependency management. So our first task was to look at these component model efforts and see if we could reconcile them to come up with a singe story.
That team later became the Application Framework Core team and the unified component model became the Managed Extensibility Framework (MEF). An early drop of MEF was released recently on CodeGallery. Krzysztof Cwalina, lead PM on the MEF team, wrote a great introduction to MEF on his blog. And, in a happy turn of events, P&P's own Glenn Block joined the MEF team recently, strengthening the already strong ties between the P&P team and the MEF team. More on this a little later...
As well as looking at the component model, I started to look closely at Silverlight and the world of RIA's. In particular, I spent a lot of time trying to figure out what an application model would look like in Silverlight land. The RIA space is particularly interesting from an app model perspective - RIA's are very appealing to both web developers and to client app developers, but for different reasons...
So the first half of this year was spent looking at Navigation, Deep Linking, Data Validation, Placeholder Based Visual Composition and Separated Presentation Patterns in Silverlight. For those attending PDC this year, there are a number of sessions that will be describing this work, as well some other really exciting work being done by the UI Framework team.
During this time I was also involved with the Patterns and Practices Prism project, which later become the Composite Application Guidance for WPF. Prism provides a range of guidance - including documentation, supporting library code, a reference implementation, quick-starts and hands-on labs - to help folks build composite client applications in WPF. The P&P folks did a great job with Prism and managed to deliver amazing guidance in a very short period of time.
And that brings us to the latest news. A couple of weeks ago I left the UI Framework team to become an architect in the P&P team! I can honestly say that I have never been more excited to join a team at Microsoft. I have worked very closely with the P&P team over the years on a number of projects - including the Smart Client Architecture Guide, the Offline Application Block, the Composite UI Application Block and most recently Prism - but to be a true part of the team is a great privilege.
We have a very exciting year ahead of us too: We're busy gearing up for the next release of Prism (which will focus on patterns and guidance for composite applications in Silverlight as well as WPF) and on the next release of EntLib. I'm also working with Glenn and the MEF team to see how Unity can evolve to complement MEF once it's released (in fact Glenn and I are scheduled to deliver a session at the PDC to talk about our initial thinking here), and we're also working on emerging guidance for cloud based computing. And that just scratches the surface...
So, hopefully, spending the last year thinking about component models to support composition, and application models for Silverlight and WPF will stand me in good stead :-)
At my TechEd session in Orlando over the summer, I showed a version of the Acropolis Notepad sample where I plugged in an MDI-like floating layout pane and added support for viewing XPS documents. I’ve had many requests for this sample code and I am happy to report that I have finally managed to get some time this week to tidy up the code, package it up and write this short summary.

You can download the code from here, and some sample XPS documents to play with here.
The sample runs on the August CTP of Acropolis, which in turn requires Visual Studio 2008 Beta 2. There are three projects within the sample solution – the Application itself, the XPS document part & view, and the floating layout pane and its support classes. The following descriptions assume that you are starting from the Notepad sample that ships alongside the Acropolis CTP here.
Let’s walk through some of the more important parts of the code, starting with the XPS document part…
XPS Document Part & View
The XPS document part is actually very simple, thanks in part to the excellent XPS support provided by WPF. In our sample we have a document part class that’s responsible for loading the XPS document from disk, and a document part view class which is responsible for displaying the XPS document to the user.
The XPSDocumentPart class looks like this:
[DefaultView( typeof( XPSDocumentPartView ) )]
public partial class XPSDocumentPart : DocumentPartBase
{
private XpsDocument _xpsDocument;
public XPSDocumentPart()
{
InitializeComponent();
// The document is readonly so disable the Save
// and the SaveAs commands.
base.SaveCommandInternal.Enabled = false;
base.SaveAsCommandInternal.Enabled = false;
}
protected override void OnLoad( System.IO.Stream input )
{
_xpsDocument = new XpsDocument( base.FileName,
System.IO.FileAccess.Read );
Document.Value = _xpsDocument.GetFixedDocumentSequence();
}
…
}
The XPSDocumentPart class represents an XPS document within the application and so derives from DocumentPartBase. In this sample, the XPS documents are read-only so we disable the Save and SaveAs command provided by the base class. The only other thing we need to do then is to read the XPS document contents and get the fixed document sequence to expose to the view.
The XPSDocumentPartView class is responsible for displaying the document to the user. In this sample, the view class simply uses the WPF DocumentViewer control which provides a lot of functionality out of the box, allowing the user to zoom, scroll, pan and search through the XPS document. The XAML for the view class is very simple:
<Border BorderThickness="6" BorderBrush="Gray">
<DocumentViewer Document="{Binding Path=Document.Value, Mode=OneWay}"/>
</Border>
The WPF DocumentViewer control does all of the hard work of course, we only have to bind its Document property to the underlying XPS document provided by the Document connection point. You could imagine providing a custom rendering of the XPS document if you needed one, say to enable read-write creation of XPS documents using a custom editor.
The last thing we need to do is to register the XPS Document Part with the app’s document manager so it knows to associate XPS documents with it. We register the part, along with a file extension in Application.xaml like this:
<Afx:DocumentManager x:Name="DocumentManager">
<Afx:DocumentManager.Templates>
<Afx:DocumentTemplate x:Name="DocumentTemplate"
Type="{x:Type xps:XPSDocumentPart}" FileExtension=".xps" />
</Afx:DocumentManager.Templates>
</Afx:DocumentManager>
The Acropolis Notepad sample demonstrates how you can build document centric applications and extend them by registering new document types. Of course not all applications are document style applications, but the power of Acropolis lies in the ability of its modular approach to support these kinds of common patterns.
So that’s the XPS part of the sample. Let’s take a look at the (much more interesting) floating layout pane.
Floating Layout Pane
The Acropolis architecture is aimed at supporting the notion of ‘separation of concerns’. It provides a modular framework that lets you plug in or swap out different strategies for different parts of the application, like layout, navigation, authentication, etc. The floating layout pane class is an example of how to implement a custom layout strategy.
In this sample, we’re going to replace the Notepad’s default tab layout strategy with one that provides free-floating windows that we can drag around (similar to the MDI layouts of ye olde MFC). It also provides a fly-out ‘docking area’ where minimized windows are displayed along with a pre-view thumbnail.
Basic Behavior
There are a couple of additional classes in the sample that provide support for resizing, dragging and for managing the pre-view thumbnail, but the main class is the FloatingLayoutPane class. This class implements the core layout strategy and defines how our custom layout pane control interacts with the rest of the Acropolis architecture.
public partial class FloatingLayoutPane :
Microsoft.Acropolis.Windows.LayoutPane
{
…
}
The UI for the FloatingLayoutPane class essentially consists of a WPF Canvas control embedded in a grid:
<Canvas x:Name="_canvas" Grid.Row="0" Grid.Column="0"
AllowDrop="False"
PreviewMouseLeftButtonDown="CanvasPreviewMouseLeftButtonDown"
PreviewMouseMove="CanvasPreviewMouseMove"
PreviewMouseLeftButtonUp="CanvasPreviewMouseLeftButtonUp">
</Canvas>
Notice that the FloatingLayoutPane class derives from the Acropolis LayoutPane base class. This base class implements the basic behavior of all of Acropolis’ layout panes (including the SplitLayoutPane and the TabLayoutPane classes that ship with the Acropolis CTP). It provides a number of virtual methods that we will need to override in order to implement our floating pane layout strategy.
The two most important virtual methods are OnChildPaneInsertRequest and OnChildPaneRemoveRequest. These methods are called whenever the application’s navigation manager adds a new part to the current scope and wants a view for it to be added to the UI, or when a part is closed and its view is to be removed from the UI. A custom layout pane overrides these methods so that it can arrange the new child pane, using whatever strategy it implements.
In our case, when a new child pane is added to the floating layout pane, we need to add it the Canvas control, set its position and make sure that the child pane displays a chrome with a collapse button. So the first part of our OnChildPaneInsertRequest implementation looks like this:
protected override void OnChildPaneInsertRequest( object sender,
PaneCollectionItemCancelableActionEventArgs e )
{
base.OnChildPaneInsertRequest( sender, e );
Pane pane = e.Item;
Canvas.SetLeft( pane, newPanePosition.X );
Canvas.SetTop( pane, newPanePosition.Y );
pane.ShowChrome = true;
pane.ShowCollapse = true;
pane.Width = newPaneSize.Width;
pane.Height = newPaneSize.Height;
this._canvas.Children.Add( pane );
To support resizing of child panes within the floating layout pane, we also create a ResizingAdorner and attach it to the child pane. The ResizingAdorner class is an adorner that allows the user to resize the element that’s being adorned, in our case the child pane:
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( pane );
adornerLayer.Add( new ResizingAdorner( pane ) );
The ResizingAdorner adds a Thumb control to the bottom right hand corner of the child pane so that the user can resize it using the mouse.
Finally, to support the animated minimize effect, we subscribe to the PaneStateChanged and PaneStateChanging events and register a name with the layout pane so we can animate it:
pane.PaneStateChanged += new RoutedEventHandler( OnPaneStateChanged );
pane.PaneStateChanging += new EventHandler<PaneStateChangingCancelEventArgs>
( OnPaneStateChanging );
// Assign a unique name to the child pane.
pane.Name = String.Format( "ChildPane{0}", paneCounter++ );
this.RegisterName( pane.Name, pane );
}
Note that the LayoutPane base class includes virtual methods that are called when the user minimizes or maximizes a child pane that it is managing. Unfortunately, the current implementation does not pass a reference to the actual child pane that the event relates to. Doh! This is a bug :-). We can work around it though by subscribing directly to the events above.
The OnChildPaneRemoveRequest method is called when the user closes a child pane. We need to unsubscribe from the child pane’s events, remove the adorner, unregister its name, and finally remove it from the canvas control:
protected override void OnChildPaneRemoveRequest( object sender,
PaneCollectionItemCancelableActionEventArgs e )
{
base.OnChildPaneRemoveRequest( sender, e );
Pane pane = e.Item;
pane.Visibility = Visibility.Collapsed;
pane.PaneStateChanged -= new RoutedEventHandler( OnPaneStateChanged );
pane.PaneStateChanging -= new EventHandler<PaneStateChangingCancelEventArgs>
( OnPaneStateChanging );
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( pane );
adornerLayer.Remove( new ResizingAdorner( pane ) );
this.UnregisterName( pane.Name );
this._canvas.Children.Remove( pane );
}
That pretty much completes the core behavior of the floating layout pane. You can implement any number of different layout strategies by overriding the two methods described above and specifying exactly how the child panes are laid out. Of course, the actual details of the layout strategy can be quite complex, but the architecture allows you to encapsulate this complexity in a re-usable pane control that you can very likely re-use in many different types of applications.
The sample FloatingLayoutPane class implements a couple of other features that enhance the user experience – the ability for the user to drag and drop panes around the canvas, and a fly-out docking area for minimized windows. Let’s take a quick look at how these are implemented.
Child Pane Drag & Drop
The FloatingLayoutPane control allows the user to drag child panes around its canvas by clicking and dragging the child pane via its chrome area. Essentially this works by subscribing to the mouse left button up/down and mouse move events and updating the child pane’s position on the canvas control.
In the left mouse button down event handler, we first check to see if the user clicked on the child pane’s chrome area (and not any of the collapse, view or command buttons). The Pane class conveniently provides an IsDragArea method for this. Once we determined that the user has clicked on the chrome area, we just need to keep track of the mouse position and capture the mouse:
if ( pane.IsDragArea( (FrameworkElement)e.OriginalSource ) )
{
isDown = true;
startPoint = e.GetPosition( _canvas );
originalElement = e.Source as PartPane;
_canvas.CaptureMouse();
e.Handled = true;
}
In the mouse move event handler, we make sure that the mouse has moved more than the minimum amount and then start the drag operation proper by calling the DragStarted method. This method creates a DropPreviewAdorner for the child pane – in this implementation, it’s actually this adorner that we are dragging around and not the child pane itself. The DropPreviewAdorner provides a visual of the child pane that it represents (using a VisualBrush), but otherwise is just a simple adorner.
As the user continues to drag the mouse around, we update the adorner’s position:
Point currentPosition =
System.Windows.Input.Mouse.GetPosition( _canvas );
overlayElement.LeftOffset = currentPosition.X - startPoint.X;
overlayElement.TopOffset = currentPosition.Y - startPoint.Y;
When the user releases the left mouse button, we tidy up the adorner and update the position of the original child pane:
AdornerLayer.GetAdornerLayer( overlayElement.AdornedElement )
.Remove( overlayElement );
_canvas.Children.Remove( overlayElement );
if ( !canceled )
{
Canvas.SetLeft( originalElement, originalLeft + overlayElement.LeftOffset );
Canvas.SetTop( originalElement, originalTop + overlayElement.TopOffset );
}
NOTE: This implementation may not be the best way to implement drag behavior for child panes! Performance can be slow if the pane being dragged is large or complex. I am not sure why this is so yet – it needs more investigation and/or a WPF expert :-). I suspect it's something to do with using the Visual Brush...
That’s pretty much it for the dragging functionality – take a look at the code for the fine details, but it’s pretty simple thanks to WPF’s canvas control and event bubbling and tunneling.
Fly-Out Docking Area
The FloatingLayoutPane has a fly-out docking area where minimized windows are displayed. This is kind of similar to the Windows Taskbar. Essentially, it just consists of a ListBox arranged in a DockPanel that is animated when the user mouses over it:
<DockPanel Grid.Column="1" Width="17" LastChildFill="True"
Style="{StaticResource flyOutPanelStyle}">
The flyOutPanelStyle defines animation storyboards that are triggered on mouse enter and leave:
<Style x:Key="flyOutPanelStyle" TargetType="{x:Type DockPanel}">
<Style.Triggers>
<EventTrigger RoutedEvent="DockPanel.MouseEnter" >
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard TargetProperty="Width">
<DoubleAnimation To="150" Duration="0:0:0.5"
AccelerationRatio="0.33" DecelerationRatio="0.33"
AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="DockPanel.MouseLeave" >
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard TargetProperty="Width">
<DoubleAnimation To="17" Duration="0:0:0.5"
AccelerationRatio="0.33" DecelerationRatio="0.33"
AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
A ListBox is used to display information about the minimized windows. Data binding is used to bind the ListBox to the list of minimized windows, which is actually a list of PaneThumbnail objects. The PaneThumbnail class is used to store a preview image of the child pane and its title. The ListBox uses a data template to define how this information is displayed.
<ListBox x:Name="_tray" SelectionMode="Single"
ItemsSource="{Binding Path=MinimizedPanes}"
MouseDoubleClick="OnTrayItemDoubleClick"
SelectionChanged="OnTraySelectionChanged"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="16" Width="16" Source="{Binding
Path=Thumbnail}" />
<TextBlock Margin="4,4,4,4" Foreground="White"
Text="{Binding Path=Title}" >
<TextBlock.ToolTip>
<StackPanel Orientation="Vertical" Margin="4,4,4,4">
<Image Height="120" Width="120"
Source="{Binding Path=Thumbnail}" />
<TextBlock HorizontalAlignment="Center"
Foreground="Black" Text="{Binding Path=Title}"
TextTrimming="CharacterEllipsis" />
</StackPanel>
</TextBlock.ToolTip>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The data template uses a tooltip to display a preview image for the minimized window. This image is generated just before the child pane is minimized using the RenderTargetBitmap class within the OnPaneStateChaning event handler:
RenderTargetBitmap rtb = RenderPaneThumbnail( childPane );
PaneThumbnail thumbNail = new PaneThumbnail( childPane, rtb );
thumbnailCache.Add( childPane, thumbNail );
Once the PaneThumbnail is created, it is added to the minimized window list so it automatically shows up in the ListBox because of the data binding. When the user double clicks on a minimized window in the ListBox, the original child pane is added back to the canvas control and the pane’s thumbnail is removed from the list.
NOTE: There is a known bug in the Pane base class that prevent the child pane from being maximized programmatically when it is reinstated on the canvas. The child pane’s chrome shows up ok, but the user has to manually maximize the child pane to see its contents. Ho hum, it is a preview release after all…
Fin
There are another couple of minor features in the sample, like the animation of the child panes as they are minimized. You can take a look at the code and step through it to see how this works. The sample is not fully implemented yet though – the animation for minimize is there but not for maximize, and there is no z-order handling of the child windows. Hopefully I will get some time soon to complete these features and post an update...
I hope you find this sample interesting and useful. I wrote this to highlight how you can plug in different (and rich) layout strategies very easily without having to change the core implementation of the application. The modular separation of concerns that Acropolis supports is the magic behind this.
Thanks for your continued evaluation of Acropolis. Your feedback is very valuable to us.
We are just putting the finishing touches to the next CTP release of Acropolis - I'll post details of the release when it happens sometime next week but all of the existing links from www.WindowsClient.net/Acropolis should continue to work OK. Apart from the new features and bug fixes (more details in my next post) you'll notice a couple of minor changes in how we're going to manage our CTP release from now on.
First, we decided to change the naming of the CTP releases to include the name of the month in which they are released. This is to bring the naming in line with other CTP released from Microsoft. We are aiming to have a release approximately every month over the summer. Releasing two CTP's in the same month is a nice problem for us to have and we'll cross that bridge when we get to it :-).
Second, we decided to split out the samples from the core release. This frees us up to update or add more samples independently of the core release. We'll be posting updated samples a couple of days after each of the core releases.
So nothing earth shattering really, just trying to make it easier for us to be more agile and to ship CTP's faster...
July CTP will still be based on Visual Studio 'Orcas' Beta 1, since it will be released before Beta 2 is available, so unfortunately there will still be a couple of designer issues (happily these have been fixed in the Beta 2 release of Visual Studio). We'll have an updated CTP very soon after Beta 2 is released.
Meanwhile, Kevin Hoffman continues to provide very valuable feedback and keep us on our toes!
First of all, Kevin rightly points out that part to part communication is currently something you have to do manually - i.e. there is no automatic 'composition' by the framework; something that hooks parts together for you at run-time. Kevin is right that this is the cornerstone of composite application building. Rest assured that we have not neglected this scenario. In fact it has been central in our minds as we designed Acropolis. We have some of the basic plumbing in place in CTP 1 (connection points, part-view contract, etc) but we don't yet have full support for composability. We actually have a very nice composition engine working (well I think it's very nice, but you are the ultimate judges) and will be integrating it into a CTP release very soon (unfortunately, not in the July CTP though :-().
Secondly, Kevin points out that Acropolis doesn't currently support multiple top-level windows very well. This is an excellent point and we've had this feedback from a number of customers. We've put some thought into this and come up with a significantly improved design. It'll be integrated around the same time that we integrate the composition engine noted above and will be released soon.
As always, we love getting feedback like this - as painful as it might be sometimes :-) - so thanks Kevin and and keep it coming.
Continuing the series of posts on the top 5 Acropolis questions at TechEd 2007...
Now this is a really good question and usually led to a great discussion!
It usually followed in response to one of three things - the demos that we gave, the template and wizard features in Acropolis, or our adoption of XAML as a declarative format for components.
Simple Apps vs Real-World Apps
This question is usually asked in response to some of the demos that we've been showing here at TechEd. Since Acropolis is meant to provide the plumbing for an application (leaving you to focus on your business logic) we took the decision to emphasize the Acropolis plumbing in the demos that we gave. This would allow developers, so our reasoning went, to see more clearly exactly what functionality Acropolis is providing.
For example, in my break-out session I built a simple text editing application and demonstrated how you can swap in and swap out various components to support different patterns for navigation, layout, etc. I show the beginnings of this in the Getting Started video on the Acropolis home page on WindowsClient.net. I chose this example specifically because this application is pretty much all plumbing and does not have much business logic at all - in fact the whole application only has about 20 lines of code (including the pluggable spell check service) - so that I could focus on how the app model fits together and how the visual designers allow you to specify a large part of the behavior of the application without the need for plumbing code.
It seems, though, that some of the attendees that saw this demo got the impression that we were claiming that you could build all applications, at any level of complexity, using just the visual designers. Unfortunately, this isn't the case, as I'll describe below. In hindsight we maybe should have demonstrated building a more real-world application, something that contains non-trivial business logic, but then, no doubt, we'd have spent most of our demo time on business logic code rather than showing off the Acropolis application model. Ho hum...
Templates and Wizards
One of the goals of Acropolis is to let you focus more on your business logic code, and this notion extends to the situation when you first create the project too. One of my pet peeves is when you select File/New Project in Visual Studio and are invariably presented with a blank page or form and are left to go write all of the plumbing yourself from scratch, again.
So we set out to beef up the template system in Visual Studio so that we could provide richer templates for common application types. These templates will define enough of the application's structure so that you can start to focus on business logic immediately after your project is created. When we demo this feature, some developers saw this as an attempt to ‘pigeon-hole' applications into two or three restrictive types, or to just generate the code that we would have had to write before.
But our intention here is not to provide templates for all conceivable types of applications. - we'll probably only ship, say, templates for the 5 most application types - but to give you a much quicker turnaround time between project creation and getting stuck into writing your business logic code. And of course you'll be able to create your own templates, to support, for example, your corporate standard application types, as a way to speed up development.
Acropolis templates will also be configurable via an ‘Application Designer' which will let you choose between the various options provided by the template and give you a visual representation of the overall structure of your application. Again, this lets you start with a application that is as close as possible to the type of application you want to build.
In CTP 1 the Application Designer takes the form of a wizard. At present, the wizard lets you choose between a few simple options but it is not re-entrant - i.e. you can't go back and change your decisions after the initial code is generated, so what we have now is very similar to the old MFC one-shot application wizard. But we know that this isn't a good long term solution. In upcoming CTPs we'll be allowing you to go back and change the options for your application again and again without having to generate a new project and copy all of the code over.
This allows for a more iterative development process. You might start with a default application, implement your business logic code, get it into the hands of some users for feedback, make changes to your applications configurable settings, and then go round again for another round of feedback.
Why XAML?
If you've downloaded and checked out the CTP 1 bits, you'll have noticed that we're using a declarative approach to defi