123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922 |
- #region Using directives
- using System;
- using System.Windows.Forms;
- using System.Diagnostics;
- using System.IO;
- using System.Globalization;
- using System.Runtime.Serialization;
- using System.Runtime.Serialization.Formatters.Binary;
- using Microsoft.Win32;
- using System.Security;
- #endregion
- // Using: in the end of this file.
- namespace DocToolkit
- {
- #region Class DocManager
- /// <summary>
- /// Document manager. Makes file-related operations:
- /// open, new, save, updating of the form title,
- /// registering of file type for Windows Shell.
- /// Built using the article:
- /// Creating Document-Centric Applications in Windows Forms
- /// by Chris Sells
- /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnforms/html/winforms09182003.asp
- /// </summary>
- public class DocManager
- {
- #region Events
- public event SaveEventHandler SaveEvent;
- public event LoadEventHandler LoadEvent;
- public event OpenFileEventHandler OpenEvent;
- public event EventHandler ClearEvent;
- public event EventHandler DocChangedEvent;
- #endregion
- #region Members
- private string fileName = "";
- private bool dirty = false;
- private Form frmOwner;
- private string newDocName;
- private string fileDlgFilter;
- private string registryPath;
- private bool updateTitle;
- private const string registryValue = "Path";
- private string fileDlgInitDir = ""; // file dialog initial directory
- #endregion
- #region Enum
- /// <summary>
- /// Enumeration used for Save function
- /// </summary>
- public enum SaveType
- {
- Save,
- SaveAs
- }
- #endregion
- #region Constructor
- /// <summary>
- /// Initialization
- /// </summary>
- /// <param name="data"></param>
- public DocManager(DocManagerData data)
- {
- frmOwner = data.FormOwner;
- frmOwner.Closing += OnClosing;
- updateTitle = data.UpdateTitle;
- newDocName = data.NewDocName;
- fileDlgFilter = data.FileDialogFilter;
- registryPath = data.RegistryPath;
- if (!registryPath.EndsWith("\\"))
- registryPath += "\\";
- registryPath += "FileDir";
- // attempt to read initial directory from registry
- RegistryKey key = Registry.CurrentUser.OpenSubKey(registryPath);
- if (key != null)
- {
- string s = (string)key.GetValue(registryValue);
- if (!Empty(s))
- fileDlgInitDir = s;
- }
- }
- #endregion
- #region Public functions and Properties
- /// <summary>
- /// Dirty property (true when document has unsaved changes).
- /// </summary>
- public bool Dirty
- {
- get
- {
- return dirty;
- }
- set
- {
- dirty = value;
- SetCaption();
- }
- }
- /// <summary>
- /// Open new document
- /// </summary>
- /// <returns></returns>
- public bool NewDocument()
- {
- if (!CloseDocument())
- return false;
- SetFileName("");
- if (ClearEvent != null)
- {
- // raise event to clear document contents in memory
- // (this class has no idea how to do this)
- ClearEvent(this, new EventArgs());
- }
- Dirty = false;
- return true;
- }
- /// <summary>
- /// Close document
- /// </summary>
- /// <returns></returns>
- public bool CloseDocument()
- {
- if (!this.dirty)
- return true;
- DialogResult res = MessageBox.Show(
- frmOwner,
- "Save changes?",
- Application.ProductName,
- MessageBoxButtons.YesNoCancel,
- MessageBoxIcon.Exclamation);
- switch (res)
- {
- case DialogResult.Yes: return SaveDocument(SaveType.Save);
- case DialogResult.No: return true;
- case DialogResult.Cancel: return false;
- default: Debug.Assert(false); return false;
- }
- }
- /// <summary>
- /// Open document
- /// </summary>
- /// <param name="newFileName">
- /// Document file name. Empty - function shows Open File dialog.
- /// </param>
- /// <returns></returns>
- public bool OpenDocument(string newFileName)
- {
- // Check if we can close current file
- if (!CloseDocument())
- return false;
- // Get the file to open
- if (Empty(newFileName))
- {
- OpenFileDialog openFileDialog1 = new OpenFileDialog();
- openFileDialog1.Filter = fileDlgFilter;
- openFileDialog1.InitialDirectory = fileDlgInitDir;
- DialogResult res = openFileDialog1.ShowDialog(frmOwner);
- if (res != DialogResult.OK)
- return false;
- newFileName = openFileDialog1.FileName;
- fileDlgInitDir = new FileInfo(newFileName).DirectoryName;
- }
- // Read the data
- try
- {
- using (Stream stream = new FileStream(
- newFileName, FileMode.Open, FileAccess.Read))
- {
- // Deserialize object from text format
- IFormatter formatter = new BinaryFormatter();
- if (LoadEvent != null) // if caller subscribed to this event
- {
- SerializationEventArgs args = new SerializationEventArgs(
- formatter, stream, newFileName);
- // raise event to load document from file
- LoadEvent(this, args);
- if (args.Error)
- {
- // report failure
- if (OpenEvent != null)
- {
- OpenEvent(this,
- new OpenFileEventArgs(newFileName, false));
- }
- return false;
- }
- // raise event to show document in the window
- if (DocChangedEvent != null)
- {
- DocChangedEvent(this, new EventArgs());
- }
- }
- }
- }
- // Catch all exceptions which may be raised from this code.
- // Caller is responsible to handle all other exceptions
- // in the functions invoked by LoadEvent and DocChangedEvent.
- catch (ArgumentNullException ex) { return HandleOpenException(ex, newFileName); }
- catch (ArgumentOutOfRangeException ex) { return HandleOpenException(ex, newFileName); }
- catch (ArgumentException ex) { return HandleOpenException(ex, newFileName); }
- catch (SecurityException ex) { return HandleOpenException(ex, newFileName); }
- catch (FileNotFoundException ex) { return HandleOpenException(ex, newFileName); }
- catch (DirectoryNotFoundException ex) { return HandleOpenException(ex, newFileName); }
- catch (PathTooLongException ex) { return HandleOpenException(ex, newFileName); }
- catch (IOException ex) { return HandleOpenException(ex, newFileName); }
- // Clear dirty bit, cache the file name and set the caption
- Dirty = false;
- SetFileName(newFileName);
- if (OpenEvent != null)
- {
- // report success
- OpenEvent(this, new OpenFileEventArgs(newFileName, true));
- }
- // Success
- return true;
- }
- /// <summary>
- /// Save file.
- /// </summary>
- /// <param name="type"></param>
- /// <returns></returns>
- public bool SaveDocument(SaveType type)
- {
- // Get the file name
- string newFileName = this.fileName;
- SaveFileDialog saveFileDialog1 = new SaveFileDialog();
- saveFileDialog1.Filter = fileDlgFilter;
- if ((type == SaveType.SaveAs) ||
- Empty(newFileName))
- {
- if (!Empty(newFileName))
- {
- saveFileDialog1.InitialDirectory = Path.GetDirectoryName(newFileName);
- saveFileDialog1.FileName = Path.GetFileName(newFileName);
- }
- else
- {
- saveFileDialog1.InitialDirectory = fileDlgInitDir;
- saveFileDialog1.FileName = newDocName;
- }
- DialogResult res = saveFileDialog1.ShowDialog(frmOwner);
- if (res != DialogResult.OK)
- return false;
- newFileName = saveFileDialog1.FileName;
- fileDlgInitDir = new FileInfo(newFileName).DirectoryName;
- }
- // Write the data
- try
- {
- using (Stream stream = new FileStream(
- newFileName, FileMode.Create, FileAccess.Write))
- {
- // Serialize object to text format
- IFormatter formatter = new BinaryFormatter();
- if (SaveEvent != null) // if caller subscribed to this event
- {
- SerializationEventArgs args = new SerializationEventArgs(
- formatter, stream, newFileName);
- // raise event
- SaveEvent(this, args);
- if (args.Error)
- return false;
- }
- }
- }
- catch (ArgumentNullException ex) { return HandleSaveException(ex, newFileName); }
- catch (ArgumentOutOfRangeException ex) { return HandleSaveException(ex, newFileName); }
- catch (ArgumentException ex) { return HandleSaveException(ex, newFileName); }
- catch (SecurityException ex) { return HandleSaveException(ex, newFileName); }
- catch (FileNotFoundException ex) { return HandleSaveException(ex, newFileName); }
- catch (DirectoryNotFoundException ex) { return HandleSaveException(ex, newFileName); }
- catch (PathTooLongException ex) { return HandleSaveException(ex, newFileName); }
- catch (IOException ex) { return HandleSaveException(ex, newFileName); }
- // Clear the dirty bit, cache the new file name
- // and the caption is set automatically
- Dirty = false;
- SetFileName(newFileName);
- // Success
- return true;
- }
- /// <summary>
- /// Assosciate file type with this program in the Registry
- /// </summary>
- /// <param name="data"></param>
- /// <returns>true - OK, false - failed</returns>
- public bool RegisterFileType(
- string fileExtension,
- string progId,
- string typeDisplayName)
- {
- try
- {
- string s = String.Format(CultureInfo.InvariantCulture, ".{0}", fileExtension);
- // Register custom extension with the shell
- using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(s))
- {
- // Map custom extension to a ProgID
- key.SetValue(null, progId);
- }
- // create ProgID key with display name
- using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(progId))
- {
- key.SetValue(null, typeDisplayName);
- }
- // register icon
- using (RegistryKey key =
- Registry.ClassesRoot.CreateSubKey(progId + @"\DefaultIcon"))
- {
- key.SetValue(null, Application.ExecutablePath + ",0");
- }
- // Register open command with the shell
- string cmdkey = progId + @"\shell\open\command";
- using (RegistryKey key =
- Registry.ClassesRoot.CreateSubKey(cmdkey))
- {
- // Map ProgID to an Open action for the shell
- key.SetValue(null, Application.ExecutablePath + " \"%1\"");
- }
- // Register application for "Open With" dialog
- string appkey = "Applications\\" +
- new FileInfo(Application.ExecutablePath).Name +
- "\\shell";
- using (RegistryKey key =
- Registry.ClassesRoot.CreateSubKey(appkey))
- {
- key.SetValue("FriendlyCache", Application.ProductName);
- }
- }
- catch (ArgumentNullException ex)
- {
- return HandleRegistryException(ex);
- }
- catch (SecurityException ex)
- {
- return HandleRegistryException(ex);
- }
- catch (ArgumentException ex)
- {
- return HandleRegistryException(ex);
- }
- catch (ObjectDisposedException ex)
- {
- return HandleRegistryException(ex);
- }
- catch (UnauthorizedAccessException ex)
- {
- return HandleRegistryException(ex);
- }
- return true;
- }
- #endregion
- #region Other Functions
- /// <summary>
- /// Hanfle exception from RegisterFileType function
- /// </summary>
- /// <param name="ex"></param>
- /// <returns></returns>
- private bool HandleRegistryException(Exception ex)
- {
- Trace.WriteLine("Registry operation failed: " + ex.Message);
- return false;
- }
- /// <summary>
- /// Save initial directory to the Registry
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
- {
- RegistryKey key = Registry.CurrentUser.CreateSubKey(registryPath);
- key.SetValue(registryValue, fileDlgInitDir);
- }
- /// <summary>
- /// Set file name and change owner's caption
- /// </summary>
- /// <param name="fileName"></param>
- private void SetFileName(string fileName)
- {
- this.fileName = fileName;
- SetCaption();
- }
- /// <summary>
- /// Set owner form caption
- /// </summary>
- private void SetCaption()
- {
- if (!updateTitle)
- return;
- frmOwner.Text = string.Format(
- CultureInfo.InvariantCulture,
- "{0} - {1}{2}",
- Application.ProductName,
- Empty(this.fileName) ? newDocName : Path.GetFileName(this.fileName),
- this.dirty ? "*" : "");
- }
- /// <summary>
- /// Handle exception in OpenDocument function
- /// </summary>
- /// <param name="ex"></param>
- /// <param name="fileName"></param>
- /// <returns></returns>
- private bool HandleOpenException(Exception ex, string fileName)
- {
- MessageBox.Show(frmOwner,
- "Open File operation failed. File name: " + fileName + "\n" +
- "Reason: " + ex.Message,
- Application.ProductName);
- if (OpenEvent != null)
- {
- // report failure
- OpenEvent(this, new OpenFileEventArgs(fileName, false));
- }
- return false;
- }
- /// <summary>
- /// Handle exception in SaveDocument function
- /// </summary>
- /// <param name="ex"></param>
- /// <param name="fileName"></param>
- /// <returns></returns>
- private bool HandleSaveException(Exception ex, string fileName)
- {
- MessageBox.Show(frmOwner,
- "Save File operation failed. File name: " + fileName + "\n" +
- "Reason: " + ex.Message,
- Application.ProductName);
- return false;
- }
- /// <summary>
- /// Helper function - test if string is empty
- /// </summary>
- /// <param name="s"></param>
- /// <returns></returns>
- static bool Empty(string s)
- {
- return s == null || s.Length == 0;
- }
- #endregion
- }
- #endregion
- #region Delegates
- public delegate void SaveEventHandler(object sender, SerializationEventArgs e);
- public delegate void LoadEventHandler(object sender, SerializationEventArgs e);
- public delegate void OpenFileEventHandler(object sender, OpenFileEventArgs e);
- #endregion
- #region Class SerializationEventArgs
- /// <summary>
- /// Serialization event arguments.
- /// Used in events raised from DocManager class.
- /// Class contains information required to load/save file.
- /// </summary>
- public class SerializationEventArgs : System.EventArgs
- {
- private IFormatter formatter;
- private Stream stream;
- private string fileName;
- private bool errorFlag;
- public SerializationEventArgs(IFormatter formatter, Stream stream,
- string fileName)
- {
- this.formatter = formatter;
- this.stream = stream;
- this.fileName = fileName;
- errorFlag = false;
- }
- public bool Error
- {
- get
- {
- return errorFlag;
- }
- set
- {
- errorFlag = value;
- }
- }
- public IFormatter Formatter
- {
- get
- {
- return formatter;
- }
- }
- public Stream SerializationStream
- {
- get
- {
- return stream;
- }
- }
- public string FileName
- {
- get
- {
- return fileName;
- }
- }
- }
- #endregion
- #region Class OpenFileEventArgs
- /// <summary>
- /// Open file event arguments.
- /// Used in events raised from DocManager class.
- /// Class contains name of file and result of Open operation.
- /// </summary>
- public class OpenFileEventArgs : System.EventArgs
- {
- private string fileName;
- private bool success;
- public OpenFileEventArgs(string fileName, bool success)
- {
- this.fileName = fileName;
- this.success = success;
- }
- public string FileName
- {
- get
- {
- return fileName;
- }
- }
- public bool Succeeded
- {
- get
- {
- return success;
- }
- }
- }
- #endregion
- #region class DocManagerData
- /// <summary>
- /// Class used for DocManager class initialization
- /// </summary>
- public class DocManagerData
- {
- public DocManagerData()
- {
- frmOwner = null;
- updateTitle = true;
- newDocName = "Untitled";
- fileDlgFilter = "All Files (*.*)|*.*";
- registryPath = "Software\\Unknown";
- }
- private Form frmOwner;
- private bool updateTitle;
- private string newDocName;
- private string fileDlgFilter;
- private string registryPath;
- public Form FormOwner
- {
- get
- {
- return frmOwner;
- }
- set
- {
- frmOwner = value;
- }
- }
- public bool UpdateTitle
- {
- get
- {
- return updateTitle;
- }
- set
- {
- updateTitle = value;
- }
- }
- public string NewDocName
- {
- get
- {
- return newDocName;
- }
- set
- {
- newDocName = value;
- }
- }
- public string FileDialogFilter
- {
- get
- {
- return fileDlgFilter;
- }
- set
- {
- fileDlgFilter = value;
- }
- }
- public string RegistryPath
- {
- get
- {
- return registryPath;
- }
- set
- {
- registryPath = value;
- }
- }
- };
- #endregion
- }
- #region Using
- /*
- Using:
- 1. Write class which implements program-specific tasks. This class keeps some data,
- knows to draw itself in the form window, and implements ISerializable interface.
- Example:
-
- [Serializable]
- public class MyTask : ISerializable
- {
- // class members
- private int myData;
- // ...
-
- public MyTask()
- {
- // ...
- }
-
- public void Draw(Graphics g, Rectangle r)
- {
- // ...
- }
-
- // other functions
- // ...
-
- // Serialization
- // This function is called when file is loaded
- protected GraphicsList(SerializationInfo info, StreamingContext context)
- {
- myData = info.GetInt32("myData");
- // ...
- }
- // Serialization
- // This function is called when file is saved
- public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- info.AddValue("myData", myData);
- // ...
- }
- }
-
- Add member of this class to the form:
-
- private MyClass myClass;
- 2. Add the DocManager member to the owner form:
- private DocManager docManager;
- 3. Add DocManager message handlers to the owner form:
- private void docManager_ClearEvent(object sender, EventArgs e)
- {
- // DocManager executed New command
- // Clear here myClass or create new empty instance:
- myClass = new MyClass();
- 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
- // For example:
-
- if ( e.Succeeded )
- // add e.FileName to MRU list
- else
- // remove e.FileName from MRU list
- }
-
- private void docManager_LoadEvent(object sender, SerializationEventArgs e)
- {
- // DocManager asks to load document from supplied stream
- try
- {
- myClass = (MyClass)e.Formatter.Deserialize(e.SerializationStream);
- }
- catch ( catch possible exceptions here )
- {
- // report error
-
- 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, myClass);
- }
- catch ( catch possible exceptions here )
- {
- // report error
-
- e.Error = true;
- }
- }
-
- 4. Initialize docManager member in the form initialization code:
- DocManagerData data = new DocManagerData();
- data.FormOwner = this;
- data.UpdateTitle = true;
- data.FileDialogFilter = "MyProgram files (*.mpf)|*.mpf|All Files (*.*)|*.*";
- data.NewDocName = "Untitled.mpf";
- data.RegistryPath = "Software\\MyCompany\\MyProgram";
- docManager = new DocManager(data);
- docManager.SaveEvent += docManager_SaveEvent;
- docManager.LoadEvent += docManager_LoadEvent;
- docManager.OpenEvent += docManager_OpenEvent;
- docManager.DocChangedEvent += docManager_DocChangedEvent;
- docManager.ClearEvent += docManager_ClearEvent;
- docManager.NewDocument();
- // Optionally - register file type for Windows Shell
- bool result = docManager.RegisterFileType("mpf", "mpffile", "MyProgram File");
- 5. Call docManager functions when necessary. For example:
- // File is dropped into the window;
- // Command line parameter is handled.
- 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();
- }
- 6. Optionally: test for unsaved data in the form Closing event:
- private void MainForm_Closing(object sender, System.ComponentModel.CancelEventArgs e)
- {
- if ( ! docManager.CloseDocument() )
- e.Cancel = true;
- }
- 7. Optionally: handle command-line parameters in the main function:
- [STAThread]
- static void Main(string[] args)
- {
- // Check command line
- if( args.Length > 1 )
- {
- MessageBox.Show("Incorrect number of arguments. Usage: MyProgram.exe [file]", "MyProgram");
- return;
- }
- // Load main form, taking command line into account
- MainForm form = new MainForm();
- if ( args.Length == 1 )
- form.OpenDocument(args[0]); // OpenDocument calls docManager.OpenDocument
- Application.Run(form);
- }
- */
- #endregion
|