65.9K
CodeProject 正在变化。 阅读更多。
Home

Silverlight 中的简化 UI 打印

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2010 年 12 月 1 日

CPOL

3分钟阅读

viewsIcon

24025

一个简化的扩展方法,允许您使用标准选项(如横向打印、缩小以适应、页面居中和打印边距)打印任何元素。

引言

在 Silverlight 4 中进行打印的能力开启了创建比以往更好的基于 Web 的应用程序的大门。Silverlight 现在为我们提供了与用户的打印机连接的机会,以创建他们可能正在查看的内容的丰富打印输出。

虽然设置 PrintDocument 是一项相当简单的任务,并且我们可以用它做很多事情,但在项目在页面上的呈现方式方面,Silverlight 还有很多不足之处。

本文旨在提供一个非常简化的扩展方法,该方法允许以清晰一致的方式打印几乎任何控件。

背景

如果您一直在 Silverlight 中开发任何类型的应用程序,特别是那些呈现大型数据集的应用程序,您无疑需要打印数据集。虽然有很多方法可以做到这一点——包括将用户从应用程序中提取到单独的浏览器窗口中,该窗口呈现 HTML 表——但没有一种方法能够充分利用 Silverlight 打印 API,同时也提供简单清晰的文档打印输出。

打印 API 困难的部分在于,它们在呈现打印页面上的 Silverlight 控件方面也没有提供太多帮助——通常会溢出边缘,或者以奇怪的位置出现在页面上。

通过仔细使用 Transforms 和一个简单的扩展方法,可以实现清晰一致的打印功能。

使用代码

虽然我通过扩展 FrameworkElement 实现了此代码,但此方法可以很容易地修改为在其他位置实现。我更喜欢扩展方法,因为它允许非常简单的执行,例如

//ControlToPrint.Print(PrintDocName, HorizontalAlignment,
//   VerticalAlignment, PageMargin, LandscapePrinting,
//   ShrinkToFit, OnCompleteCallback);
MyControl.Print("My Print Document", HorizontalAlignment.Center, 
                VerticalAlignment.Top, new Thickness(100), true, true, null);

要打印多个项目,您只需在项目列表中调用相同的函数,例如

//(List(<Grid>)).Print(PrintDocName, HorizontalAlignment,
//   VerticalAlignment, PageMargin, LandscapePrinting,
//   ShrinkToFit, OnCompleteCallback);
List<Grid> myListOfGrids = new List<Grid>();
...
myListOfGrids.Print("My Print Document", 
  HorizontalAlignment.Center, VerticalAlignment.Top, 
  new Thickness(100), true, true, null);

Grid g;
...
g.Children.Print"My Print Document", HorizontalAlignment.Center, 
   VerticalAlignment.Top, new Thickness(100), true, true, null);

让我们看看它是如何工作的。

首先,我们创建扩展方法的静态类,该类将包含我们的扩展方法。完成此操作后,任何需要使用它的类都将引用扩展方法命名空间。

public static class Extensions
{  }

然后,我们希望提供三种方法,其中两种重载了最后一种。第一种方法针对单个元素,第二种方法针对 UIElementCollection,而最后一种方法针对通用元素的 List,它最终将是完成所有工作的方法。

public static class Extensions
{
      public static void Print(this FrameworkElement element, string Document, 
             HorizontalAlignment HorizontalAlignment, 
             VerticalAlignment VerticalAlignment, Thickness PageMargin, 
             bool PrintLandscape, bool ShrinkToFit, Action OnPrintComplete)
      {
          Print(new List<FrameworkElement>() { element }, Document, 
                HorizontalAlignment, VerticalAlignment, PageMargin, 
                PrintLandscape, ShrinkToFit, OnPrintComplete);
      }

      public static void Print(this UIElementCollection elements, 
             string Document, HorizontalAlignment HorizontalAlignment, 
             VerticalAlignment VerticalAlignment, Thickness PageMargin, 
             bool PrintLandscape, bool ShrinkToFit, Action OnPrintComplete)
      {
          Print(elements.ToList(), Document, HorizontalAlignment, VerticalAlignment, 
                PageMargin, PrintLandscape, ShrinkToFit, OnPrintComplete);
      }

      public static void Print<T>(this List<T> elements, string Document, 
             HorizontalAlignment HorizontalAlignment, 
             VerticalAlignment VerticalAlignment, Thickness PageMargin, 
             bool PrintLandscape, bool ShrinkToFit, Action OnPrintComplete)
      {
         ...
      }
}

如果对象不是从 FrameworkElement 派生的,则应抛出异常。我们还需要确保正在打印的每个项目都已实际呈现在控件上。这样做的原因是我们需要使用元素的 ActualWidthActualHeight - 这意味着该元素必须出现并具有父对象。

可以通过将控件呈现到其 Opacity 设置为零的父控件来解决此限制。然后,在打印完成后清除父控件。

if (!typeof(FrameworkElement).IsAssignableFrom(elements[currentItemIndex].GetType()))
{
     throw new Exception("Element must be an object inheriting from FrameworkElement");
}

FrameworkElement element = elements[currentItemIndex] as FrameworkElement;

if (element.Parent == null || element.ActualWidth == double.NaN || 
    element.ActualHeight == double.NaN)
{
     throw new Exception("Element must be rendered, " + 
                         "and must have a parent in order to print.");
}

当打印开始时,将调用 PrintPage 事件。此事件是我们用于呈现页面的事件。发生这种情况时,我们要应用 TranslateTransform,然后应用我们的 RotateTransform(如果我们在横向模式下打印),然后应用 ScaleTransform。最后,我们将应用 TranslateTransform 将元素定位在页面上,如参数指定的那样。

重要的是要注意,我们希望以这种特定的顺序执行此操作,以便我们知道应用所有转换的中心点。

正是在此事件方法中,我们迭代我们的 FrameworkElement 列表,单独应用转换,然后打印页面。我们每次都将 HasMorePages 设置为 true,直到到达列表的末尾。

最后,如果我们在横向模式下打印,重要的是要注意,我们要将元素的宽度与页面高度进行比较,并将元素的高度与页面宽度进行比较。

if (!typeof(FrameworkElement).IsAssignableFrom(elements[currentItemIndex].GetType()))
{
    throw new Exception("Element must be an object inheriting from FrameworkElement");
}

FrameworkElement element = elements[currentItemIndex] as FrameworkElement;

if (element.Parent == null || element.ActualWidth == double.NaN || 
                              element.ActualHeight == double.NaN)
{
    throw new Exception("Element must be rendered, " + 
                        "and must have a parent in order to print.");
}
                
TransformGroup transformGroup = new TransformGroup();

//First move to middle of page...
transformGroup.Children.Add(new TranslateTransform() { 
  X = (evt.PrintableArea.Width - element.ActualWidth) / 2, 
  Y = (evt.PrintableArea.Height - element.ActualHeight) / 2 });
double scale = 1;
if (PrintLandscape)
{
    //Then, rotate around the center
    transformGroup.Children.Add(new RotateTransform() { Angle = 90, 
       CenterX = evt.PrintableArea.Width / 2, 
       CenterY = evt.PrintableArea.Height / 2 });

    if (ShrinkToFit)
    {
        if ((element.ActualWidth + PageMargin.Left + PageMargin.Right) > 
                                   evt.PrintableArea.Height)
        {
            scale = Math.Round(evt.PrintableArea.Height / 
              (element.ActualWidth + PageMargin.Left + PageMargin.Right), 2);
        }
        if ((element.ActualHeight + PageMargin.Top + PageMargin.Bottom) > 
                                    evt.PrintableArea.Width)
        {
            double scale2 = Math.Round(evt.PrintableArea.Width / 
              (element.ActualHeight + PageMargin.Top + PageMargin.Bottom), 2);
            scale = (scale2 < scale) ? scale2 : scale;
        }
    }
}
else if (ShrinkToFit)
{
    //Scale down to fit the page + margin
                    
    if ((element.ActualWidth + PageMargin.Left + PageMargin.Right) > 
                               evt.PrintableArea.Width)
    {
        scale = Math.Round(evt.PrintableArea.Width / 
                          (element.ActualWidth + PageMargin.Left + PageMargin.Right), 2);
    }
    if ((element.ActualHeight + PageMargin.Top + PageMargin.Bottom) > 
                                evt.PrintableArea.Height)
    {
        double scale2 = Math.Round(evt.PrintableArea.Height / 
          (element.ActualHeight + PageMargin.Top + PageMargin.Bottom), 2);
        scale = (scale2 < scale) ? scale2 : scale;
    }
}

//Scale down to fit the page + margin
if (scale != 1)
{
    transformGroup.Children.Add(new ScaleTransform() { ScaleX = scale, 
         ScaleY = scale, CenterX = evt.PrintableArea.Width / 2, 
         CenterY = evt.PrintableArea.Height / 2 });
}

if (VerticalAlignment == VerticalAlignment.Top)
{
    //Now move to Top
    if (PrintLandscape)
    {
        transformGroup.Children.Add(new TranslateTransform() { X = 0, 
          Y = PageMargin.Top - (evt.PrintableArea.Height - 
                               (element.ActualWidth * scale)) / 2 });
    }
    else
    {
        transformGroup.Children.Add(new TranslateTransform() { X = 0, 
          Y = PageMargin.Top - (evt.PrintableArea.Height - 
                               (element.ActualHeight * scale)) / 2 });
    }
}
else if (VerticalAlignment == VerticalAlignment.Bottom)
{
    //Now move to Bottom
    if (PrintLandscape)
    {
        transformGroup.Children.Add(new TranslateTransform() { X = 0, 
          Y = ((evt.PrintableArea.Height - 
               (element.ActualWidth * scale)) / 2) - PageMargin.Bottom });
    }
    else
    {
        transformGroup.Children.Add(new TranslateTransform() { X = 0, 
          Y = ((evt.PrintableArea.Height - 
               (element.ActualHeight * scale)) / 2) - PageMargin.Bottom });
    }
}

if (HorizontalAlignment == HorizontalAlignment.Left)
{
    //Now move to Left
    if (PrintLandscape)
    {
        transformGroup.Children.Add(new TranslateTransform() { 
          X = PageMargin.Left - (evt.PrintableArea.Width - 
          (element.ActualHeight * scale)) / 2, Y = 0 });
    }
    else
    {
        transformGroup.Children.Add(new TranslateTransform() { 
          X = PageMargin.Left - (evt.PrintableArea.Width - 
                                (element.ActualWidth * scale)) / 2, Y = 0 });
    }
}
else if (HorizontalAlignment == HorizontalAlignment.Right)
{
    //Now move to Right
    if (PrintLandscape)
    {
        transformGroup.Children.Add(new TranslateTransform() { 
          X = ((evt.PrintableArea.Width - 
               (element.ActualHeight * scale)) / 2) - PageMargin.Right, Y = 0 });
    }
    else
    {
        transformGroup.Children.Add(new TranslateTransform() { 
          X = ((evt.PrintableArea.Width - 
               (element.ActualWidth * scale)) / 2) - PageMargin.Right, Y = 0 });
    }
}

evt.PageVisual = element;
evt.PageVisual.RenderTransform = transformGroup;
                
//Increment to next item,
currentItemIndex++;
                
//If the currentItemIndex is less than the number of elements, keep printing
evt.HasMorePages = currentItemIndex < elements.Count;

最后,当我们完成打印后,我们将需要撤消我们所做的所有转换。为此,我们连接到 EndPrint 方法并迭代我们的元素列表,重置所有转换。

我们通过调用 OnPrintComplete Action 来结束,如果已设置。

foreach (var item in elements)
{
     FrameworkElement element = item as FrameworkElement;
     //Reset everything...
     TransformGroup transformGroup = new TransformGroup();
     transformGroup.Children.Add(new ScaleTransform() { ScaleX = 1, ScaleY = 1 });
     transformGroup.Children.Add(new RotateTransform() { Angle = 0 });
     transformGroup.Children.Add(new TranslateTransform() { X = 0, Y = 0 });
     element.RenderTransform = transformGroup;
}

//Callback to complete
if (OnPrintComplete != null)
{
     OnPrintComplete();
}

组合在一起,我们的最终产品看起来像

public static class Extensions
{
    public static void Print(this FrameworkElement element, 
           string Document, HorizontalAlignment HorizontalAlignment, 
           VerticalAlignment VerticalAlignment, Thickness PageMargin, 
           bool PrintLandscape, bool ShrinkToFit, Action OnPrintComplete)
    {
        Print(new List<FrameworkElement>() { element }, Document, 
              HorizontalAlignment, VerticalAlignment, PageMargin, 
              PrintLandscape, ShrinkToFit, OnPrintComplete);
    }

    public static void Print(this UIElementCollection elements, string Document, 
           HorizontalAlignment HorizontalAlignment, 
           VerticalAlignment VerticalAlignment, Thickness PageMargin, 
           bool PrintLandscape, bool ShrinkToFit, Action OnPrintComplete)
    {
        Print(elements.ToList(), Document, HorizontalAlignment, 
              VerticalAlignment, PageMargin, PrintLandscape, 
              ShrinkToFit, OnPrintComplete);
    }

    public static void Print<T>(this List<T> elements, 
           string Document, HorizontalAlignment HorizontalAlignment, 
           VerticalAlignment VerticalAlignment, Thickness PageMargin, 
           bool PrintLandscape, bool ShrinkToFit, Action OnPrintComplete)
    {
        PrintDocument printDocument = new PrintDocument();
        PageMargin = PageMargin == null ? new Thickness(10) : PageMargin;
        Document = (string.IsNullOrEmpty(Document)) ? "Print Document" : Document;
        int currentItemIndex = 0;
        printDocument.PrintPage += delegate(object sender, PrintPageEventArgs evt)
        {
            if (!typeof(FrameworkElement).IsAssignableFrom(
                         elements[currentItemIndex].GetType()))
            {
                throw new Exception("Element must be an " + 
                      "object inheriting from FrameworkElement");
            }

            FrameworkElement element = elements[currentItemIndex] as FrameworkElement;

            if (element.Parent == null || element.ActualWidth == double.NaN || 
                element.ActualHeight == double.NaN)
            {
                throw new Exception("Element must be rendered, " + 
                          "and must have a parent in order to print.");
            }
                
            TransformGroup transformGroup = new TransformGroup();

            //First move to middle of page...
            transformGroup.Children.Add(new TranslateTransform() { 
              X = (evt.PrintableArea.Width - element.ActualWidth) / 2, 
              Y = (evt.PrintableArea.Height - element.ActualHeight) / 2 });
            double scale = 1;
            if (PrintLandscape)
            {
                //Then, rotate around the center
                transformGroup.Children.Add(new RotateTransform() { Angle = 90, 
                   CenterX = evt.PrintableArea.Width / 2, 
                   CenterY = evt.PrintableArea.Height / 2 });

                if (ShrinkToFit)
                {
                    if ((element.ActualWidth + PageMargin.Left + 
                          PageMargin.Right) > evt.PrintableArea.Height)
                    {
                        scale = Math.Round(evt.PrintableArea.Height / 
                          (element.ActualWidth + PageMargin.Left + PageMargin.Right), 2);
                    }
                    if ((element.ActualHeight + PageMargin.Top + PageMargin.Bottom) > 
                                                evt.PrintableArea.Width)
                    {
                        double scale2 = Math.Round(evt.PrintableArea.Width / 
                          (element.ActualHeight + PageMargin.Top + PageMargin.Bottom), 2);
                        scale = (scale2 < scale) ? scale2 : scale;
                    }
                }
            }
            else if (ShrinkToFit)
            {
                //Scale down to fit the page + margin
                    
                if ((element.ActualWidth + PageMargin.Left + 
                        PageMargin.Right) > evt.PrintableArea.Width)
                {
                    scale = Math.Round(evt.PrintableArea.Width / 
                      (element.ActualWidth + PageMargin.Left + PageMargin.Right), 2);
                }
                if ((element.ActualHeight + PageMargin.Top + PageMargin.Bottom) > 
                             evt.PrintableArea.Height)
                {
                    double scale2 = Math.Round(evt.PrintableArea.Height / 
                      (element.ActualHeight + PageMargin.Top + PageMargin.Bottom), 2);
                    scale = (scale2 < scale) ? scale2 : scale;
                }
            }

            //Scale down to fit the page + margin
            if (scale != 1)
            {
                transformGroup.Children.Add(new ScaleTransform() { ScaleX = scale, 
                   ScaleY = scale, CenterX = evt.PrintableArea.Width / 2, 
                   CenterY = evt.PrintableArea.Height / 2 });
            }

            if (VerticalAlignment == VerticalAlignment.Top)
            {
                //Now move to Top
                if (PrintLandscape)
                {
                    transformGroup.Children.Add(new TranslateTransform() { 
                      X = 0, Y = PageMargin.Top - (evt.PrintableArea.Height - 
                      (element.ActualWidth * scale)) / 2 });
                }
                else
                {
                    transformGroup.Children.Add(new TranslateTransform() { X = 0, 
                      Y = PageMargin.Top - (evt.PrintableArea.Height - 
                      (element.ActualHeight * scale)) / 2 });
                }
            }
            else if (VerticalAlignment == VerticalAlignment.Bottom)
            {
                //Now move to Bottom
                if (PrintLandscape)
                {
                    transformGroup.Children.Add(new TranslateTransform() { X = 0, 
                      Y = ((evt.PrintableArea.Height - 
                      (element.ActualWidth * scale)) / 2) - PageMargin.Bottom });
                }
                else
                {
                    transformGroup.Children.Add(new TranslateTransform() { X = 0, 
                      Y = ((evt.PrintableArea.Height - 
                      (element.ActualHeight * scale)) / 2) - PageMargin.Bottom });
                }
            }

            if (HorizontalAlignment == HorizontalAlignment.Left)
            {
                //Now move to Left
                if (PrintLandscape)
                {
                    transformGroup.Children.Add(new TranslateTransform() { 
                      X = PageMargin.Left - (evt.PrintableArea.Width - 
                      (element.ActualHeight * scale)) / 2, Y = 0 });
                }
                else
                {
                    transformGroup.Children.Add(new TranslateTransform() { 
                      X = PageMargin.Left - (evt.PrintableArea.Width - 
                      (element.ActualWidth * scale)) / 2, Y = 0 });
                }
            }
            else if (HorizontalAlignment == HorizontalAlignment.Right)
            {
                //Now move to Right
                if (PrintLandscape)
                {
                    transformGroup.Children.Add(new TranslateTransform() { 
                      X = ((evt.PrintableArea.Width - 
                      (element.ActualHeight * scale)) / 2) - PageMargin.Right, Y = 0 });
                }
                else
                {
                    transformGroup.Children.Add(new TranslateTransform() { 
                      X = ((evt.PrintableArea.Width - 
                      (element.ActualWidth * scale)) / 2) - PageMargin.Right, Y = 0 });
                }
            }

            evt.PageVisual = element;
            evt.PageVisual.RenderTransform = transformGroup;
                
            //Increment to next item,
            currentItemIndex++;
                
            //If the currentItemIndex is less than the number of elements, keep printing
            evt.HasMorePages = currentItemIndex < elements.Count;
        };

        printDocument.EndPrint += delegate(object sender, EndPrintEventArgs evt)
        {
            foreach (var item in elements)
            {
                FrameworkElement element = item as FrameworkElement;
                //Reset everything...
                TransformGroup transformGroup = new TransformGroup();
                transformGroup.Children.Add(
                  new ScaleTransform() { ScaleX = 1, ScaleY = 1 });
                transformGroup.Children.Add(new RotateTransform() { Angle = 0 });
                transformGroup.Children.Add(
                  new TranslateTransform() { X = 0, Y = 0 });
                element.RenderTransform = transformGroup;
            }

            //Callback to complete
            if (OnPrintComplete != null)
            {
                OnPrintComplete();
            }
        };

        printDocument.Print(Document);
    }
}
© . All rights reserved.