Click here to Skip to main content
6,630,901 members and growing! (19,597 online)
Email Password   helpLost your password?
Languages » C# » Windows Forms     Intermediate

Document Management Toolkit Library

By Alex Fr

Set of classes for creating full-featured Windows Forms applications.
C#.NET 1.0, .NET 1.1, Win2K, WinXPVS.NET2003, Dev
Posted:12 Jul 2004
Views:36,562
Bookmarked:38 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
5 votes for this article.
Popularity: 1.65 Rating: 2.36 out of 5
1 vote, 20.0%
1
1 vote, 20.0%
2

3
1 vote, 20.0%
4
2 votes, 40.0%
5

Introduction

DocToolkit library is a set of classes which may be used to create full-featured Windows Forms applications for document management. Suppose we want to create an application with the following functions:

  • Open file, editing file in the window, Save, Save As functions, testing for dirty state (document is changed), displaying of current file name in the form title.
  • Associating of file type with the program for Windows Shell.
  • Handling files passed to the program in the command line and dropped from the Windows Explorer.
  • Managing list of most recently used files (MRU).
  • Persisting of the last application window state.

Reading this list of functions, we can see that editing file in the window is program-specific and changes in every project. All other tasks may be encapsulated to number of classes and used by a unified way. DocToolkit library is a framework which may be used to develop such applications.

DocToolkit classes

DocToolkit library contains a number of classes. Some of them are published in the CodeProject in my previous articles, two classes use code from another articles. Links to the class source is provided in class description in the article and in the class code. All classes have a unified programming interface; source code is tested for conformance to the Microsoft .NET Framework Design Guidelines using the FxCop tool.

  • DocManager class. Makes file-related operations: Open, New, Save, updating of the form title, registering of file type for Windows Shell. This class is written using the MSDN Magazine article "Creating Document-Centric Applications in Windows Forms" by Chris Sells.

    This class has a number of functions which are called by owner form, like NewDocument, OpenDocument, SaveDocument. The class raises a number of events which should be handled in the owner form, like SaveEvent, LoadEvent and DocChangedEvent. For handling these events, the owner form provides task-specific code for serialization, refreshing information in the window etc.

  • DragDropManager class. Encapsulates the code required to open files dropped to the program window from Windows Explorer. Class subscribes to the owner form DragEnter and DragDrop events and raises event FileDroppedEvent passing file name(s) to open.
  • MruManager class. Manages list of recently opened files (MRU) - shows it in the owner form menu and saves it to the Registry. When user selects file from the MRU list, class raises MruOpenEvent event which is handled in the owner form.
  • PersistWindowState class. Keeps main application window state when program exits, and restores this state on the next run. Class is written by Joel Matthias and published in the article: Saving and Restoring the Location, Size and Window State of a .NET Form.

Using the DocToolkit Library

Step-by-step instructions to create Windows Forms application using the DocToolkit library.

  1. Create new C# Windows Forms application. Demo application is called UIDocSample.
  2. Add reference to the DocToolkit Library. Ensure that DocToolkit.dll is available at runtime. Demo projects DocToolkit and UIDocSample have output directories ..\bin\Debug\ and ..\bin\Release\, so both DLL and exe files are written to the same directory.
  3. Add MainMenu control to the form. Add File popup menu and add the following menu items to it:
    Item Text Item Name
    New menuFileNew
    Open menuFileOpen
    Save menuFileSave
    Save As menuFileSaveAs
    - (separator)  
    Recent Files menuFileRecent
    - (separator)  
    Exit menuFileExit
  4. Add new class to the project. Call it DocClass. Paste the following code to this class file:
    using System;
    using System.Runtime.Serialization;
    using System.Windows.Forms;
    using System.Drawing;
    using System.Security.Permissions;
    using System.Globalization;
    
    
    namespace UIDocSample
    {
        /// <summary>
    
        /// Document class which makes program-specific tasks
    
        /// </summary>
    
        [Serializable]
        public class DocClass  : ISerializable
        {
            private int sampleData;
    
            public DocClass()
            {
                sampleData = 0;
            }
            
            public bool Empty
            {
                get
                {
                    return (sampleData == 0);
                }
            }
    
            public void Draw(Graphics g)
            {
                Font drawFont = new Font("Arial", 16);
                SolidBrush drawBrush = new SolidBrush(Color.Black);
                PointF drawPoint = new PointF(10.0F, 10.0F);
                g.DrawString(sampleData.ToString(CultureInfo.InvariantCulture), 
                                               drawFont, drawBrush, drawPoint);
            }
    
            public void Change()
            {
                sampleData++;
            }
    
            // Serialization
    
            // This function is called when file is loaded
    
            protected DocClass(SerializationInfo info, 
                                 StreamingContext context)
            {
                sampleData = info.GetInt32("sampleData");
            }
    
            // Serialization
    
            // This function is called when file is saved
    
            [SecurityPermissionAttribute(SecurityAction.Demand, 
                                   SerializationFormatter=true)]
            public virtual void GetObjectData(SerializationInfo info, 
                                               StreamingContext context)
            {
                info.AddValue("sampleData", sampleData);
            }
    
        }
    }

    This class is prototype for a class which performs program-specific tasks. It has some data, has function for changing of this data, knows to draw itself to the Graphics object and supports ISerializable interface, this allows to save/load class instance.

  5. Add DocClass member to the Form1 class and initialize it in the class constructor:
            private DocClass docClass;
            
            public Form1()
            {
                InitializeComponent();
    
                docClass = new DocClass();
            }

    Add Paint and MouseDown event handlers to the Form1 class and fill them by the following way:

            private void Form1_Paint(object sender, 
                       System.Windows.Forms.PaintEventArgs e)
            {
                 docClass.Draw(e.Graphics);     
            }
    
            private void Form1_MouseDown(object sender, 
                       System.Windows.Forms.MouseEventArgs e)
            {
                if ( e.Button == MouseButtons.Left )
                {
                    docClass.Change();
                    Refresh();
                }
            }

    Now, we have the prototype of the application which manages some data in the window and changes it by clicking on the window client area. The next steps show how to add DocToolkit helper classes to this application.

  6. Add DocManager member to the Form1 class:
            private DocManager docManager;

    Add the following functions to the Form1 class:

            private void docManager_ClearEvent(object sender, EventArgs e)
            {
                // DocManager reports that document should be cleared
    
                // (user selected New command)
    
                docClass = new DocClass();
                Refresh();
            }
            
            private void docManager_DocChangedEvent(object sender, EventArgs e)
            {
                // DocManager reports that document was changed 
    
                // (loaded from file)
    
                Refresh();
            }
    
            private void docManager_OpenEvent(object sender, 
                                             OpenFileEventArgs e)
            {
                // DocManager reports about successful/unsuccessful
    
                // Open File operation
    
                
                // Will be filled later
    
            }
            
            private void docManager_LoadEvent(object sender, 
                                          SerializationEventArgs e)
            {
                // DocManager asks to load document from supplied stream
    
                try
                {
                    docClass  = 
                      (DocClass)e.Formatter.Deserialize(e.SerializationStream);
                }
                catch ( Exception ex )
                {
                    MessageBox.Show(this, 
                        "Open File operation failed. File name: " 
                        + e.FileName + "\n" +
                        "Reason: " + ex.Message, 
                        Application.ProductName);
    
                    e.Error = true;
                }
    
            }
            
            private void docManager_SaveEvent(object sender, 
                                        SerializationEventArgs e)
            {
                // DocManager asks to save document to supplied stream
    
                try
                {
                    e.Formatter.Serialize(e.SerializationStream, docClass);
                }
                catch ( Exception ex )
                {
                    MessageBox.Show(this, 
                        "Save File operation failed. File name: " 
                        + e.FileName + "\n" +
                        "Reason: " + ex.Message, 
                        Application.ProductName);
    
                    e.Error = true;
                }
                      
            }
  7. Add function InitializeHelperObjects to the Form1 class:
            void InitializeHelperObjects()
            {
                string registryPath = "Software\\YourCompany\\UIDocSample";
    
                // docManager
    
    
                DocManagerData data = new DocManagerData();
                data.FormOwner = this;
                data.UpdateTitle = true;
                data.FileDialogFilter = "UIDocSample files" + 
                         " (*.uds)|*.uds|All Files (*.*)|*.*";
                data.NewDocName = "Untitled.uds";
                data.RegistryPath = registryPath;
    
                docManager = new DocManager(data);
    
                // Subscribe to DocManager events
    
                docManager.SaveEvent += 
                          new SaveEventHandler(docManager_SaveEvent);
                docManager.LoadEvent += 
                          new LoadEventHandler(docManager_LoadEvent);
                docManager.OpenEvent += 
                          new OpenFileEventHandler(docManager_OpenEvent);
                docManager.DocChangedEvent += 
                          new EventHandler(docManager_DocChangedEvent);
                docManager.ClearEvent += 
                          new EventHandler(docManager_ClearEvent);
    
                docManager.NewDocument();
    
                // Register file type for Windows Shell
    
                docManager.RegisterFileType("uds", "udsfile", 
                                            "UIDocSample File");            
            }

    Call InitializeHelperObjects function from Form1 constructor:

            public Form1()
            {
                InitializeComponent();
    
                docClass = new DocClass();
                InitializeHelperObjects();
            }

    Open function Form1_MouseDown added before and add these lines to it:

            private void Form1_MouseDown(object sender, 
                            System.Windows.Forms.MouseEventArgs e)
            {
                if ( e.Button == MouseButtons.Left )
                {
                    docClass.Change();
                    docManager.Dirty = true;        // add this line
    
                    Refresh();
                }
            }
  8. Add functions to the Form1 class which call various DocManager functions:
            public void OpenDocument(string file)
            {
                docManager.OpenDocument(file);
            }
    
            // User Selected File - Open command.
    
            private void CommandOpen()
            {
                docManager.OpenDocument("");
            }
    
            // User selected File - Save command
    
            private void CommandSave()
            {
                docManager.SaveDocument(DocManager.SaveType.Save);
            }
    
            // User selected File - Save As command
    
            private void CommandSaveAs()
            {
                docManager.SaveDocument(DocManager.SaveType.SaveAs);
            }
    
            // User selected File - New command
    
            private void CommandNew()
            {
                docManager.NewDocument();
            }
  9. Add handlers to New, Open, Save, Save As menu items and Closing event:
            private void menuFileNew_Click(object sender, System.EventArgs e)
            {
                CommandNew();        
            }
    
            private void menuFileOpen_Click(object sender, System.EventArgs e)
            {
                CommandOpen();
            }
    
            private void menuFileSave_Click(object sender, System.EventArgs e)
            {
                CommandSave();
            }
    
            private void menuFileSaveAs_Click(object sender, System.EventArgs e)
            {
                CommandSaveAs();
            }
    
            private void Form1_Closing(object sender, 
                         System.ComponentModel.CancelEventArgs e)
            {
                if ( ! docManager.CloseDocument() )
                    e.Cancel = true;
            }
  10. Change Main function by the following way:
            [STAThread]
            static void Main(string[] args) 
            {
                // Check command line
    
                if( args.Length > 1 ) 
                {
                    MessageBox.Show("Incorrect number" + 
                      " of arguments. Usage: UIDocSample.exe [file]", 
                      "UIDocSample");
                    return;
                }
    
                Form1 form = new Form1();
    
                if ( args.Length == 1 ) 
                    // OpenDocument calls docManager.OpenDocument
    
                    form.OpenDocument(args[0]);
    
                Application.Run(form);
            }

    DocManager is the most complicated class in the DocToolkit library. Adding of other classes is relatively simple.

  11. DragDropManager class support.

    Add function which is used to open file dropped to the from:

            private void dragDropManager_FileDroppedEvent(object sender, 
                                                   FileDroppedEventArgs e)
            {
                OpenDocument(e.FileArray.GetValue(0).ToString());
            }

    Add DragDropManager member to the Form1 class:

            private DragDropManager dragDropManager;

    and initialize it in the InitializeHelperObjects function:

            void InitializeHelperObjects()
            {
              // ...
    
    
              // dragDropManager
    
              dragDropManager = new DragDropManager(this);
              dragDropManager.FileDroppedEvent += new 
               FileDroppedEventHandler(this.dragDropManager_FileDroppedEvent); 
            }
  12. MruManager class support.

    Add function which is used to open file selected from the MRU list:

            private void mruManager_MruOpenEvent(object sender, 
                                          MruFileOpenEventArgs e)
            {
                OpenDocument(e.FileName);
            }

    Add MruManager member to the Form1 class:

            private MruManager mruManager;

    and initialize it in the InitializeHelperObjects function:

            void InitializeHelperObjects()
            {
                // ...
    
    
                // mruManager
    
                mruManager = new MruManager();
                mruManager.Initialize(
                    this,               // owner form
    
                    menuFileRecent,     // Recent Files menu item
    
                    registryPath);      // Registry path to keep MRU list
    
    
                mruManager.MruOpenEvent += new 
                  MruFileOpenEventHandler(this.mruManager_MruOpenEvent);
            }

    Open function docManager_OpenEvent added before, and add this code to it:

            private void docManager_OpenEvent(object sender, 
                                                  OpenFileEventArgs e)
            {
                // DocManager reports about
    
                // successful/unsuccessful Open File operation
    
                if ( e.Succeeded )
                {
                    mruManager.Add(e.FileName);
                }
                else
                {
                    mruManager.Remove(e.FileName);
                }
            }
  13. PersistWindowState class support.

    Add PersistWindowState member to the Form1 class:

            private PersistWindowState persistState;

    and initialize it in the InitializeHelperObjects function:

            void InitializeHelperObjects()
            {
                // ...
    
    
                // persistState
    
                persistState = new PersistWindowState(registryPath, this);
            }
  14. Build the program and run it. In its initial state, it shows 0 in the window. Clicking in the window client area increments this number. Current file name is shown in the form title. When document has unsaved information, file name is followed by asterisk. Program has New, Open, Save, and Save As functions. If information is currently unsaved, and user tries to close the form or execute New or Open commands, program asks confirmation. Program may open file from the command line and file dropped from Windows Explorer. uds file type is associated with this program, so you can double-click on such a file in Windows Explorer and program opens it.

    Program has MRU files list. Every successfully opened file is added to this list. Program window is opened in the state it was closed last time. Open Registry key HKEY_CURRENT_USER\Software\YourCompany\UIDocSample. You can see information written by DocToolkit helper classes under this key. To convert this sample to real application, replace DocClass class with class which implements your application-specific tasks.

Setting controls state at application idle time

Commands in the UIDocSample program may be executed by selecting the menu items. Generally, applications may have also toolbar, buttons and other controls. In different situations, these controls may have different states: enabled/disabled, checked, visible/invisible etc. Every user action may change the controls' state. We need generic way to do this in the program. The way I am using is setting controls' state at application idle time. This way is very good for toolbar buttons and dialog controls. The best way for updating of menu items is handling of the Popup event. However, they may be handled at idle time as well.

Suppose we want to keep Save As command enabled when DocClass is not in its initial state (not empty). Save command should be enabled if DocClass is not empty and last changes are not saved yet. Add the following line to the Form1 constructor:

            Application.Idle += new EventHandler(Application_Idle);

and add the Application_Idle function:

        private void Application_Idle(object sender, EventArgs e)
        {
            menuFileSaveAs.Enabled = (!docClass.Empty);
            menuFileSave.Enabled = (docManager.Dirty & (!docClass.Empty));
            
            // State of any controls may be set here:

            // menu items, toolbar buttons, dialog controls etc.

        }

Now, a menu item's state is changed together with program state. This way works like ON_UPDATE_COMMAND_UI MFC handler for toolbar buttons.

Conclusion

When writing Windows Forms applications for document management, we need to implement the same standard tasks in every project. .NET is missing MFC-style framework for creating applications of this type. DocToolkit library is attempting to create such a framework. Developing applications using this library allows to concentrate on implementation of project-specific tasks, having standard document management stuff in the DocToolkit classes.

Client program shown in this article is very simple. The question is whether DocToolkit library is good enough to be used in real world applications. In my next article, I want to introduce the DrawTools program which shows how to draw graphic objects on the window client area using mouse. This program uses DocToolkit library and I found it convenient to use this library in it.

Acknowledgements

  1. Chris Sells. Creating Document-Centric Applications in Windows Forms.
  2. Joel Matthias. Saving and Restoring the Location, Size and Window State of a .NET Form.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here

    About the Author

    Alex Fr


    Member

    Occupation: Software Developer
    Location: Israel Israel

    Other popular C# articles:

    Article Top
    You must Sign In to use this message board.
    FAQ FAQ 
     
    Noise Tolerance  Layout  Per page   
     Msgs 1 to 3 of 3 (Total in Forum: 3) (Refresh)FirstPrevNext
    GeneralError Message PinmemberAazee21:25 12 Mar '06  
    GeneralRe: Error Message PinmemberAlex Fr21:44 12 Mar '06  
    GeneralError in Demo Pinmemberlieperik19:46 22 Jul '04  

    General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    PermaLink | Privacy | Terms of Use
    Last Updated: 12 Jul 2004
    Editor: Smitha Vijayan
    Copyright 2004 by Alex Fr
    Everything else Copyright © CodeProject, 1999-2009
    Web10 | Advertise on the Code Project