|
namespace System.Workflow.ComponentModel.Design
{
using System;
using System.Drawing;
using System.Diagnostics;
using System.Collections;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.Drawing.Drawing2D;
using System.ComponentModel.Design;
#region Class WorkflowLayout
//All the coordinates and sizes are in logical coordinate system
internal abstract class WorkflowLayout : IDisposable
{
#region Members and Constructor/Destruction
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public enum LayoutUpdateReason { LayoutChanged, ZoomChanged }
protected IServiceProvider serviceProvider;
protected WorkflowView parentView;
public WorkflowLayout(IServiceProvider serviceProvider)
{
Debug.Assert(serviceProvider != null);
if (serviceProvider == null)
throw new ArgumentNullException("serviceProvider");
this.serviceProvider = serviceProvider;
this.parentView = this.serviceProvider.GetService(typeof(WorkflowView)) as WorkflowView;
Debug.Assert(this.parentView != null);
if (this.parentView == null)
throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(WorkflowView).FullName));
}
public virtual void Dispose()
{
}
#endregion
#region Public Interface
public abstract float Scaling { get; }
public abstract Size Extent { get; }
public abstract Point RootDesignerAlignment { get; }
public abstract bool IsCoOrdInLayout(Point logicalCoOrd);
public abstract Rectangle MapInRectangleToLayout(Rectangle logicalRectangle);
public abstract Rectangle MapOutRectangleFromLayout(Rectangle logicalRectangle);
public abstract Point MapInCoOrdToLayout(Point logicalPoint);
public abstract Point MapOutCoOrdFromLayout(Point logicalPoint);
public abstract void OnPaint(PaintEventArgs e, ViewPortData viewPortData);
public abstract void OnPaintWorkflow(PaintEventArgs e, ViewPortData viewPortData);
public abstract void Update(Graphics graphics, LayoutUpdateReason reason);
#endregion
}
#endregion
#region Class DefaultWorkflowLayout: For rendering Root without any customization
internal abstract class DefaultWorkflowLayout : WorkflowLayout
{
#region Members and Constructor
public static Size Separator = new Size(30, 30);
public DefaultWorkflowLayout(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
#endregion
#region WorkflowLayout Overrides
public override float Scaling
{
get
{
return 1.0f;
}
}
public override Size Extent
{
get
{
Size rootDesignerSize = (this.parentView.RootDesigner != null) ? this.parentView.RootDesigner.Size : Size.Empty;
Size totalSize = new Size(rootDesignerSize.Width + DefaultWorkflowLayout.Separator.Width * 2, rootDesignerSize.Height + DefaultWorkflowLayout.Separator.Height * 2);
Size clientSize = this.parentView.ViewPortSize;
return new Size(Math.Max(totalSize.Width, clientSize.Width), Math.Max(totalSize.Height, clientSize.Height));
}
}
public override Point RootDesignerAlignment
{
get
{
return new Point(DefaultWorkflowLayout.Separator);
}
}
public override bool IsCoOrdInLayout(Point logicalCoOrd)
{
return true;
}
public override Rectangle MapInRectangleToLayout(Rectangle logicalRectangle)
{
return logicalRectangle;
}
public override Rectangle MapOutRectangleFromLayout(Rectangle logicalRectangle)
{
return logicalRectangle;
}
public override Point MapInCoOrdToLayout(Point logicalPoint)
{
return logicalPoint;
}
public override Point MapOutCoOrdFromLayout(Point logicalPoint)
{
return logicalPoint;
}
public override void Update(Graphics graphics, LayoutUpdateReason reason)
{
//We dont do anything as our layout is simple
}
//
public override void OnPaint(PaintEventArgs e, ViewPortData viewPortData)
{
Graphics graphics = e.Graphics;
Debug.Assert(graphics != null);
//Get the drawing canvas
Bitmap memoryBitmap = viewPortData.MemoryBitmap;
Debug.Assert(memoryBitmap != null);
//Fill the background using the workspace color so that we communicate the paging concept
Rectangle workspaceRectangle = new Rectangle(Point.Empty, memoryBitmap.Size);
graphics.FillRectangle(AmbientTheme.WorkspaceBackgroundBrush, workspaceRectangle);
if (this.parentView.RootDesigner != null &&
this.parentView.RootDesigner.Bounds.Width >= 0 && this.parentView.RootDesigner.Bounds.Height >= 0)
{
GraphicsContainer graphicsState = graphics.BeginContainer();
//Create the scaling matrix
Matrix transformationMatrix = new Matrix();
transformationMatrix.Scale(viewPortData.Scaling.Width, viewPortData.Scaling.Height, MatrixOrder.Prepend);
//When we draw on the viewport we draw in scaled and translated.
//So that we minimize the calls to DrawImage
//Make sure that we scale down the logical view port origin in order to take care of scaling factor
//Before we select the transform factor we make sure that logicalviewport origin is scaled down
Point[] logicalViewPortOrigin = new Point[] { viewPortData.LogicalViewPort.Location };
transformationMatrix.TransformPoints(logicalViewPortOrigin);
//For performance improvement and to eliminate one extra DrawImage...we draw the designers on the viewport
//bitmap with visual depth consideration
transformationMatrix.Translate(-logicalViewPortOrigin[0].X + viewPortData.ShadowDepth.Width, -logicalViewPortOrigin[0].Y + viewPortData.ShadowDepth.Height, MatrixOrder.Append);
//Select the transform into viewport graphics.
//Viewport bitmap has the scaled and translated designers which we then map to
//the actual graphics based on page layout
graphics.Transform = transformationMatrix;
using (Region clipRegion = new Region(ActivityDesignerPaint.GetDesignerPath(this.parentView.RootDesigner, false)))
{
Region oldRegion = graphics.Clip;
graphics.Clip = clipRegion;
AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
graphics.FillRectangle(Brushes.White, this.parentView.RootDesigner.Bounds);
if (ambientTheme.WorkflowWatermarkImage != null)
ActivityDesignerPaint.DrawImage(graphics, ambientTheme.WorkflowWatermarkImage, this.parentView.RootDesigner.Bounds, new Rectangle(Point.Empty, ambientTheme.WorkflowWatermarkImage.Size), ambientTheme.WatermarkAlignment, AmbientTheme.WatermarkTransparency, false);
graphics.Clip = oldRegion;
}
graphics.EndContainer(graphicsState);
}
}
public override void OnPaintWorkflow(PaintEventArgs e, ViewPortData viewPortData)
{
Graphics graphics = e.Graphics;
Debug.Assert(graphics != null);
//Get the drawing canvas
Bitmap memoryBitmap = viewPortData.MemoryBitmap;
Debug.Assert(memoryBitmap != null);
Rectangle bitmapArea = new Rectangle(Point.Empty, memoryBitmap.Size);
ActivityDesignerPaint.DrawImage(graphics, memoryBitmap, bitmapArea, bitmapArea, DesignerContentAlignment.Fill, 1.0f, WorkflowTheme.CurrentTheme.AmbientTheme.DrawGrayscale);
}
#endregion
}
#endregion
#region Class ActivityRootLayout: For rendering when Activity is Root
internal sealed class ActivityRootLayout : DefaultWorkflowLayout
{
#region Members and Constructor
internal ActivityRootLayout(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
#endregion
public override Size Extent
{
get
{
Size rootDesignerSize = (this.parentView.RootDesigner != null) ? this.parentView.RootDesigner.Size : Size.Empty;
Size totalSize = new Size(rootDesignerSize.Width + DefaultWorkflowLayout.Separator.Width * 2, rootDesignerSize.Height + DefaultWorkflowLayout.Separator.Height * 2);
Size clientSize = this.parentView.ViewPortSize;
//since the activity designer doesnt take the full viewport area, we need to scale available viewport back by the zoom factor
clientSize.Width = (int)(clientSize.Width / ((float)this.parentView.Zoom / 100.0f));
clientSize.Height = (int)(clientSize.Height / ((float)this.parentView.Zoom / 100.0f));
return new Size(Math.Max(totalSize.Width, clientSize.Width), Math.Max(totalSize.Height, clientSize.Height));
}
}
public override void OnPaint(PaintEventArgs e, ViewPortData viewPortData)
{
base.OnPaint(e, viewPortData);
Graphics graphics = e.Graphics;
if (this.parentView.RootDesigner != null &&
this.parentView.RootDesigner.Bounds.Width >= 0 && this.parentView.RootDesigner.Bounds.Height >= 0)
{
GraphicsContainer graphicsState = graphics.BeginContainer();
//Create the scaling matrix
Matrix transformationMatrix = new Matrix();
transformationMatrix.Scale(viewPortData.Scaling.Width, viewPortData.Scaling.Height, MatrixOrder.Prepend);
//When we draw on the viewport we draw in scaled and translated.
//So that we minimize the calls to DrawImage
//Make sure that we scale down the logical view port origin in order to take care of scaling factor
//Before we select the transform factor we make sure that logicalviewport origin is scaled down
Point[] logicalViewPortOrigin = new Point[] { viewPortData.LogicalViewPort.Location };
transformationMatrix.TransformPoints(logicalViewPortOrigin);
//For performance improvement and to eliminate one extra DrawImage...we draw the designers on the viewport
//bitmap with visual depth consideration
transformationMatrix.Translate(-logicalViewPortOrigin[0].X + viewPortData.ShadowDepth.Width, -logicalViewPortOrigin[0].Y + viewPortData.ShadowDepth.Height, MatrixOrder.Append);
//Select the transform into viewport graphics.
//Viewport bitmap has the scaled and translated designers which we then map to
//the actual graphics based on page layout
graphics.Transform = transformationMatrix;
Rectangle rootBounds = this.parentView.RootDesigner.Bounds;
graphics.ExcludeClip(rootBounds);
rootBounds.Inflate(ActivityRootLayout.Separator.Width / 2, ActivityRootLayout.Separator.Height / 2);
ActivityDesignerPaint.DrawDropShadow(graphics, rootBounds, AmbientTheme.WorkflowBorderPen.Color, AmbientTheme.DropShadowWidth, LightSourcePosition.Left | LightSourcePosition.Top, 0.2f, false);
graphics.FillRectangle(WorkflowTheme.CurrentTheme.AmbientTheme.BackgroundBrush, rootBounds);
graphics.DrawRectangle(AmbientTheme.WorkflowBorderPen, rootBounds);
graphics.EndContainer(graphicsState);
}
}
}
#endregion
#region Class WorkflowRootLayout: For rendering when Sequential Workflow is Root, by always centering it
internal sealed class WorkflowRootLayout : DefaultWorkflowLayout
{
#region Members and Constructor
public WorkflowRootLayout(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
#endregion
#region WorkflowLayout Overrides
public override Rectangle MapInRectangleToLayout(Rectangle logicalRectangle)
{
Size offSet = Offset;
logicalRectangle.X -= offSet.Width;
logicalRectangle.Y -= offSet.Height;
return logicalRectangle;
}
public override Rectangle MapOutRectangleFromLayout(Rectangle logicalRectangle)
{
Size offSet = Offset;
logicalRectangle.X += offSet.Width;
logicalRectangle.Y += offSet.Height;
return logicalRectangle;
}
public override Point MapInCoOrdToLayout(Point logicalPoint)
{
Size offSet = Offset;
logicalPoint.Offset(-offSet.Width, -offSet.Height);
return logicalPoint;
}
public override Point MapOutCoOrdFromLayout(Point logicalPoint)
{
Size offSet = Offset;
logicalPoint.Offset(offSet.Width, offSet.Height);
return logicalPoint;
}
#endregion
#region Helpers
private Size Offset
{
get
{
//This logic is needed in order to keep the service root designer centered
Size layoutExtent = Extent;
Size totalSize = this.parentView.ClientSizeToLogical(this.parentView.ViewPortSize);
totalSize.Width = Math.Max(totalSize.Width, layoutExtent.Width);
totalSize.Height = Math.Max(totalSize.Height, layoutExtent.Height);
return new Size(Math.Max(0, (totalSize.Width - layoutExtent.Width) / 2), Math.Max(0, (totalSize.Height - layoutExtent.Height) / 2));
}
}
#endregion
}
#endregion
#region Class PrintPreviewLayout: For rendering print preview layout
internal sealed class PrintPreviewLayout : WorkflowLayout
{
#region Members and Constructor
private static Size DefaultPageSeparator = new Size(30, 30);
private static Margins DefaultPageMargins = new Margins(20, 20, 20, 20);
private WorkflowPrintDocument printDocument = null;
private ArrayList pageLayoutInfo = new ArrayList();
//We calculate the following variables when we perform layout, we store these variables
//so that drawing will be faster
private Margins headerFooterMargins = new Margins(0, 0, 0, 0);
private Size pageSeparator = PrintPreviewLayout.DefaultPageSeparator;
private Margins pageMargins = PrintPreviewLayout.DefaultPageMargins;
private Size rowColumns = new Size(1, 1); //Width = Columns, Height = Rows
private float scaling = 1.0f;
private Size pageSize = Size.Empty;
private DateTime previewTime = DateTime.Now;
internal PrintPreviewLayout(IServiceProvider serviceProvider, WorkflowPrintDocument printDoc)
: base(serviceProvider)
{
this.printDocument = printDoc;
}
#endregion
#region WorkflowLayout Overrides
public override float Scaling
{
get
{
return this.scaling;
}
}
public override Size Extent
{
get
{
//RowColumns Width = Columns, Height = Rows
Size maxSize = Size.Empty;
maxSize.Width = (this.rowColumns.Width * this.pageSize.Width) + ((this.rowColumns.Width + 1) * (PageSeparator.Width));
maxSize.Height = (this.rowColumns.Height * this.pageSize.Height) + ((this.rowColumns.Height + 1) * (PageSeparator.Height));
return maxSize;
}
}
public override Point RootDesignerAlignment
{
get
{
Point alignment = Point.Empty;
Size printableAreaPerPage = new Size(this.pageSize.Width - (PageMargins.Left + PageMargins.Right), this.pageSize.Height - (PageMargins.Top + PageMargins.Bottom));
Size totalPrintableArea = new Size(this.rowColumns.Width * printableAreaPerPage.Width, this.rowColumns.Height * printableAreaPerPage.Height);
Size rootDesignerSize = (this.parentView.RootDesigner != null) ? this.parentView.RootDesigner.Size : Size.Empty;
Size selectionSize = WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize;
if (this.printDocument.PageSetupData.CenterHorizontally)
alignment.X = (totalPrintableArea.Width - rootDesignerSize.Width) / 2;
alignment.X = Math.Max(alignment.X, selectionSize.Width + selectionSize.Width / 2);
if (this.printDocument.PageSetupData.CenterVertically)
alignment.Y = (totalPrintableArea.Height - rootDesignerSize.Height) / 2;
alignment.Y = Math.Max(alignment.Y, selectionSize.Height + selectionSize.Height / 2);
return alignment;
}
}
public override bool IsCoOrdInLayout(Point logicalCoOrd)
{
foreach (PageLayoutData pageLayoutData in this.pageLayoutInfo)
{
if (pageLayoutData.ViewablePageBounds.Contains(logicalCoOrd))
return true;
}
return false;
}
public override Rectangle MapInRectangleToLayout(Rectangle logicalRectangle)
{
Rectangle transformedViewPort = Rectangle.Empty;
//Now we start mapping the rectangle based on page layout
foreach (PageLayoutData pageLayoutData in this.pageLayoutInfo)
{
Rectangle intersectedPhysicalViewPort = logicalRectangle;
intersectedPhysicalViewPort.Intersect(pageLayoutData.ViewablePageBounds);
if (!intersectedPhysicalViewPort.IsEmpty)
{
Point deltaLocation = new Point(intersectedPhysicalViewPort.X - pageLayoutData.ViewablePageBounds.X, intersectedPhysicalViewPort.Y - pageLayoutData.ViewablePageBounds.Y);
Size deltaSize = new Size(pageLayoutData.ViewablePageBounds.Width - intersectedPhysicalViewPort.Width, pageLayoutData.ViewablePageBounds.Height - intersectedPhysicalViewPort.Height);
deltaSize.Width -= deltaLocation.X;
deltaSize.Height -= deltaLocation.Y;
//Get the intersecting rectangle
Rectangle insersectedLogicalViewPort = Rectangle.Empty;
insersectedLogicalViewPort.X = pageLayoutData.LogicalPageBounds.X + deltaLocation.X;
insersectedLogicalViewPort.Y = pageLayoutData.LogicalPageBounds.Y + deltaLocation.Y;
insersectedLogicalViewPort.Width = pageLayoutData.LogicalPageBounds.Width - deltaLocation.X;
insersectedLogicalViewPort.Width -= deltaSize.Width;
insersectedLogicalViewPort.Height = pageLayoutData.LogicalPageBounds.Height - deltaLocation.Y;
insersectedLogicalViewPort.Height -= deltaSize.Height;
transformedViewPort = (transformedViewPort.IsEmpty) ? insersectedLogicalViewPort : Rectangle.Union(transformedViewPort, insersectedLogicalViewPort);
}
}
return transformedViewPort;
}
public override Rectangle MapOutRectangleFromLayout(Rectangle logicalRectangle)
{
Rectangle transformedViewPort = Rectangle.Empty;
//Now we start mapping the rectangle based on page layout
foreach (PageLayoutData pageLayoutData in this.pageLayoutInfo)
{
Rectangle intersectedLogicalViewPort = logicalRectangle;
intersectedLogicalViewPort.Intersect(pageLayoutData.LogicalPageBounds);
if (!intersectedLogicalViewPort.IsEmpty)
{
Point deltaLocation = new Point(intersectedLogicalViewPort.X - pageLayoutData.LogicalPageBounds.X, intersectedLogicalViewPort.Y - pageLayoutData.LogicalPageBounds.Y);
Size deltaSize = new Size(pageLayoutData.LogicalPageBounds.Width - intersectedLogicalViewPort.Width, pageLayoutData.LogicalPageBounds.Height - intersectedLogicalViewPort.Height);
deltaSize.Width -= deltaLocation.X;
deltaSize.Height -= deltaLocation.Y;
//Get the intersecting rectangle
Rectangle insersectedPhysicalViewPort = Rectangle.Empty;
insersectedPhysicalViewPort.X = pageLayoutData.ViewablePageBounds.X + deltaLocation.X;
insersectedPhysicalViewPort.Y = pageLayoutData.ViewablePageBounds.Y + deltaLocation.Y;
insersectedPhysicalViewPort.Width = pageLayoutData.ViewablePageBounds.Width - deltaLocation.X;
insersectedPhysicalViewPort.Width -= deltaSize.Width;
insersectedPhysicalViewPort.Height = pageLayoutData.ViewablePageBounds.Height - deltaLocation.Y;
insersectedPhysicalViewPort.Height -= deltaSize.Height;
transformedViewPort = (transformedViewPort.IsEmpty) ? insersectedPhysicalViewPort : Rectangle.Union(transformedViewPort, insersectedPhysicalViewPort);
}
}
return transformedViewPort;
}
public override Point MapInCoOrdToLayout(Point logicalPoint)
{
//Only for default layout we scale the coordinates outside the pageboundry for all other cases scaling fails
foreach (PageLayoutData pageLayoutData in this.pageLayoutInfo)
{
if (pageLayoutData.PageBounds.Contains(logicalPoint))
{
Point delta = new Point(logicalPoint.X - pageLayoutData.ViewablePageBounds.Left, logicalPoint.Y - pageLayoutData.ViewablePageBounds.Top);
logicalPoint = new Point(pageLayoutData.LogicalPageBounds.Left + delta.X, pageLayoutData.LogicalPageBounds.Top + delta.Y);
break;
}
}
return logicalPoint;
}
public override Point MapOutCoOrdFromLayout(Point logicalPoint)
{
foreach (PageLayoutData pageLayoutData in this.pageLayoutInfo)
{
if (pageLayoutData.LogicalPageBounds.Contains(logicalPoint))
{
Point delta = new Point(logicalPoint.X - pageLayoutData.LogicalPageBounds.Left, logicalPoint.Y - pageLayoutData.LogicalPageBounds.Top);
logicalPoint = new Point(pageLayoutData.ViewablePageBounds.Left + delta.X, pageLayoutData.ViewablePageBounds.Top + delta.Y);
break;
}
}
return logicalPoint;
}
public override void OnPaint(PaintEventArgs e, ViewPortData viewPortData)
{
Graphics graphics = e.Graphics;
Debug.Assert(graphics != null);
AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
//Get the drawing canvas
Bitmap memoryBitmap = viewPortData.MemoryBitmap;
Debug.Assert(memoryBitmap != null);
//Fill the background using the workspace color so that we communicate the paging concept
graphics.FillRectangle(Brushes.White, new Rectangle(Point.Empty, memoryBitmap.Size));
//Fill the background using the workspace color so that we communicate the paging concept
//if there is no workflow watermark, just return
if (ambientTheme.WorkflowWatermarkImage == null)
return;
//Create the transformation matrix and calculate the physical viewport without translation and scaling
//We need to get the physical view port due to the fact that there can be circustances when zoom percentage
//is very high, logical view port can be empty in such cases
GraphicsContainer graphicsState = graphics.BeginContainer();
Matrix coOrdTxMatrix = new Matrix();
coOrdTxMatrix.Scale(viewPortData.Scaling.Width, viewPortData.Scaling.Height, MatrixOrder.Prepend);
coOrdTxMatrix.Invert();
Point[] points = new Point[] { viewPortData.Translation, new Point(viewPortData.ViewPortSize) };
coOrdTxMatrix.TransformPoints(points);
Rectangle physicalViewPort = new Rectangle(points[0], new Size(points[1]));
//because the watermark image needs to be scaled according to the zoom level, we
//a) scale the graphics of the bitmap up by the zoom factor
//a) scale the coordinates transform matrix down by the zoom factor
coOrdTxMatrix = new Matrix();
coOrdTxMatrix.Scale(viewPortData.Scaling.Width / (float)this.parentView.Zoom * 100.0f, viewPortData.Scaling.Height / (float)this.parentView.Zoom * 100.0f);
//Make sure that we now clear the translation factor
Matrix graphicsMatrics = new Matrix();
graphicsMatrics.Scale((float)this.parentView.Zoom / 100.0f, (float)this.parentView.Zoom / 100.0f);
graphics.Transform = graphicsMatrics;
foreach (PageLayoutData pageLayoutData in this.pageLayoutInfo)
{
//We do not draw the non intersecting pages, get the intersected viewport
//We purposely use the physical viewport here because, there are cases in which the viewport
//will not contain any logical bitmap areas in which case we atleast need to draw the pages properly
if (!pageLayoutData.PageBounds.IntersectsWith(physicalViewPort))
continue;
//Draw the watermark into the in-memory bitmap
//This is the area of the viewport bitmap we need to copy on the page
Rectangle viewPortBitmapArea = Rectangle.Empty;
viewPortBitmapArea.X = pageLayoutData.LogicalPageBounds.X - viewPortData.LogicalViewPort.X;
viewPortBitmapArea.Y = pageLayoutData.LogicalPageBounds.Y - viewPortData.LogicalViewPort.Y;
viewPortBitmapArea.Width = pageLayoutData.LogicalPageBounds.Width;
viewPortBitmapArea.Height = pageLayoutData.LogicalPageBounds.Height;
//This rectangle is in translated logical units, we need to scale it down
points = new Point[] { viewPortBitmapArea.Location, new Point(viewPortBitmapArea.Size) };
coOrdTxMatrix.TransformPoints(points);
viewPortBitmapArea.Location = points[0];
viewPortBitmapArea.Size = new Size(points[1]);
ActivityDesignerPaint.DrawImage(graphics, ambientTheme.WorkflowWatermarkImage, viewPortBitmapArea, new Rectangle(Point.Empty, ambientTheme.WorkflowWatermarkImage.Size), ambientTheme.WatermarkAlignment, AmbientTheme.WatermarkTransparency, false);
}
//Now clear the matrix
graphics.EndContainer(graphicsState);
}
public override void OnPaintWorkflow(PaintEventArgs e, ViewPortData viewPortData)
{
Graphics graphics = e.Graphics;
Debug.Assert(graphics != null);
Bitmap memoryBitmap = viewPortData.MemoryBitmap;
Debug.Assert(memoryBitmap != null);
//Get the drawing canvas
AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
//We set the highest quality interpolation so that we do not loose the image quality
GraphicsContainer graphicsState = graphics.BeginContainer();
//Fill the background using the workspace color so that we communicate the paging concept
Rectangle workspaceRectangle = new Rectangle(Point.Empty, memoryBitmap.Size);
graphics.FillRectangle(AmbientTheme.WorkspaceBackgroundBrush, workspaceRectangle);
using (Font headerFooterFont = new Font(ambientTheme.Font.FontFamily, ambientTheme.Font.Size / this.scaling, ambientTheme.Font.Style))
{
int currentPage = 0;
Matrix emptyMatrix = new Matrix();
//Create the transformation matrix and calculate the physical viewport without translation and scaling
//We need to get the physical view port due to the fact that there can be circustances when zoom percentage
//is very high, logical view port can be empty in such cases
Matrix coOrdTxMatrix = new Matrix();
coOrdTxMatrix.Scale(viewPortData.Scaling.Width, viewPortData.Scaling.Height, MatrixOrder.Prepend);
coOrdTxMatrix.Invert();
Point[] points = new Point[] { viewPortData.Translation, new Point(viewPortData.ViewPortSize) };
coOrdTxMatrix.TransformPoints(points);
coOrdTxMatrix.Invert();
Rectangle physicalViewPort = new Rectangle(points[0], new Size(points[1]));
//Create the data for rendering header/footer
WorkflowPrintDocument.HeaderFooterData headerFooterData = new WorkflowPrintDocument.HeaderFooterData();
headerFooterData.HeaderFooterMargins = this.headerFooterMargins;
headerFooterData.PrintTime = this.previewTime;
headerFooterData.TotalPages = this.pageLayoutInfo.Count;
headerFooterData.Scaling = this.scaling;
headerFooterData.Font = headerFooterFont;
WorkflowDesignerLoader serviceDesignerLoader = this.serviceProvider.GetService(typeof(WorkflowDesignerLoader)) as WorkflowDesignerLoader;
headerFooterData.FileName = (serviceDesignerLoader != null) ? serviceDesignerLoader.FileName : String.Empty;
//Create the viewport transformation matrix
Matrix viewPortMatrix = new Matrix();
viewPortMatrix.Scale(viewPortData.Scaling.Width, viewPortData.Scaling.Height, MatrixOrder.Prepend);
viewPortMatrix.Translate(-viewPortData.Translation.X, -viewPortData.Translation.Y, MatrixOrder.Append);
//We now have the viewport properly drawn, now we need to draw it on the actual graphics object
//Now that we have the designer bitmap we start splicing it based on the pages
//Note that this is quite expensive operation and hence one should try to use
//The memory bitmap we have got is scaled appropriately
foreach (PageLayoutData pageLayoutData in this.pageLayoutInfo)
{
currentPage += 1;
//We do not draw the non intersecting pages, get the intersected viewport
//We purposely use the physical viewport here because, there are cases in which the viewport
//will not contain any logical bitmap areas in which case we atleast need to draw the pages properly
if (!pageLayoutData.PageBounds.IntersectsWith(physicalViewPort) || pageLayoutData.PageBounds.Width <= 0 || pageLayoutData.PageBounds.Height <= 0)
continue;
//******START PAGE DRAWING, FIRST DRAW THE OUTLINE
//Scale and translate so that we can draw the pages
graphics.Transform = viewPortMatrix;
graphics.FillRectangle(Brushes.White, pageLayoutData.PageBounds);
ActivityDesignerPaint.DrawDropShadow(graphics, pageLayoutData.PageBounds, Color.Black, AmbientTheme.DropShadowWidth, LightSourcePosition.Left | LightSourcePosition.Top, 0.2f, false);
//***START BITMAP SPLICING
//Draw spliced bitmap for the page if we have any displayable area
Rectangle intersectedViewPort = pageLayoutData.LogicalPageBounds;
intersectedViewPort.Intersect(viewPortData.LogicalViewPort);
if (!intersectedViewPort.IsEmpty)
{
//Make sure that we now clear the translation factor
graphics.Transform = emptyMatrix;
//Paint bitmap on the pages
//Now that the page rectangle is actually drawn, we will scale down the Location of page rectangle
//so that we can draw the viewport bitmap part on it
Point bitmapDrawingPoint = Point.Empty;
bitmapDrawingPoint.X = pageLayoutData.ViewablePageBounds.X + Math.Abs(pageLayoutData.LogicalPageBounds.X - intersectedViewPort.X);
bitmapDrawingPoint.Y = pageLayoutData.ViewablePageBounds.Y + Math.Abs(pageLayoutData.LogicalPageBounds.Y - intersectedViewPort.Y);
points = new Point[] { bitmapDrawingPoint };
coOrdTxMatrix.TransformPoints(points);
bitmapDrawingPoint = new Point(points[0].X - viewPortData.Translation.X, points[0].Y - viewPortData.Translation.Y);
//This is the area of the viewport bitmap we need to copy on the page
Rectangle viewPortBitmapArea = Rectangle.Empty;
viewPortBitmapArea.X = intersectedViewPort.X - viewPortData.LogicalViewPort.X;
viewPortBitmapArea.Y = intersectedViewPort.Y - viewPortData.LogicalViewPort.Y;
viewPortBitmapArea.Width = intersectedViewPort.Width;
viewPortBitmapArea.Height = intersectedViewPort.Height;
//This rectangle is in translated logical units, we need to scale it down
points = new Point[] { viewPortBitmapArea.Location, new Point(viewPortBitmapArea.Size) };
coOrdTxMatrix.TransformPoints(points);
viewPortBitmapArea.Location = points[0];
viewPortBitmapArea.Size = new Size(points[1]);
ActivityDesignerPaint.DrawImage(graphics, memoryBitmap, new Rectangle(bitmapDrawingPoint, viewPortBitmapArea.Size), viewPortBitmapArea, DesignerContentAlignment.Fill, 1.0f, WorkflowTheme.CurrentTheme.AmbientTheme.DrawGrayscale);
}
//***END BITMAP SPLICING
//Draw the page outline
graphics.Transform = viewPortMatrix;
graphics.DrawRectangle(Pens.Black, pageLayoutData.PageBounds);
//Draw the printable page outline
graphics.DrawRectangle(ambientTheme.ForegroundPen, pageLayoutData.ViewablePageBounds.Left - 3, pageLayoutData.ViewablePageBounds.Top - 3, pageLayoutData.ViewablePageBounds.Width + 6, pageLayoutData.ViewablePageBounds.Height + 6);
//Draw the header and footer after we draw the actual page
headerFooterData.PageBounds = pageLayoutData.PageBounds;
headerFooterData.PageBoundsWithoutMargin = pageLayoutData.ViewablePageBounds;
headerFooterData.CurrentPage = currentPage;
//Draw the header
if (this.printDocument.PageSetupData.HeaderTemplate.Length > 0)
this.printDocument.PrintHeaderFooter(graphics, true, headerFooterData);
//Draw footer
if (this.printDocument.PageSetupData.FooterTemplate.Length > 0)
this.printDocument.PrintHeaderFooter(graphics, false, headerFooterData);
//***END DRAWING HEADER FOOTER
}
graphics.EndContainer(graphicsState);
}
}
public override void Update(Graphics graphics, LayoutUpdateReason reason)
{
//do not recalculate pages when it's just a zoom change
if (reason == LayoutUpdateReason.ZoomChanged)
return;
if (graphics == null)
throw new ArgumentException("graphics");
//Set the scaling, pageSize, margins, pageseparator by reserse scaling; so that when we actually scale
//at the time of drawing things will be correctly calculated
Size margin = WorkflowTheme.CurrentTheme.AmbientTheme.Margin;
Size paperSize = GetPaperSize(graphics);
Margins margins = GetAdjustedMargins(graphics);
Size rootDesignerSize = (this.parentView.RootDesigner != null) ? this.parentView.RootDesigner.Size : Size.Empty;
if (!rootDesignerSize.IsEmpty)
{
Size selectionSize = WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize;
rootDesignerSize.Width += 3 * selectionSize.Width;
rootDesignerSize.Height += 3 * selectionSize.Height;
}
//STEP1 : Calculate the scaling factor
if (this.printDocument.PageSetupData.AdjustToScaleFactor)
{
this.scaling = ((float)this.printDocument.PageSetupData.ScaleFactor / 100.0f);
}
else
{
Size printableArea = new Size(paperSize.Width - (margins.Left + margins.Right), paperSize.Height - (margins.Top + margins.Bottom));
printableArea.Width = Math.Max(printableArea.Width, 1);
printableArea.Height = Math.Max(printableArea.Height, 1);
PointF scaleFactor = new PointF(
((float)this.printDocument.PageSetupData.PagesWide * (float)printableArea.Width / (float)rootDesignerSize.Width),
((float)this.printDocument.PageSetupData.PagesTall * (float)printableArea.Height / (float)rootDesignerSize.Height));
//Take the minimum scaling as we do not want to unevenly scale the bitmap
this.scaling = Math.Min(scaleFactor.X, scaleFactor.Y);
//leave just 3 digital points (also, that will remove potential problems with ceiling e.g. when the number of pages would be 3.00000000001 we'll get 4)
this.scaling = (float)(Math.Floor((double)this.scaling * 1000.0d) / 1000.0d);
}
//STEP2 : Calculate the pagesize
this.pageSize = paperSize;
this.pageSize.Width = Convert.ToInt32(Math.Ceiling(((float)this.pageSize.Width) / this.scaling));
this.pageSize.Height = Convert.ToInt32(Math.Ceiling(((float)this.pageSize.Height) / this.scaling));
//STEP3 : Calculate the page separator
IDesignerOptionService designerOptionService = this.serviceProvider.GetService(typeof(IDesignerOptionService)) as IDesignerOptionService;
if (designerOptionService != null)
{
object separator = designerOptionService.GetOptionValue("WinOEDesigner", "PageSeparator");
PageSeparator = (separator != null) ? (Size)separator : PrintPreviewLayout.DefaultPageSeparator;
}
PageSeparator = new Size(Convert.ToInt32(Math.Ceiling(((float)PageSeparator.Width) / this.scaling)), Convert.ToInt32(Math.Ceiling(((float)PageSeparator.Height) / this.scaling)));
//STEP4: Calculate the margins after reverse scaling the margins, so that when we set the normal scalezoom we have correct margins
PageMargins = margins;
PageMargins.Left = Convert.ToInt32((float)PageMargins.Left / this.scaling);
PageMargins.Right = Convert.ToInt32((float)PageMargins.Right / this.scaling);
PageMargins.Top = Convert.ToInt32((float)PageMargins.Top / this.scaling);
PageMargins.Bottom = Convert.ToInt32((float)PageMargins.Bottom / this.scaling);
//STEP5: Calculate the header and footer margins
this.headerFooterMargins.Top = Convert.ToInt32((float)this.printDocument.PageSetupData.HeaderMargin / this.scaling);
this.headerFooterMargins.Bottom = Convert.ToInt32((float)this.printDocument.PageSetupData.FooterMargin / this.scaling);
this.previewTime = DateTime.Now;
//STEP6: Calculate the the row columns
Size viewablePageSize = new Size(this.pageSize.Width - (PageMargins.Left + PageMargins.Right), this.pageSize.Height - (PageMargins.Top + PageMargins.Bottom));
viewablePageSize.Width = Math.Max(viewablePageSize.Width, 1);
viewablePageSize.Height = Math.Max(viewablePageSize.Height, 1);
//We check for greater than 1 here as the division might introduce rounding factor
//Columns
this.rowColumns.Width = rootDesignerSize.Width / viewablePageSize.Width;
this.rowColumns.Width += ((rootDesignerSize.Width % viewablePageSize.Width) > 1) ? 1 : 0;
this.rowColumns.Width = Math.Max(1, this.rowColumns.Width);
//Rows
this.rowColumns.Height = rootDesignerSize.Height / viewablePageSize.Height;
this.rowColumns.Height += ((rootDesignerSize.Height % viewablePageSize.Height) > 1) ? 1 : 0;
this.rowColumns.Height = Math.Max(1, this.rowColumns.Height);
//STEP7: Calculate the pagelayoutdata
this.pageLayoutInfo.Clear();
//Create the layout data
for (int row = 0; row < this.rowColumns.Height; row++)
{
for (int column = 0; column < this.rowColumns.Width; column++)
{
Point pageLocation = Point.Empty;
pageLocation.X = (column * this.pageSize.Width) + ((column + 1) * PageSeparator.Width);
pageLocation.Y = (row * this.pageSize.Height) + ((row + 1) * PageSeparator.Height);
Point viewablePageLocation = Point.Empty;
viewablePageLocation.X = pageLocation.X + PageMargins.Left;
viewablePageLocation.Y = pageLocation.Y + PageMargins.Top;
Rectangle logicalBounds = new Rectangle(column * viewablePageSize.Width, row * viewablePageSize.Height, viewablePageSize.Width, viewablePageSize.Height);
Rectangle pageBounds = new Rectangle(pageLocation, this.pageSize);
Rectangle viewablePageBounds = new Rectangle(viewablePageLocation, viewablePageSize);
this.pageLayoutInfo.Add(new PageLayoutData(logicalBounds, pageBounds, viewablePageBounds, new Point(column, row)));
}
}
}
#endregion
#region Helpers
private Size GetPaperSize(Graphics graphics)
{
Size size = Size.Empty;
PaperSize paperSize = this.printDocument.DefaultPageSettings.PaperSize;
this.printDocument.DefaultPageSettings.PaperSize = paperSize;
if (this.printDocument.PageSetupData.Landscape)
{
size.Width = Math.Max(paperSize.Height, 1);
size.Height = Math.Max(paperSize.Width, 1);
}
else
{
size.Width = Math.Max(paperSize.Width, 1);
size.Height = Math.Max(paperSize.Height, 1);
}
return size;
}
private Margins GetAdjustedMargins(Graphics graphics)
{
Margins margins = this.printDocument.PageSetupData.Margins;
if (this.printDocument.PageSetupData.Landscape)
{
int temp = margins.Left;
margins.Left = margins.Right;
margins.Right = temp;
temp = margins.Bottom;
margins.Bottom = margins.Top;
margins.Top = temp;
}
//Read the unprintable margins
Margins hardMargins = new Margins();
using (Graphics printerGraphics = this.printDocument.PrinterSettings.CreateMeasurementGraphics())
hardMargins = this.printDocument.GetHardMargins(printerGraphics);
Margins adjustedMargins = new Margins(Math.Max(margins.Left, hardMargins.Left),
Math.Max(margins.Right, hardMargins.Right),
Math.Max(margins.Top, hardMargins.Top),
Math.Max(margins.Bottom, hardMargins.Bottom));
return adjustedMargins;
}
private Size PageSeparator
{
get
{
return this.pageSeparator;
}
set
{
this.pageSeparator = value;
}
}
private Margins PageMargins
{
get
{
return this.pageMargins;
}
set
{
this.pageMargins = value;
}
}
#endregion
#region Struct PageLayoutData
// Please note that all the page bounds in here are in scaled coordinates
// PageBounds are the bounds of the page in scaled coordinates with margins
// ViewablePageBounds are the bounds in scaled coordinates without margins
// LogicalBounds are the page bounds when mapped to our logical coordinate system.
// |-----------------------|
// |PageBounds |
// | |---------------|...........Mapped to logical page bounds
// | |Viewable | |
// | |PageBounds | |
// | | | |
// | | | |
// | | | |
// | | | |
// | | | |
// | | | |
// | | | |
// | |---------------|...........
// | |
// -------------------------
private struct PageLayoutData
{
//logical page bounds start from 0,0 and go to the size of the designer
public Rectangle LogicalPageBounds;
//Page bounds are used to draw the complete page in the layout with margin
public Rectangle PageBounds;
//screen viewable page bounds (Excludes the margin area)
public Rectangle ViewablePageBounds;
//row column position contains the row column position of the page
public Point Position;
public PageLayoutData(Rectangle logicalPageBounds, Rectangle pageBounds, Rectangle viewablePageBounds, Point rowColumnPos)
{
this.LogicalPageBounds = logicalPageBounds;
this.PageBounds = pageBounds;
//Exclude the margins themselves
this.ViewablePageBounds = viewablePageBounds;
this.Position = rowColumnPos;
}
}
#endregion
}
#endregion
}
|