【Win10应用开发】自定义打印选项

原文:【Win10应用开发】自定义打印选项

老周在前一篇烂文中已经给大伙伴们演示了如何打印UI元素,今天的烂文就向各位介绍一下,如何向打印对话框添加自定义选项。如果只是讲如何实现,会比较抽象,也比较枯燥,而且相当无聊,更是说不清楚,毕竟这打印API用起来要比其他API稍稍复杂了一点。所以老周就做了一个打印图片的垃圾应用,在打印对话框中,你可以选择设置要打印图片的不透明度、旋转角度(0度,90度,180度,270度)。

OK,为环保事业做贡献,老周今天也节约一点口水。下面咱们开始干活。

1、先来设计一些UI,该UI简单大方朴素美丽极致,其XAML代码如下:

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Button Content="打印" Click="OnClick"/>

            <Image Grid.Row="1" Width="300" Height="200" VerticalAlignment="Center" HorizontalAlignment="Center" Name="img"  Source="Assets/03.jpg"/>
        </Grid>

按钮的作用就是打开打印对话框,Image显示要打印的图片。不过呢,为了打印过程中设置选项(就是老周上面讲的什么不透明度等)时不会干扰界面上的Image,我打算在代码中再声明一个Image对象,专门用来打印。代码是这样的:

        Image copyImage = null;

        public MainPage()
        {
            this.InitializeComponent();
            copyImage = new Image();
            copyImage.Source = img.Source;
            copyImage.Stretch = Stretch.Uniform;
            rotateTrsf = new RotateTransform() { Angle = 0d };
            copyImage.RenderTransformOrigin = new Point(0.5, 0.5);
            this.copyImage.RenderTransform = rotateTrsf;
        }

大家会看到我弄了个RotateTransform对象,干啥用的呢?你忘了吗,我刚说了要在打印对话框中选择图片的旋转角度的,对了,就是用来让copyImage进行旋转的。

 

2、接下来实现打印,基本过程我在前一篇烂文中说过,获取PrintManager实例,处理PrintTaskRequested事件,当有新的打印任务请求时,会发生该事件。看:

        private void PrintMgr_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
        {
            PrintTask task = null;
            task = args.Request.CreatePrintTask("打印图像", async printSrcrqtArgs =>
            {
                task.Completed += async (ps, pe) =>
                {
                    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
                            {
                                MessageDialog msgdlg = new MessageDialog("");
                                if (pe.Completion == PrintTaskCompletion.Canceled)
                                {
                                    msgdlg.Content = "打印被取消。";
                                }
                                else if (pe.Completion == PrintTaskCompletion.Abandoned)
                                {
                                    msgdlg.Content = "已放弃打印。";
                                }
                                else if (pe.Completion == PrintTaskCompletion.Submitted)
                                {
                                    msgdlg.Content = "已提交打印。";
                                }
                                await msgdlg.ShowAsync();
                            });

                };

                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                  {
                      printSrcrqtArgs.SetSource(_printdoc?.DocumentSource);
                  });
            });

            task.Options.Orientation = PrintOrientation.Landscape;
            // 创建自定义选项
            PrintTaskOptionDetails optionDetails = PrintTaskOptionDetails.GetFromPrintTaskOptions(task.Options);
            // 清除所有选项
            optionDetails.DisplayedOptions.Clear();
            // 添加标准项:页面方向
            optionDetails.DisplayedOptions.Add(StandardPrintTaskOptions.Orientation);
            // 添加自定义选项
            // 不透明度
            PrintCustomItemListOptionDetails list = optionDetails.CreateItemListOption(OPTION_OPACITY, "不透明度");
            list.AddItem(OPACITY_50PC, "50 %");
            list.AddItem(OPACITY_75PC, "75 %");
            list.AddItem(OPACITY_100PC, "100 %");
            optionDetails.DisplayedOptions.Add(OPTION_OPACITY);
            // 旋转角度
            list = optionDetails.CreateItemListOption(OPTION_ANGLE, "旋转角度");
            list.AddItem(ANGLE_0, "0 度");
            list.AddItem(ANGLE_90, "90 度");
            list.AddItem(ANGLE_180, "180 度");
            list.AddItem(ANGLE_270, "270 度");
            optionDetails.DisplayedOptions.Add(OPTION_ANGLE);
            // 处理选项更改事件
            optionDetails.OptionChanged += OptionDetails_OptionChanged;
        }

为了节约代码行数,同时为了展现C#语言的风采,我这里套用了几层Lambda表达式。

请大伙注意后半段代码,前半段代码你应该在前一篇烂文中见过,但后半段代码是小鲜肉,在本文中才出现,它的作用是向打印对话框中添加标准的,以及自定义的选项。

何为标准选项?就是一般打印参数(通用级别),这些选项的ID名称都由Windows.Graphics.Printing.StandardPrintTaskOptions类的静态属性所公开。

自定义选项就咱们自己定的选项,本例中,图片透明度、旋转角度就是自定义选项。

a、PrintTaskOptionDetails.GetFromPrintTaskOptions方法获取一个PrintTaskOptionDetails对象,可用它来操作选项集合。

b、PrintTaskOptionDetails对象有个DisplayedOptions属性,它是一个字符串列表,表示要在打印对话框上显示哪些选项,就把这些选项的ID放进去。没有在这个列表中的项就只有当用户点击“更多设置”时才会显示。

c、这行代码是把标准选项中的页面方向选项加到显示列表中,因为刚才用Clear方法把DisplayedOptions清空了,所以要加入一下。

            optionDetails.DisplayedOptions.Add(StandardPrintTaskOptions.Orientation);

d、调用CreateItemListOption方法可以创建一个新的自定义选项,方法有两个字符串类型的参数,第一个参数是这组选项的ID,第二个参数是显示的内容,即要显示在打印对话框上的选项标题,ID是不显示出来的。
e、用AddItem方法添加子选项时也是如此,一个ID值和一个显示值。

注意:CreateItemListOption方法创建的是整个选项组,比如“旋转方向”,而AddItem方法才是向选项组添加列表项,比如90度、180度等。

f、选项列表是添加了,但为了在打印对话框中能够实时看到预览效果,应该处理Windows.Graphics.Printing.OptionDetails.PrintTaskOptionDetails对象的OptionChanged事件,当这些选项集合中的任意一组选项发生改变时就会触发事件。

        private async void OptionDetails_OptionChanged(PrintTaskOptionDetails sender, PrintTaskOptionChangedEventArgs args)
        {
            if (args.OptionId == null) return;
            string optID = args.OptionId as string;await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                _printdoc.InvalidatePreview();
            });
        }

事件参数中的OptionId属性就是返回被更改的选项组的ID。注意是某个组选项的ID,不是单个子项的ID。
调用PrintDocument的InvalidatePreview方法是让PrintDocument中的UI元素重新呈现,和刷新差不多。

==================================================

和上一篇烂文中的一样,需要处理PrintDocument对象的几个事件,以处理打印和预览中相关的操作。

        private void _printdoc_Paginate(object sender, PaginateEventArgs e)
        {
            // 根据打印对话框选项的变化来修改可视化对象
            PrintTaskOptionDetails optdetails = PrintTaskOptionDetails.GetFromPrintTaskOptions(e.PrintTaskOptions);
            // 不透明度
            IPrintOptionDetails opaOpt = optdetails.Options[OPTION_OPACITY];
            string opacity = opaOpt.Value as string;
            switch (opacity)
            {
                case OPACITY_50PC:
                    copyImage.Opacity = 0.5d;
                    break;
                case OPACITY_75PC:
                    copyImage.Opacity = 0.75d;
                    break;
                case OPACITY_100PC:
                    copyImage.Opacity = 1d;
                    break;
                default:
                    copyImage.Opacity = 1d;
                    break;
            }
            // 旋转角度
            IPrintOptionDetails angleOpt = optdetails.Options[OPTION_ANGLE];
            string angle = angleOpt.Value as string;
            switch (angle)
            {
                case ANGLE_0:
                    rotateTrsf.Angle = 0d;
                    break;
                case ANGLE_90:
                    rotateTrsf.Angle = 90d;
                    break;
                case ANGLE_180:
                    rotateTrsf.Angle = 180d;
                    break;
                case ANGLE_270:
                    rotateTrsf.Angle = 270d;
                    break;
                default:
                    rotateTrsf.Angle = 0d;
                    break;
            }

            // 设置预览页面数
            _printdoc.SetPreviewPageCount(1, PreviewPageCountType.Final);
        }

        private void _printdoc_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
        {
            // 呈现预览
            try
            {
                _printdoc.SetPreviewPage(e.PageNumber, copyImage);
            }
            catch
            {
                // 发生异常时忽略
            }
        }

        private void _printdoc_AddPages(object sender, AddPagesEventArgs e)
        {
            // 添加要打印的页面
            _printdoc.AddPage(copyImage);
            // 报告添加完成
            _printdoc.AddPagesComplete();
        }

 在GetPreviewPage事件处理中把代码放在try...catch中,是防止发生异常。因为标准打印选项的更改本身就会引发重绘行为,但我们这里是把标准项和自定义项混在一起用,有发生异常的可能性,所以放到try中安全一些。

如果不把代码放在try块中,其实还有一种方法:就是在刚才的OptionChanged事件的处理过程中,只判断当OptionId是我们自定义的选项组时才调用PrintDocument的InvalidatePreview方法,如果是标准选项就会忽略,这样就不会发生异常。即

        private async void OptionDetails_OptionChanged(PrintTaskOptionDetails sender, PrintTaskOptionChangedEventArgs args)
        {
            if (args.OptionId == null) return;
            string optID = args.OptionId as string;
            if (optID == OPTION_OPACITY || optID == OPTION_ANGLE)
            {
                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                  {
                       _printdoc.InvalidatePreview();
                  });
            }
        }

=====================================================

不知不觉中,示例已经完成了,现在来运行一下。

运行后如下图:

点打印按钮,打开打印对话框。在打印对话框中,你可以进行参数设置。

 

最后看看打印结果。

 

好了,今天咱们就聊到这儿,下次有空咱们继续吹牛。

示例下载地址:http://files.cnblogs.com/files/tcjiaan/customPrintOptionSample.zip

 

时间: 2024-10-26 06:17:06

【Win10应用开发】自定义打印选项的相关文章

【Win10应用开发】协议-下篇:自定义多个协议

原文:[Win10应用开发]协议-下篇:自定义多个协议 前面介绍了如何为应用程序自定义协议,于是有朋友会问,我希望为我的应用注册多个协议,不同的协议处理不同的事情,能吗?答案是能的. 方法主要在配置清单文件上,这里我给出一个例子,示例应用将注册两个协议,分别为music:和video:. 在清单文件中找到Package/Applications/Application节点,在Application元素下增加Extensions节点,表示为应用程序声明的扩展,可以包含N个uap:Extension

【Win10应用开发】自定义桌面壁纸

原文:[Win10应用开发]自定义桌面壁纸 调用通用的API来设置桌面壁纸,是一件既简单又有趣的事情,结合XAML可以生成图像的特性,你甚至可以做一个应用,让用户用他所拍的照片做成一张自定义壁纸,然后作为桌面壁纸. 这个API是通用的,应用运行在任意Windows设备上都能使用,当然包括手机.在Windows.System.UserProfile命名空间下,有一个叫UserProfilePersonalizationSettings的类,它可以修改锁屏壁纸和桌面壁纸,调用后会返回bool值,如果

【Win10应用开发】自定义磁贴通知的排版

原文:[Win10应用开发]自定义磁贴通知的排版 前面老周用了两篇烂文,向大家介绍了Adaptive磁贴的模板使用.那些XML模板已经很强大了,不过,如果你觉得那些排版还不足以满足需求,不妨试试自己来定义磁贴的内容. 其实,Runtime App支持在后台任务中生成XAML呈现,只要你编写的后台任务类从XamlRenderingBackgroundTask(位于Windows.UI.Xaml.Media.Imaging命名空间)类派生即可.利用这一特性,我们可以在后台生成XAML布局,然后通过R

Win10 UWP开发系列:使用VS2015 Update2+ionic开发第一个Cordova App

原文:Win10 UWP开发系列:使用VS2015 Update2+ionic开发第一个Cordova App 安装VS2015 Update2的过程是非常曲折的.还好经过不懈的努力,终于折腾成功了. 如果开发Cordova项目的话,推荐大家用一下ionic这个框架,效果还不错.对于Cordova.PhoneGap.ionic.AngularJS这些框架或库的关系,我个人理解是这样,PhoneGap是一个商业项目,用来实现HTML5式的跨平台开发,后来Adobe公司将其中的核心代码开源,就是Co

“Win10 UAP 开发系列”之主题模式切换

原文:"Win10 UAP 开发系列"之主题模式切换 微软动作真是快,本来想写WP8.1RT系列,结果刚整理了一点就出Win10 UAP了.不过还好RT到Win10的差别还不算太大.前两天参加了Win10开发极客秀,虽然没获奖,不过在韦恩卑鄙的帮助下顺利将澎湃新闻WP8.1版升级到了Win10UAP,使用了一些新的特性,最近争取有时间慢慢把一些东西总结一下. 今天先说一下如何在Win10 UAP中切换主题模式. 切换日间.夜间主题模式这个功能我从WP8就实现了,并封装成了一个库,用在我

Win10 UWP开发系列:开发一个自定义控件——带数字徽章的AppBarButton

原文:Win10 UWP开发系列:开发一个自定义控件--带数字徽章的AppBarButton 最近有个项目有一个这样的需求,在文章浏览页底部有几个AppBarButton,其中有一个是评论按钮,需要在评论按钮上显示一个红色数字,类似微信的新消息提醒: 这种设计在iOS和Android平台都是很常见的,但UWP上并没有提供现成的控件.所以只能自己实现一个了.   做出来效果是这样的:   分析一下实现的思路.首先这还是一个AppBarButton,只是其中增加了一个数字徽章的显示,这个属性应该是可

Win10 UWP 开发系列:使用SplitView实现汉堡菜单及页面内导航

原文:Win10 UWP 开发系列:使用SplitView实现汉堡菜单及页面内导航 在Win10之前,WP平台的App主要有枢轴和全景两种导航模式,我个人更喜欢Pivot即枢轴模式,可以左右切换,非常方便.全景视图因为对设计要求比较高,自己总是做不出好的效果.对于一般的新闻阅读类App来说,Pivot更适合多个频道的展示,因为内容基本都是一样的. 到了Win10,微软模仿其他平台也推出了汉堡菜单,但并没有提供现成的控件,而是需要开发者通过一个名为SplitView的控件来实现.我个人并不觉得左上

Win10 UWP开发中的重复性静态UI绘制小技巧 2

原文:Win10 UWP开发中的重复性静态UI绘制小技巧 2 小技巧1 地址:http://www.cnblogs.com/ms-uap/p/4641419.html 介绍 我们在上一篇博文中展示了通过Shape.Stroke族属性实现静态重复性UI绘制,使得UWP界面的实现变得稍微灵活一些了. 但这一技巧还是有不少局限的,毕竟折腾StrokeDashArray属性看上去并不是那么直观和适用(还存在用扇形欺骗观众这样的"问题"啦). 这一篇博文我们将为大家介绍一种更为适用,同时也更为灵

Win10 UWP开发系列:实现Master/Detail布局

原文:Win10 UWP开发系列:实现Master/Detail布局 在开发XX新闻的过程中,UI部分使用了Master/Detail(大纲/细节)布局样式.Win10系统中的邮件App就是这种样式,左侧一个列表,右侧是详情页面.关于这种 样式的说明可参看MSDN文档:https://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/dn997765.aspx 样式如下: 在微软官方的Sample里,有这种样式的代码示例,下载地址:https: