Click here to Skip to main content
15,891,316 members
Articles / Desktop Programming / WPF

200% Reflective Class Diagram Creation Tool

Rate me:
Please Sign up or sign in to vote.
4.92/5 (200 votes)
20 Feb 2014CPOL22 min read 584.4K   11.5K   437  
WPF: Version II of my 100% Reflective class diagram creation tool.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Shapes;
using SUT.PrintEngine.Utils;

namespace SUT.PrintEngine.Paginators
{
    public class VisualPaginator : DocumentPaginator
    {
        protected const string DrawingVisualNullMessage = "Drawing Visual Source is null";
        public int HorizontalPageCount;

        private readonly Size _printSize;
        protected Thickness PageMargins;
        private readonly Thickness _originalMargin;
        protected Size ContentSize;
        protected Pen FramePen;
        protected readonly List<double> AdjustedPageWidths = new List<double>();
        protected readonly List<double> AdjustedPageHeights = new List<double>();
        private int _verticalPageCount;
        protected Rect FrameRect;
        public List<DrawingVisual> DrawingVisuals;
        private IDocumentPaginatorSource _document;
        protected double PrintablePageWidth;
        public DrawingVisual DrawingVisual;
        protected double PrintablePageHeight;
        public bool ShowPageMarkers;
        protected double HeaderHeight;
        public event EventHandler<PageEventArgs> PageCreated;

        public void OnPageCreated(PageEventArgs e)
        {
            EventHandler<PageEventArgs> handler = PageCreated;
            if (handler != null) handler(this, e);
        }

        public VisualPaginator(DrawingVisual source, Size printSize, Thickness pageMargins, Thickness originalMargin)
        {
            DrawingVisual = source;
            _printSize = printSize;
            PageMargins = pageMargins;
            _originalMargin = originalMargin;

        }

        public void Initialize(bool isMarkPageNumbers)
        {
            ShowPageMarkers = isMarkPageNumbers;
            var totalHorizontalMargin = PageMargins.Left + PageMargins.Right;
            var toltalVerticalMargin = PageMargins.Top + PageMargins.Bottom;

            PrintablePageWidth = PageSize.Width - totalHorizontalMargin;
            PrintablePageHeight = PageSize.Height - toltalVerticalMargin;

            ContentSize = new Size(_printSize.Width - totalHorizontalMargin, _printSize.Height - toltalVerticalMargin);
            FrameRect = new Rect(new Point(PageMargins.Left, PageMargins.Top), new Size(_printSize.Width - totalHorizontalMargin, _printSize.Height - toltalVerticalMargin));
            FramePen = new Pen(Brushes.Black, 0);

            HorizontalPageCount = GetHorizontalPageCount();

            _verticalPageCount = GetVerticalPageCount();

            CreateAllPageVisuals();
        }

        private void CreateAllPageVisuals()
        {
            DrawingVisuals = new List<DrawingVisual>();

            for (var verticalPageNumber = 0; verticalPageNumber < _verticalPageCount; verticalPageNumber++)
            {
                for (var horizontalPageNumber = 0; horizontalPageNumber < HorizontalPageCount; horizontalPageNumber++)
                {
                    const float horizontalOffset = 0;
                    var verticalOffset = (float)(verticalPageNumber * PrintablePageHeight);
                    var pageBounds = GetPageBounds(horizontalPageNumber, verticalPageNumber, horizontalOffset, verticalOffset);
                    var visual = new DrawingVisual();
                    using (var dc = visual.RenderOpen())
                    {
                        CreatePageVisual(pageBounds, DrawingVisual,
                                         IsFooterPage(horizontalPageNumber), dc);
                    }
                    DrawingVisuals.Add(visual);
                }
            }
        }

        protected virtual Rect GetPageBounds(int horizontalPageNumber, int verticalPageNumber, float horizontalOffset, float verticalOffset)
        {
            var x = (float)((horizontalPageNumber * PrintablePageWidth));
            return new Rect { X = x, Y = verticalOffset, Size = ContentSize };
        }

        private static bool IsFooterPage(int horizontalPageNumber)
        {
            return horizontalPageNumber == 0;
        }

        protected virtual int GetVerticalPageCount()
        {
            int count;
            if (IsDrawingNotNull())
                count = (int)Math.Ceiling(GetDrawingBounds().Height / (PrintablePageHeight));
            else
            {
                throw new NullReferenceException(DrawingVisualNullMessage);
            }
            return count;
        }

        protected virtual Rect GetDrawingBounds()
        {
            return DrawingVisual.Drawing.Bounds;
        }

        protected virtual bool IsDrawingNotNull()
        {
            return DrawingVisual.Drawing != null;
        }

        public override DocumentPage GetPage(int pageNumber)
        {
            DrawingVisual pageVisual = GetPageVisual(pageNumber);
            var documentPage = new DocumentPage(pageVisual, PageSize, FrameRect, FrameRect);
            if (ShowPageMarkers)
                InsertPageMarkers(pageNumber + 1, documentPage);
            OnPageCreated(new PageEventArgs(pageNumber));
            return documentPage;
        }

        private DrawingVisual GetPageVisual(int pageNumber)
        {
            var totalHorizontalMargin = _originalMargin.Left + _originalMargin.Right;
            var toltalVerticalMargin = _originalMargin.Top + _originalMargin.Bottom;
            var printablePageWidth = PageSize.Width - totalHorizontalMargin;
            var printablePageHeight = PageSize.Height - toltalVerticalMargin - 10;

            var xFactor = printablePageWidth / PageSize.Width;
            var yFactor = printablePageHeight / PageSize.Height;
            var scaleFactor = Math.Max(xFactor, yFactor);
            var pageVisual = DrawingVisuals[pageNumber];
            var transformGroup = new TransformGroup();
            var scaleTransform = new ScaleTransform(scaleFactor, scaleFactor);
            var translateTransform = new TranslateTransform(_originalMargin.Left, _originalMargin.Top);
            transformGroup.Children.Add(translateTransform);
            transformGroup.Children.Add(scaleTransform);
            pageVisual.Transform = transformGroup;
            return pageVisual;
        }

        protected virtual void InsertPageMarkers(int pageNumber, DocumentPage documentPage)
        {
            var labelDrawingVisual = new DrawingVisual();
            using (var drawingContext = labelDrawingVisual.RenderOpen())
            {
                var pageNumberContent = pageNumber + "/" + PageCount;
                var ft = new FormattedText(pageNumberContent,
                                           Thread.CurrentThread.CurrentCulture,
                                           FlowDirection.LeftToRight,
                                           new Typeface("Verdana"),
                                           15, Brushes.Black);

                drawingContext.DrawText(ft, new Point(PageMargins.Left, PageMargins.Top));
            }

            var drawingGroup = new DrawingGroup();
            drawingGroup.Children.Add(((DrawingVisual)documentPage.Visual).Drawing);
            drawingGroup.Children.Add(labelDrawingVisual.Drawing);

            var currentDrawingVisual = (DrawingVisual)documentPage.Visual;
            using (var currentDrawingContext = currentDrawingVisual.RenderOpen())
            {
                currentDrawingContext.DrawDrawing(drawingGroup);
                currentDrawingContext.PushOpacityMask(Brushes.White);
            }
        }

        public override bool IsPageCountValid
        {
            get
            {
                return true;
            }
        }

        public override int PageCount
        {
            get
            {
                return _verticalPageCount * HorizontalPageCount;
            }
        }

        public override sealed Size PageSize
        {
            get { return _printSize; }
            set { }
        }

        public override IDocumentPaginatorSource Source
        {
            get
            {
                return _document;
            }
        }

        public void UpdatePageMarkers(bool showPageMarkers)
        {
            ShowPageMarkers = showPageMarkers;
        }

        public IDocumentPaginatorSource CreateDocumentPaginatorSource()
        {
            var document = new FixedDocument();
            for (var i = 0; i < PageCount; i++)
            {
                var page = GetPage(i);
                var fp = new FixedPage { ContentBox = FrameRect, BleedBox = FrameRect, Width = page.Size.Width, Height = page.Size.Height };

                var vb = new DrawingBrush(DrawingVisuals[i].Drawing)
                             {
                                 Stretch = Stretch.Uniform,
                                 ViewboxUnits = BrushMappingMode.Absolute,
                                 Viewbox = new Rect(page.Size)
                             };
                var totalHorizontalMargin = _originalMargin.Left + _originalMargin.Right;
                var toltalVerticalMargin = _originalMargin.Top + _originalMargin.Bottom;
                var printablePageWidth = PageSize.Width - totalHorizontalMargin;
                var printablePageHeight = PageSize.Height - toltalVerticalMargin - 10;

                var rect = new Rect(_originalMargin.Left, _originalMargin.Top, printablePageWidth, printablePageHeight);
                fp.Children.Add(CreateContentRectangle(vb, rect));
                var pageContent = new PageContent();
                ((IAddChild)pageContent).AddChild(fp);
                document.Pages.Add(pageContent);
            }
            _document = document;
            _document.DocumentPaginator.PageSize = new Size(PageSize.Width, PageSize.Height);
            return _document;
        }

        public List<FixedDocument> CreateFixedDocumentsForEachPage()
        {
            var documents = new List<FixedDocument>();
            for (var i = 0; i < PageCount; i++)
            {
                var document = new FixedDocument();
                var page = GetPage(i);
                var fp = new FixedPage { ContentBox = FrameRect, BleedBox = FrameRect, Width = page.Size.Width, Height = page.Size.Height };
                var vb = new DrawingBrush(DrawingVisuals[i].Drawing) { Stretch = Stretch.Uniform, ViewboxUnits = BrushMappingMode.Absolute, Viewbox = new Rect(page.Size) };

                var totalHorizontalMargin = _originalMargin.Left + _originalMargin.Right;
                var toltalVerticalMargin = _originalMargin.Top + _originalMargin.Bottom;
                var printablePageWidth = PageSize.Width - totalHorizontalMargin;
                var printablePageHeight = PageSize.Height - toltalVerticalMargin - 10;

                var rect = new Rect(_originalMargin.Left, _originalMargin.Top, printablePageWidth, printablePageHeight);
                fp.Children.Add(CreateContentRectangle(vb, rect));
                var pageContent = new PageContent();
                ((IAddChild)pageContent).AddChild(fp);
                document.Pages.Add(pageContent);
                documents.Add(document);
            }
            return documents;
        }

        public List<FixedDocument> CreateFixedDocumentsForEachPageWithPageNumber(int startPageNumber, double height, string slideName)
        {
            slideName = GetSlideNameForEntityChartHeader(slideName);
            var documents = new List<FixedDocument>();
            for (var i = 0; i < PageCount; i++)
            {
                var document = new FixedDocument();
                var page = GetPage(i);
                var fp = new FixedPage { ContentBox = FrameRect, BleedBox = FrameRect, Width = page.Size.Width, Height = page.Size.Height };
                var vb = new DrawingBrush(DrawingVisuals[i].Drawing) { Stretch = Stretch.Uniform, ViewboxUnits = BrushMappingMode.Absolute, Viewbox = new Rect(page.Size) };

                var totalHorizontalMargin = _originalMargin.Left + _originalMargin.Right;
                var toltalVerticalMargin = _originalMargin.Top + _originalMargin.Bottom;
                var printablePageWidth = PageSize.Width - totalHorizontalMargin;
                var printablePageHeight = height - toltalVerticalMargin - 10;

                var rect = new Rect(_originalMargin.Left, _originalMargin.Top + Constants.CsBook.EntityChartPageHeaderSize, printablePageWidth, printablePageHeight);
                fp.Children.Add(CreateContentRectangle(vb, rect));

                InsertEntityChartPageHeader(fp, startPageNumber++, slideName, i + 1);

                var pageContent = new PageContent();
                ((IAddChild)pageContent).AddChild(fp);
                document.Pages.Add(pageContent);
                documents.Add(document);
            }
            return documents;
        }

        private string GetSlideNameForEntityChartHeader(string slideName)
        {
            var entityChartSlideNameMaxSize = PageSize.Width - Constants.CsBook.PageNumberTextLength - 10;
            if (slideName.Length > entityChartSlideNameMaxSize)
            {
                return string.Format("{0}...", slideName.Substring(0, (int)entityChartSlideNameMaxSize));
            }
            return slideName;
        }

        private void InsertEntityChartPageHeader(FixedPage fp, int pageNumber, string slideName, int pageIndex)
        {
            var slideNameAvailableWidth = PageSize.Width - 2 * Constants.CsBook.PageNumberTextLength;

            var pageNumberLabel = new TextBlock { Text = string.Format("Page {0}", pageNumber) };
            FixedPage.SetLeft(pageNumberLabel, PageSize.Width - Constants.CsBook.PageNumberTextLength);
            FixedPage.SetTop(pageNumberLabel, 10);
            fp.Children.Add(pageNumberLabel);

            var noOfTotal = string.Format("({0} of {1})", pageIndex, PageCount);
            const int noOfTotalFontSize = 10;
            var ft1 = new FormattedText(noOfTotal, Thread.CurrentThread.CurrentCulture,
                                       FlowDirection.LeftToRight, new Typeface("Verdana"), noOfTotalFontSize, Brushes.Black);
            var pageNumberTextWidth = ft1.Width;
            var availableSlideNameWidth = slideNameAvailableWidth - pageNumberTextWidth;
            ft1 = new FormattedText(slideName, Thread.CurrentThread.CurrentCulture,
                                       FlowDirection.LeftToRight, new Typeface("Verdana"), 20, Brushes.Black);
            var slideNameTextWidth = ft1.Width;
            var txtBlockSlideNameWidth = (slideNameTextWidth > availableSlideNameWidth)
                                             ? availableSlideNameWidth
                                             : slideNameTextWidth;
            var stackPanel = new StackPanel { Orientation = Orientation.Horizontal};
            var txtBlockSlideName = new TextBlock
                                        {
                                            Text = slideName,
                                            Width = Math.Max(0, txtBlockSlideNameWidth),
                                            FontFamily = new FontFamily("Verdana"),
                                            FontSize = 20,
                                            TextTrimming = TextTrimming.CharacterEllipsis,
                                            Padding = new Thickness(0),
                                            Margin = new Thickness(0),
                                            VerticalAlignment = VerticalAlignment.Bottom
                                        };
            var txtBlockPageNo = new TextBlock
                                     {
                                         Text = noOfTotal,
                                         Width = pageNumberTextWidth,
                                         FontFamily = new FontFamily("Verdana"),
                                         FontSize = noOfTotalFontSize,
                                         TextTrimming = TextTrimming.None,
                                         VerticalAlignment = VerticalAlignment.Bottom,
                                         Padding = new Thickness(0,0,0,2),
                                         Margin = new Thickness(0)
                                     };
            stackPanel.Children.Add(txtBlockSlideName);
            stackPanel.Children.Add(txtBlockPageNo);
            var label = new Label
                            {
                                Width = slideNameAvailableWidth,
                                Content = stackPanel,
                                HorizontalContentAlignment = HorizontalAlignment.Center,
                                Padding = new Thickness(0),
                                Margin = new Thickness(0)
                            };


            FixedPage.SetLeft(label, Constants.CsBook.PageNumberTextLength);
            FixedPage.SetTop(label, 10);
            fp.Children.Add(label);
        }

        private static Rectangle CreateContentRectangle(Brush vb, Rect rect)
        {
            var rc = new Rectangle { Width = rect.Width, Height = rect.Height, Fill = vb };
            FixedPage.SetLeft(rc, rect.X);
            FixedPage.SetTop(rc, rect.Y);
            FixedPage.SetRight(rc, rect.Width);
            FixedPage.SetBottom(rc, rect.Height);
            return rc;
        }

        protected virtual void CreatePageVisual(Rect pageBounds, DrawingVisual source, bool isFooterPage, DrawingContext drawingContext)
        {
            drawingContext.DrawRectangle(null, FramePen, new Rect { X = FrameRect.X, Y = FrameRect.Y, Width = FrameRect.Width, Height = FrameRect.Height });
            var offsetX = PageMargins.Left - pageBounds.X - 1;
            var offsetY = PageMargins.Top - pageBounds.Y;
            drawingContext.PushTransform(new TranslateTransform(offsetX, offsetY + HeaderHeight));
            var pg = new Rect(new Point(pageBounds.X, pageBounds.Y), new Size(pageBounds.Width, pageBounds.Height));
            drawingContext.PushClip(new RectangleGeometry(pg));
            drawingContext.PushOpacityMask(Brushes.White);
            drawingContext.DrawDrawing(source.Drawing);
        }

        protected virtual int GetHorizontalPageCount()
        {
            if (IsDrawingNotNull())
                return (int)Math.Ceiling(GetDrawingBounds().Width / PrintablePageWidth);
            throw new NullReferenceException(DrawingVisualNullMessage);
        }

        public FixedDocument GetDocument(int startIndex, int endIndex)
        {
            var document = new FixedDocument();
            for (var i = startIndex; i < endIndex; i++)
            {
                var fp = new FixedPage { ContentBox = FrameRect, BleedBox = FrameRect };
                var page = GetPage(i);
                var vb = new DrawingBrush(DrawingVisuals[i].Drawing) { Stretch = Stretch.Uniform, ViewboxUnits = BrushMappingMode.Absolute, Viewbox = new Rect(page.Size) };
                fp.Children.Add(CreateContentRectangle(vb, FrameRect));
                var pageContent = new PageContent();
                ((IAddChild)pageContent).AddChild(fp);
                document.Pages.Add(pageContent);
            }
            document.DocumentPaginator.PageSize = new Size(PageSize.Width, PageSize.Height);
            return document;
        }
    }

    public class PageEventArgs : EventArgs
    {
        private readonly int _pageNumber;
        public int PageNumber { get { return _pageNumber; }}

        public PageEventArgs(int pageNumber)
        {
            _pageNumber = pageNumber;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions