通过截屏将 PowerPoint 幻灯片从 Silverlight 创建出来
本文介绍如何截取 Silverlight 页面的快照并将其转换为 PowerPoint 幻灯片。
引言
在本文中,我将介绍如何使用 Silverlight 应用程序通过截取 Silverlight 页面的各种快照来创建 Microsoft PowerPoint 文件中的幻灯片。当我想要从 Silverlight 项目创建 PowerPoint 幻灯片时,我在互联网上找不到解决方案,因此我受到启发写下这篇文章,尽管我确实找到了许多关于在 PowerPoint 中使用 Silverlight 的结果。
工作原理
解决方案包含三个项目
- PowerpointService(一个 WCF 服务项目)
- SilverlightToPowerpoint(一个 Silverlight 项目)
- SilverlightToPowerpoint.Web(一个托管 Silverlight 项目的 Web 应用程序)
Silverlight 项目通过发送页面快照(作为图像的字节数组)和标题来调用 WCF 服务。作为回应,WCF 会创建 PowerPoint 幻灯片并将它们作为字节数组返回。Silverlight 应用程序会提示用户显示“保存”对话框,以决定将文件保存在何处,并将字节数组保存为 Microsoft PowerPoint 文件。
WCF 服务是必需的,因为我们想要操作 Silverlight 不支持的文件,因为它在客户端浏览器中运行,并且不允许访问文件。(我听到一些人说“嘿!那里有 Isolated Storage”,但为了实现此功能,那并不是理想的解决方案。)
第三方库
使用第三方库来截取 Silverlight 的快照并创建 Microsoft PowerPoint 幻灯片。
为了截取 Silverlight 的快照,Silverlight 项目中使用了以下库:
- ImageTools.dll
- ImageTools.IO.Png.dll
- ICSharpCode.SharpZipLib.Silverlight.dll
这些是免费库,可以从 CodePlex 下载。
用于创建幻灯片,WCF 服务项目中使用了DocumentFormat.OpenXml.dll。该库可以从 OpenXmlDeveloper 下载。
注意:您实际上不需要下载这些库,因为它们已包含在附加的解决方案中。链接仅供您进一步参考。
屏幕截图
在 Silverlight 项目中,我使用了三张三辆汽车的图片,但这些图片被存储在 Web 项目的ClientBin->Images文件夹下,以减小 Silverlight 项目的大小。
运行 Silverlight 应用程序的第一个页面是:
选择汽车后
添加到幻灯片集合后(默认情况下,显示汽车名称作为标题,但这可以更改)
添加多辆汽车(在代码中,我将其最大设置为 20,但您可以更改此设置)
将幻灯片保存到 PowerPoint 文件
这是确认信息
...以及 PowerPoint 文件
代码解析
Silverlight 项目
Silverlight 项目包含一个用户控件文件MainPage.xaml,其中编写了所有 UI 设计。尽管.xaml对于任何 Silverlight 开发人员来说都是不言自明的且易于理解的,但下面的标记代码需要特殊说明。
<stackpanel name="spSlide" grid.row="1">
<img name="imgCar" stretch="UniformToFill" />
</stackpanel>
这个 StackPanel
非常重要,因为在代码中,我将 StackPanel
及其子控件转换为图像并将其传递给 WCF 服务。这里我只使用了图像,但如果您想添加更多控件,必须将它们添加到 StackPanel
下。当然,在代码中我使用了 StackPanel
,但您也可以使用其他任何容器控件。
这是单击添加到幻灯片时执行的代码。
if (cbCars.SelectedValue == null) return; //No item is chosen
// comment this line if you want allow unlimited
// number of slides. or update the number
if (_slides.Count > 20)
throw new Exception("Maximum slides (20) added already!");
//convert the stackpanel and its controls as image and store it in memory
WriteableBitmap image = new WriteableBitmap(spSlide, new ScaleTransform());
try
{
PptSlide slide = new PptSlide();
byte[] binaryData = GetImageBufferAsPng(image);
BitmapImage bmpImage = new BitmapImage();
Stream stream = new MemoryStream(binaryData, 0, binaryData.Length);
bmpImage.SetSource(stream);
slide.Image = bmpImage;
slide.ImageBinary = binaryData;
slide.ImageTitle = string.Format("{0}_{1}",
((ComboBoxItem)cbCars.SelectedItem).Content, _slides.Count + 1);
_slides.Add(slide);
tabSlides.Header = string.Format("Slides ({0})", _slides.Count);
}
catch (Exception ex)
{
ShowErrorAlert(string.Format(
"Error while adding to slide collection:\n{0}", ex.Message));
}
此代码使用 Image 库将 StackPanel
及其控件转换为 PNG 图像,并将其添加到内部的可观察集合中,该集合是 DataGrid
的源。
这是单击另存为 PowerPoint 时执行的代码。
if (_slides.Count == 0) return;
try
{
var service = new PowerpointClient();
var slides = new List<powerpointimage>();
foreach (var item in _slides)
{
var slide = new PowerpointImage();
slide.ByteArray = item.ImageBinary;
slide.Title = item.ImageTitle;
slides.Add(slide);
}
SaveFileDialog = new SaveFileDialog();
SaveFileDialog.DefaultExt = "Powerpoint 2007 (*.pptx)|*.pptx";
SaveFileDialog.Filter = "Powerpoint 2007 (*.pptx)|*.pptx";
SaveFileDialog.FilterIndex = 1;
if (SaveFileDialog.ShowDialog() == true)
{
service.PowerpointAsByteCompleted +=
service_PowerpointAsByteCompleted;
service.PowerpointAsByteAsync(slides);
tbStatus.Text = "Saving file...";
}
}
catch (Exception ex)
{
ShowErrorAlert(string.Format(
"Error while getting powerpoint file:\n{0}", ex.Message));
}
...WCF 服务调用完成后
void service_PowerpointAsByteCompleted(object sender,
PowerpointAsByteCompletedEventArgs e)
{
if (e.Error != null)
{
ShowErrorAlert(string.Format(
"Error while getting powerpoint file:\n{0}",
e.Error.Message));
return;
}
if (!string.IsNullOrWhiteSpace(e.Result.ErrorMessage))
{
ShowErrorAlert(e.Result.ErrorMessage);
return;
}
byte[] pptByte = e.Result.PptByte;
if (pptByte == null)
{
ShowErrorAlert("Empty powerpoint file is returned from server.");
return;
}
using (System.IO.Stream stream = SaveFileDialog.OpenFile())
{
stream.Write(pptByte, 0, pptByte.Length);
tbStatus.Text = "File saved: " + SaveFileDialog.SafeFileName;
}
}
WCF 项目
该项目仅提供一个方法 PowerpointAsByte
。以及服务接口 IPowerpoint
。
[ServiceContract]
public interface IPowerpoint
{
/// <summary>
/// Converts the images from images collection to powerpoint
/// slides and return the powerpoint file as byte array
/// </summary>
/// <param name="slides" />
/// <returns />
[OperationContract]
PowerpointAsByteResult PowerpointAsByte(IList<powerpointimage> images);
}
服务类 Powerpoint
public class Powerpoint : IPowerpoint
{
/// <summary>
/// Method accepts collection of images and convert them as powerpoint slides.
/// </summary>
/// <param name="slides" />
/// <returns>byte array of powerpoint file</returns>
public PowerpointAsByteResult PowerpointAsByte(IList<powerpointimage> slides)
{
PowerpointAsByteResult result = new PowerpointAsByteResult();
//Don't throw any exception across wire.
try
{
result.PptByte = (new PowerpointHelper()).GetPowerpointAsByte(slides);
}
catch (Exception ex)
{
result.ErrorMessage = string.Format(
"Error while converting images to powerpoint:\n{0}",ex.Message);
}
return result;
}
}
WCF 服务的类图
在开始使用代码之前需要记住的一些事项
- 您需要 Visual Studio 2010 才能打开此代码。
- 运行项目时,在进行 WCF 调用时可能会收到错误消息。这是因为 WCF 服务的地址在您的开发服务器上可能会发生变化。因此,请先运行 WCF 服务并获取其地址,然后在 Silverlight 项目的ServiceReferences.ClientConfig文件中进行更新。
- 如果您处理的是大尺寸图像,则需要增加web.config文件中的上传大小限制。请查看此链接:http://forums.silverlight.net/forums/p/65327/433543.aspx。
结论
我相信本文将至少帮助到一些需要此功能的人。请测试此代码并就任何问题/建议发表您的评论。