Silverlight 中的简化 UI 打印





0/5 (0投票)
一个简化的扩展方法,允许您使用标准选项(如横向打印、缩小以适应、页面居中和打印边距)打印任何元素。
引言
在 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
派生的,则应抛出异常。我们还需要确保正在打印的每个项目都已实际呈现在控件上。这样做的原因是我们需要使用元素的 ActualWidth
和 ActualHeight
- 这意味着该元素必须出现并具有父对象。
可以通过将控件呈现到其 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);
}
}