All entries for Friday 09 September 2005

September 09, 2005

Document–View Architecture

A current job I'm doing has exposed the need for a half-decent document-view architecture in .NET. Back in my (brief) MFC days it was all about CDocument and CView objects! However in .NET (even 2.0) we are without any infrastructure to build on.

The only proper documented attempt I found online was Chris Sells series Creating Document-Centric Applications in Windows Forms Although Chris does give a rather excellent explanation and full implementation there were a few reasons why I can't use it. First off the "document" aspect is an actual Component, that's great for drag-n-drop style development, but I already have a code base that I simply wish to "act-as" a document. Also Chris's code is a bit too complete, in such a way that it limits my options in terms of implementation of my application. Finally, Chris's code does not easily support multiple document types or multiple views of a single document. All that said, I still thank Chris for that excellent series of articles that certainly got me thinking!

So what is my approach then?

  • Interfaces – Loose Coupling – Flexibilty*
    My framework revolves around 4 simple interfaces:
  • IDocument
  • IDocumentFactory
  • IView
  • IDocumentViewManager

IDocument defines what a document object should be able to do:

Public Interface IDocument
ReadOnly Property IsDirty() As Boolean
ReadOnly Property IsNew() As Boolean
Function Save() As IDocument
Function SaveAs(ByVal context As Object) As IDocument
End Interface
  • Track if it is dirty (has un-saved changes) and if it is new (never saved).
  • Save itself and save it self to a given context. Note that context will usually be a filename, but there is no reason that a certain document type wouldn't save to a database or other location.
Public Interface IView
Property Document() As IDocument
Sub EndEdit()
Event Closing As EventHandler(Of System.ComponentModel.CancelEventArgs)
Event Closed As EventHandler
End Interface
A view should provide some manner of interaction with a document. Typically, this will be a Form or UserControl.
It needs a reference to its document object.
The EndEdit() method will called when saving to allow the view to commit any pending edits to the document.
The Closing event will be listened to by the infrastructure to know when a view has been closed. When the last view of a document it is closing it can ask the user to save changes, etc.
Originally I did not have a Closed event and put the code to remove a document from the application in the Closing handler. However, due to way in which .NET MDI Parent forms raise Closing on all child forms, then raise all the Closed, instead of Closing followed by Closed for each child, I had to require the this additional event. Despite the added implementation detail, it does make more sense in some ways,
Public Interface IDocumentFactory
Function NewDocument() As IDocument
Function OpenDocument(ByVal context As Object) As IDocument
Function CreateView(ByVal document As IDocument) As IView
End Interface

The purpose of a document factory is to allow the framework to open new and existing documents, and to create a view of a document. You wonder why we don’t just create document objects ourselves, but the reason for this interface will become clearer once we delve into the document-view manager class. By abstracting the idea of a document factory we can do clever things like put a factory class into a DLL separate from the document. We can enumerate these factory assemblies without having to load all the associated business logic and document code into our working set. When an application supports a large number of different document types this reduction in memory allocation will certain improve start-up times. Additional meta-data can be added to the factory implementations to describe the documents they can create. This meta-data could be used to build runtime UI, such as “New Document” and “Open Document” dialog boxes.
A factory implementation could even return different document types and views depending on some value passed into the constructor, for example. This would not be possible if the creation logic was in the actual document.

The following interface is the real core of the framework. So it is a little big, but bear with me!

Public Interface IDocumentViewManager
Event ActiveViewChanged As EventHandler
Event ViewListChanged As EventHandler(Of ViewListChangedEventArgs)
Event PromptToSaveChanges As EventHandler(Of PromptToSaveChangesEventArgs)
Event ShowSaveAsUI As EventHandler(Of ShowSaveAsUIEventArgs)

Property ActiveView() As IView
ReadOnly Property Documents() As ReadOnlyCollection(Of IDocument)
ReadOnly Property Views(ByVal document As IDocument) As ReadOnlyCollection(Of IView)
Function NewDocument(ByVal documentFactory As IDocumentFactory) As IDocument
Function OpenDocument(ByVal documentFactory As IDocumentFactory, ByVal context As Object) As IDocument
Function SaveDocument(ByVal document As IDocument) As IDocument
Function SaveDocumentAs(ByVal document As IDocument) As IDocument
Sub CreateView(ByVal document As IDocument, ByVal info As IDocumentFactory)
Sub AddView(ByVal document As IDocument, ByVal view As IView)
End Interface

Before diving into the member-by-member descriptions, I should discuss the overall objective a document-view manager. UIs change and get re-done over time, so it is important to remove the document-view management from any actual UI code. Yes, we could stuff a document list into a MDI form, but that will never be a maintainable solution over time. At the same time I did not want to totally prevent that option. My framework includes an implementation of IDocumentViewManager, but there is nothing to stop someone making a form implement it directly.

A document-view manager basically has a list of documents currently loaded and a list of open views for each document. If provides a central location to create new documents, open existing ones and save them. It can create the default view for a document and also allows a view created externally to be added to a document.
The manager also keeps track of views being closed so it can prompt to save changes if a document is dirty.

First the events:

  • ActiveViewChanged should be raised when, as expected, the active IView in the application has changed. This will allow other UI to update accordingly.
  • ViewListChanged should be raised when an IView is added/deleted to/from a document.
  • PromptToSaveChanges should ask the user if they want to save changes to a given document. The event arguments contain both the document and a “response” property in which to store the selected action (Yes, No or Cancel).
  • ShowSaveAsUI should display some way for the user to enter the context under which to save the document. For example, show a SaveFileDialog. The event arguments still allow the setting of a “Cancel” property to abort the save operation. There is also a property, Context, to save the filename or other save location.

The three properties are fairly self explanatory. ActiveView gets or sets the currently active IView object in the UI. Documents returns a ReadOnlyCollection, enforcing that all changes must be made through the interfaces methods, not directly on the collection. Views will return a ReadOnlyCollection of views for a given document.

The NewDocument and OpenDocument functions use a document factory to actually create or open a document. Once a document object is created it is added to the manager’s list of documents.

The save functions should examine the state of the document (via IsDirty and IsNew) and raise the necessary events to prompt the user and get the save context. In addition, EndEnd() should be invoked on each IView for the document.

… I have run out of time at the moment, but will continue this discussion at the next convenient point in time…

Google Ads

Search this blog

Most recent comments

  • I scratched my eye while i was holding some kind of plastic packaging.. Anyways the corner of the pl… by Ercan on this entry
  • About a year ago my contacts that i was wearing, i guess were fautly, because shortly after they wer… by Jon on this entry
  • I got shower gel in my eye 4 and a half days ago, and becasue i rubbed my eyes a lot, i have scratch… by Chris on this entry
  • This website may help–health/tc/Eye–Injuries–Home–Treatment by S on this entry
  • I somehow got dirt, or debris in my eye. The terrible pain sent me in a tailspin. I was afraid of sa… by Bobbi on this entry


September 2005

Mo Tu We Th Fr Sa Su
Aug |  Today  | Oct
         1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30      


Blog archive

Not signed in
Sign in

Powered by BlogBuilder