using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Windows.Forms;
namespace Netron.Diagramming.Core {
///
/// Abstract base class for the various diagram control representations
/// (WebForm and WinForm).
///
[ToolboxItem(false)]
public class DiagramControlBase :
ScrollableControl,
ISupportInitialize,
IDiagramControl {
#region Constants
protected const string constGeneral = "General";
#endregion
#region Events
///
/// Occurs when the something got selected and the properties of it can/should be shown.
///
[Category(constGeneral),
Description("Occurs when the something got selected and the " +
"properties of it can/should be shown."),
Browsable(true)]
public event EventHandler OnShowSelectionProperties;
///
/// Occurs when the Properties have changed
///
[Category(constGeneral),
Description("Occurs when the properties have changed."),
Browsable(true)]
public event EventHandler OnShowDocumentProperties;
///
/// Occurs when the properties of the canvas are exposed.
///
public event EventHandler OnShowCanvasProperties;
///
/// Occurs when an entity is added.
/// This event usually is bubbled from one of the layers
///
[Category(constGeneral), Description("Occurs when an entity is added."), Browsable(true)]
public event EventHandler OnEntityAdded;
///
/// Occurs when an entity is removed.
/// This event usually is bubbled from one of the layers
///
[Category(constGeneral), Description("Occurs when an entity is removed."), Browsable(true)]
public event EventHandler OnEntityRemoved;
///
/// Occurs on opening a file
///
[Category(constGeneral), Description("Occurs when the control will open a file."), Browsable(true)]
public event EventHandler OnOpeningDiagram;
///
/// Occurs when a file was opened
///
[Category(constGeneral), Description(" Occurs when a file was opened."), Browsable(true)]
public event EventHandler OnDiagramOpened;
///
/// Occurs when the history has changed in the undo/redo mechanism
///
public event EventHandler OnHistoryChange;
///
/// Occurs before saving a diagram
///
[Category(constGeneral), Description("Occurs before saving a diagram."), Browsable(true)]
public event EventHandler OnSavingDiagram;
///
/// Occurs after saving a diagram
///
[Category(constGeneral), Description("Occurs after saving a diagram."), Browsable(true)]
public event EventHandler OnDiagramSaved;
///
/// Occurs when the control resets the document internally, usually through the Ctrl+N hotkey.
///
[Category(constGeneral), Description("Occurs when the control resets the document internally, usually through the Ctrl+N hotkey."), Browsable(true)]
public event EventHandler OnNewDiagram;
#region Event raisers
///
/// Raises the OnShowCanvasProperties event.
///
protected void RaiseOnShowCanvasProperties(SelectionEventArgs e) {
EventHandler handler = OnShowCanvasProperties;
if (handler != null) {
handler(this, e);
}
}
///
/// Raises the OnNewDiagram event.
///
protected void RaiseOnNewDiagram() {
EventHandler handler = OnNewDiagram;
if (handler != null) {
handler(this, EventArgs.Empty);
}
}
///
/// Raises the OnHistory change.
///
protected void RaiseOnHistoryChange(HistoryChangeEventArgs e) {
EventHandler handler = OnHistoryChange;
if (handler != null) {
handler(this, e);
}
}
///
/// Raises the event
///
/// Properties event argument
protected virtual void RaiseOnShowDocumentProperties(PropertiesEventArgs e) {
EventHandler handler = OnShowDocumentProperties;
if (handler != null) {
handler(this, e);
}
}
///
/// Raises the OnSavingDiagram event
///
///
public void RaiseOnSavingDiagram(string filePath) {
if (OnSavingDiagram != null)
OnSavingDiagram(this, new FileEventArgs(new System.IO.FileInfo(filePath)));
}
///
/// Raises the OnShowSelectionProperties event.
///
/// The instance containing the event data.
protected virtual void RaiseOnShowSelectionProperties(SelectionEventArgs e) {
EventHandler handler = OnShowSelectionProperties;
if (handler != null) {
handler(this, e);
}
}
///
/// Raises the event.
///
/// The instance containing the event data.
protected virtual void RaiseOnEntityAdded(EntityEventArgs e) {
EventHandler handler = OnEntityAdded;
if (handler != null) {
handler(this, e);
}
}
///
/// Raises the .
///
/// The instance containing the event data.
protected virtual void RaiseOnEntityRemoved(EntityEventArgs e) {
EventHandler handler = OnEntityRemoved;
if (handler != null) {
handler(this, e);
}
}
///
/// Raises the OnDiagramSaved event
///
///
public void RaiseOnDiagramSaved(string filePath) {
if (OnDiagramSaved != null)
OnDiagramSaved(this, new FileEventArgs(new System.IO.FileInfo(filePath)));
}
///
/// Raises the OnOpeningDiagram event
///
public void RaiseOnOpeningDiagram(string filePath) {
if (OnOpeningDiagram != null)
OnOpeningDiagram(this, new FileEventArgs(new System.IO.FileInfo(filePath)));
}
///
/// Raises the OnDiagramOpened event
///
public void RaiseOnDiagramOpened(string filePath) {
if (OnDiagramOpened != null)
OnDiagramOpened(this, new FileEventArgs(new System.IO.FileInfo(filePath)));
}
#endregion
#endregion
#region Fields
// ------------------------------------------------------------------
///
/// The page settings for printing the diagram.
///
// ------------------------------------------------------------------
protected PageSettings mPageSettings;
// ------------------------------------------------------------------
///
/// The filename to use when saving the diagram.
///
// ------------------------------------------------------------------
protected string mFileName = "";
// ------------------------------------------------------------------
///
/// Collection of files that were opened. The collection can be
/// saved to disk by calling 'SaveRecentFiles(string path)'.
///
// ------------------------------------------------------------------
protected ArrayList myRecentFiles = new ArrayList();
// ------------------------------------------------------------------
///
/// the view
///
// ------------------------------------------------------------------
protected IView mView;
// ------------------------------------------------------------------
///
/// the controller
///
// ------------------------------------------------------------------
protected IController mController;
// ------------------------------------------------------------------
///
/// the Document field
///
// ------------------------------------------------------------------
protected Document mDocument;
protected bool mEnableAddConnection = true;
#endregion
#region Properties
// ------------------------------------------------------------------
///
/// Gets or sets the page settings for the entire document.
///
// ------------------------------------------------------------------
public PageSettings PageSettings {
get {
return mPageSettings;
}
set {
mPageSettings = value;
// PageSettings has the page size in hundreths of an inch and
// the view requires mils (thousandths of an inch).
mView.PageSize = new Size(
mPageSettings.PaperSize.Width * 10,
mPageSettings.PaperSize.Height * 10);
mView.Landscape = mPageSettings.Landscape;
}
}
// ------------------------------------------------------------------
///
/// Specifies if all shape's connectors are shown.
///
// ------------------------------------------------------------------
public bool ShowConnectors {
get {
return this.mView.ShowConnectors;
}
set {
this.mView.ShowConnectors = value;
}
}
// ------------------------------------------------------------------
///
/// Gets or sets the filename to save the diagram as.
///
// ------------------------------------------------------------------
public string FileName {
get {
return mFileName;
}
set {
mFileName = value;
}
}
///
/// Pans the diagram.
///
/// The pan.
[Browsable(false)]
public Point Origin {
get { return this.Controller.View.Origin; }
set { this.Controller.View.Origin = value; }
}
///
/// Gets or sets the magnification/zoom.
///
/// The magnification.
[Browsable(false)]
public SizeF Magnification {
get { return this.Controller.View.Magnification; }
set { this.Controller.View.Magnification = value; }
}
///
/// Gets the selected items.
///
/// The selected items.
[Browsable(false)]
public CollectionBase SelectedItems {
get { return this.Controller.Model.Selection.SelectedItems; }
}
///
/// Gets or sets whether to show the rulers.
///
/// true to show the rulers; otherwise, false.
[Browsable(true), Description("Gets or sets whether to show the rulers.")]
public bool ShowRulers {
get { return View.ShowRulers; }
set { View.ShowRulers = value; }
}
///
/// Gets or sets whether a connection can be added.
///
/// true if [enable add connection]; otherwise, false.
[Browsable(true), Description("Gets or sets whether the user can add new connections to the diagram."), Category("Interactions")]
public bool EnableAddConnection {
get { return mEnableAddConnection; }
set { mEnableAddConnection = value; }
}
#endregion
#region Constructor
///
/// Default constructor
///
protected DiagramControlBase() {
// Update the display setting.
Graphics g = CreateGraphics();
Display.DpiX = g.DpiX;
Display.DpiY = g.DpiY;
//create the provider for all shapy diagram elements
ShapeProvider shapeProvider = new ShapeProvider();
TypeDescriptor.AddProvider(
shapeProvider,
typeof(SimpleShapeBase));
TypeDescriptor.AddProvider(
shapeProvider,
typeof(ComplexShapeBase));
//the provider for connections
ConnectionProvider connectionProvider =
new ConnectionProvider();
TypeDescriptor.AddProvider(
connectionProvider,
typeof(ConnectionBase));
//scrolling stuff
this.AutoScroll = true;
this.HScroll = true;
this.VScroll = true;
mPageSettings = new PageSettings();
mPageSettings.Landscape = true;
mPageSettings.PaperSize = new PaperSize("Letter", 850, 1100);
}
#endregion
#region Properties
///
/// Gets or sets the background image displayed in the control.
///
///
/// An that represents the image to display in the background of the control.
///
public override Image BackgroundImage {
get {
return base.BackgroundImage;
}
set {
base.BackgroundImage = value;
//TODO: change the backgroundtype
}
}
//protected override void OnGiveFeedback(GiveFeedbackEventArgs gfbevent)
//{
// base.OnGiveFeedback(gfbevent);
// gfbevent.UseDefaultCursors = false;
// Cursor.Current = CursorPallet.DropShape;
//}
///
///
///
[Browsable(true), Description("The background color of the canvas if the type is set to 'flat'"), Category("Appearance")]
public new Color BackColor {
get {
return base.BackColor;
}
set {
//communicate the change down to the model
//shouldn't this be done via the controller?
mDocument.Model.CurrentPage.Ambience.BackgroundColor = value;
ArtPalette.DefaultPageBackgroundColor = value;
base.BackColor = value;
this.Invalidate();
}
}
///
/// Gets or sets the type of the background.
///
/// The type of the background.
[Browsable(true), Description("The background type"), Category("Appearance"), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public CanvasBackgroundTypes BackgroundType {
get { return mDocument.Model.CurrentPage.Ambience.BackgroundType; }
set { mDocument.Model.CurrentPage.Ambience.BackgroundType = value; }
}
///
/// Gets or sets the view.
///
/// The view.
public IView View {
get {
return mView;
}
set {
mView = value;
}
}
///
/// Gets or sets the controller.
///
/// The controller.
public IController Controller {
get {
return mController;
}
set { AttachToController(value); }
}
// ------------------------------------------------------------------
///
/// Gets or sets the Document. You must call
/// 'AttachToDocument(Document)' to apply a new document.
///
// ------------------------------------------------------------------
public virtual Document Document {
get { return mDocument; }
set { mDocument = value; }
}
#endregion
#region Methods
// ------------------------------------------------------------------
///
/// Saves all recently opened files to the path specified. The files
/// are saved to a simple text file, one filename per line.
///
///
/// int: The max number of filenams to
/// save.
// ------------------------------------------------------------------
public virtual void SaveRecentlyOpenedFiles(
string path,
int maxFiles) {
if (myRecentFiles.Count < 1) {
return;
}
StreamWriter sw = File.CreateText(path);
// Keep track of all files saved so we don't save duplicates.
ArrayList savedFiles = new ArrayList();
string fileName;
// Start at the last (i.e. most recent file opened) and save
// until either 0 or maxFiles is reached.
for (int i = myRecentFiles.Count - 1; i >= 0; i--) {
if (maxFiles == 0) {
break;
}
fileName = myRecentFiles[i].ToString();
if (File.Exists(fileName)) {
if (savedFiles.Contains(fileName) == false) {
sw.WriteLine(fileName);
savedFiles.Add(fileName);
maxFiles--;
}
}
}
sw.Flush();
sw.Close();
}
// ------------------------------------------------------------------
///
/// Reads the list of recently opened filenames from the
/// path specified. If the path doesn't exist or if there aren't
/// any files to read, then 'null' is returned.
///
/// string[]
// ------------------------------------------------------------------
public virtual string[] ReadRecentlyOpenedFiles(string path) {
if (File.Exists(path) == false) {
return null;
}
StreamReader sr = new StreamReader(path);
string text = sr.ReadToEnd();
string[] filenames = text.Split("\n".ToCharArray());
this.myRecentFiles.Clear();
string trimmedFileName;
foreach (string filename in filenames) {
trimmedFileName = filename.Trim("\r".ToCharArray());
if (File.Exists(trimmedFileName)) {
this.myRecentFiles.Add(trimmedFileName);
}
}
if (this.myRecentFiles.Count > 0) {
return (string[])myRecentFiles.ToArray(typeof(string));
} else {
return null;
}
}
// ------------------------------------------------------------------
///
/// Sets the layout root.
///
/// The shape.
// ------------------------------------------------------------------
public virtual void SetLayoutRoot(IShape shape) {
this.Controller.Model.LayoutRoot = shape;
}
// ------------------------------------------------------------------
///
/// Runs the activity.
///
/// Name of the activity.
// ------------------------------------------------------------------
public virtual void RunActivity(string activityName) {
this.Controller.RunActivity(activityName);
}
// ------------------------------------------------------------------
///
/// Handles the OnEntityRemoved event of the Controller control.
///
/// The source of the event.
/// The
/// instance
/// containing the event data.
// ------------------------------------------------------------------
protected virtual void HandleOnEntityRemoved(
object sender,
EntityEventArgs e) {
RaiseOnEntityRemoved(e);
}
// ------------------------------------------------------------------
///
/// Handles the OnEntityAdded event of the Controller control.
///
/// The source of the event.
/// The
/// instance
/// containing the event data.
// ------------------------------------------------------------------
protected virtual void HandleOnEntityAdded(
object sender,
EntityEventArgs e) {
RaiseOnEntityAdded(e);
}
// ------------------------------------------------------------------
///
/// Handles the OnBackColorChange event of the View control.
///
/// The source of the event.
/// The
/// instance
/// containing the event data.
// ------------------------------------------------------------------
protected void View_OnBackColorChange(object sender, ColorEventArgs e) {
base.BackColor = e.Color;
}
// ------------------------------------------------------------------
///
/// Bubbles the OnHistoryChange event from the Controller to the surface
///
/// The source of the event.
/// The
///
/// instance containing the event data.
// ------------------------------------------------------------------
void mController_OnHistoryChange(
object sender,
HistoryChangeEventArgs e) {
RaiseOnHistoryChange(e);
}
void Controller_OnShowSelectionProperties(object sender, SelectionEventArgs e) {
RaiseOnShowSelectionProperties(e);
}
// ------------------------------------------------------------------
///
/// Attaches to the controller specified and registers for all
/// required events to update this control from the controller.
///
/// IController
// ------------------------------------------------------------------
protected virtual void AttachToController(IController controller) {
if (controller == null)
throw new ArgumentNullException();
mController = controller;
mController.OnHistoryChange +=
new EventHandler(
mController_OnHistoryChange);
mController.OnShowSelectionProperties +=
new EventHandler(
Controller_OnShowSelectionProperties);
mController.OnEntityAdded +=
new EventHandler(
HandleOnEntityAdded);
mController.OnEntityRemoved +=
new EventHandler(
HandleOnEntityRemoved);
}
// ------------------------------------------------------------------
///
/// Resets the document and underlying model.
///
// ------------------------------------------------------------------
public virtual void NewDocument() {
// Lets see, we really only want to ask the user if they want to
// save their changes if they don't have changes to save.
// We can check the un/redo mananger to see if there are changes.
if ((this.mController.UndoManager.CanRedo()) ||
(this.mController.UndoManager.CanUndo())) {
DialogResult result = MessageBox.Show(
"Save changes before clearing the current diagram?",
"Confirm Clear",
MessageBoxButtons.YesNoCancel,
MessageBoxIcon.Question);
if (result == DialogResult.Yes) {
this.Save();
} else if (result == DialogResult.Cancel) {
return;
}
}
Document = new Document();
this.mController.UndoManager.ClearUndoRedo();
AttachToDocument(Document);
this.mFileName = "";
RaiseOnNewDiagram();
//this.Controller.Model.Clear();
}
#region Printing
// ------------------------------------------------------------------
///
/// Displays a PageSetupDialog so the user can specify how each
/// page is printed.
///
// ------------------------------------------------------------------
public void PageSetup() {
PageSetupDialog dialog = new PageSetupDialog();
dialog.PageSettings = this.PageSettings;
if (dialog.ShowDialog() == DialogResult.OK) {
this.PageSettings = dialog.PageSettings;
}
}
// ------------------------------------------------------------------
///
/// Prints all pages of the diagram.
///
// ------------------------------------------------------------------
public void Print() {
DiagramPrinter myPrinter = new DiagramPrinter(
this.PageSettings,
this,
false);
}
// ------------------------------------------------------------------
///
/// Print previews all pages of the diagram.
///
// ------------------------------------------------------------------
public void PrintPreview() {
DiagramPrinter myPrinter = new DiagramPrinter(
this.PageSettings,
this,
true);
}
#endregion
#region IO
// ------------------------------------------------------------------
///
/// If the current filename (FileName property) is empty, then a
/// SaveFileDialog is displayed for the user to specify what to save
/// the diagram as. Otherwise, the current filename is used to save
/// the diagram.
///
// ------------------------------------------------------------------
public virtual void Save() {
try {
if (this.mFileName == "") {
SaveFileDialog dialog = new SaveFileDialog();
if (dialog.ShowDialog() == DialogResult.OK) {
this.SaveAs(dialog.FileName);
}
} else {
this.SaveAs(this.mFileName);
}
}
catch (Exception ex) {
MessageBox.Show("An error occured while saving the " +
"diagram. See below for the message about the " +
"error.\n\n" +
"Message: " + ex.Message,
"Save Diagram Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
// ------------------------------------------------------------------
///
/// Displays a SaveFileDialog regardless if there's an existing
/// filename so the user can save the diagram to a new location.
///
// ------------------------------------------------------------------
public virtual void SaveAs() {
SaveFileDialog dialog = new SaveFileDialog();
if (dialog.ShowDialog() == DialogResult.OK) {
SaveAs(dialog.FileName);
}
}
// ------------------------------------------------------------------
///
/// Saves the diagram to the given location.
///
/// The path.
// ------------------------------------------------------------------
public virtual void SaveAs(string path) {
if (!Directory.Exists(Path.GetDirectoryName(path))) {
throw new DirectoryNotFoundException(
"Create the directory before saving the diagram to it.");
}
RaiseOnSavingDiagram(path);
if (BinarySerializer.SaveAs(path, this) == true) {
// Store the filename if the save was successful.
this.mFileName = path;
// Clear the undo/redo history.
this.mController.UndoManager.ClearUndoRedo();
RaiseOnDiagramSaved(path);
}
}
// ------------------------------------------------------------------
///
/// Displays an OpenFileDialog for the user to specify the diagram
/// to open.
///
// ------------------------------------------------------------------
public virtual void Open() {
OpenFileDialog dialog = new OpenFileDialog();
if (dialog.ShowDialog() == DialogResult.OK) {
this.Open(dialog.FileName);
}
}
// ------------------------------------------------------------------
///
/// Opens the diagram from the specified path.
///
/// The path.
// ------------------------------------------------------------------
public virtual void Open(string path) {
if (!Directory.Exists(Path.GetDirectoryName(path))) {
throw new DirectoryNotFoundException(
"Create the directory before saving the diagram to it.");
}
this.NewDocument();//makes it possible for the host to ask for
// saving first.
RaiseOnOpeningDiagram(path);//do it before opening the new diagram.
if (BinarySerializer.Open(path, this) == true) {
// Store the filename if successful.
this.mFileName = path;
// Add the filename to the list of previously opened files.
this.myRecentFiles.Add(path);
// Raise that a new diagram was opened.
RaiseOnDiagramOpened(path);
}
//this.mFileName = mFileName;
}
#endregion
// ------------------------------------------------------------------
///
/// Attachement to the document.
///
/// The document.
// ------------------------------------------------------------------
public virtual void AttachToDocument(Document document) {
if (document == null)
throw new ArgumentNullException();
this.mDocument = document;
#region re-attach to the new model
View.Model = Document.Model;
Controller.Model = Document.Model;
#endregion
#region Update the ambience
document.Model.SetCurrentPage(0);
#endregion
this.Controller.Model.Selection.Clear();
this.Invalidate();
}
// ------------------------------------------------------------------
///
/// Activates a registered tool with the given name
///
/// the name of a tool
// ------------------------------------------------------------------
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
public void ActivateTool(string toolName) {
if (toolName == null || toolName.Trim().Length == 0) {
throw new ArgumentNullException("The tool name cannot " +
"be 'null' or empty.");
}
if (this.Controller == null) {
throw new InconsistencyException(
"The Controller of the surface is 'null', this is a " +
"strong inconsistency in the MVC model.");
}
if (toolName.Trim().Length > 0)
this.Controller.ActivateTool(toolName);
}
// ------------------------------------------------------------------
///
/// Starts the tool with the given name.
///
/// the name of a registered tool
// ------------------------------------------------------------------
public void LaunchTool(string toolName) {
if (this.Controller == null) return;
this.Controller.ActivateTool(toolName);
}
// ------------------------------------------------------------------
///
/// Overrides the base method to call the view which will paint the
/// diagram on the given graphics surface.
///
/// PaintEventArgs
// ------------------------------------------------------------------
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
mView.Paint(e.Graphics);
}
// ------------------------------------------------------------------
///
/// Paints the background of the control.
///
/// A
/// that
/// contains information about the control to paint.
// ------------------------------------------------------------------
protected override void OnPaintBackground(PaintEventArgs pevent) {
base.OnPaintBackground(pevent);
mView.PaintBackground(pevent.Graphics);
}
// ------------------------------------------------------------------
///
/// Undoes the last action that was recorded in the UndoManager.
///
// ------------------------------------------------------------------
public void Undo() {
this.Controller.Undo();
}
// ------------------------------------------------------------------
///
/// Redoes the last action that was recorded in the UndoManager.
///
// ------------------------------------------------------------------
public void Redo() {
this.Controller.Redo();
}
#endregion
#region Explicit ISupportInitialize implementation
void ISupportInitialize.BeginInit() {
//here you can check the conformity of properties
BeginInit();
}
///
/// Signals the object that initialization is complete.
///
void ISupportInitialize.EndInit() {
EndInit();
}
///
/// Signals the object that initialization is starting.
///
protected virtual void BeginInit() {
}
///
/// Signals the object that initialization is complete.
///
protected virtual void EndInit() {
//necessary if the background type is gradient since the brush requires the size of the client rectangle
mView.SetBackgroundType(BackgroundType);
//the diagram control provider gives problems in design mode. If enable at design mode the
//diagramming control becomes a container component rather than a form control because the essential
//properties are disabled through the ControlProvider (the Location or ID for example).
//Also, do not try to put this in the constructor, you need the ISupportInitialize to get a meaningful DesignMode value.
if (!DesignMode) {
ControlProvider controlProvider = new ControlProvider();
TypeDescriptor.AddProvider(controlProvider, typeof(DiagramControlBase));
}
}
#endregion
}
}