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

如何生成 WPF 图像并在网页中渲染它

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (7投票s)

2008年10月9日

CPOL

2分钟阅读

viewsIcon

75517

downloadIcon

1111

WPF 图像生成

引言

以下代码示例将向您展示如何使用 WPF 生成基本图像并在网页中渲染它。它将涵盖在此解决方案开发过程中遇到的主要问题。

背景

在某个项目工作中,我们需要生成包含实时数据的图像,以便在向客户群发送电子邮件时,从数据库中数据的快照渲染图像。该图像嵌入到电子邮件中,即使在一天后打开它,它仍然是实时、相关且特定于阅读者的。此解决方案的优点在于,您可以根据电子邮件打开的时间来编码图像生成,因此如果您的客户一周后打开它,您可以提供当前的优惠或特价。

使用代码

下面是一个基本的 aspx 网页,将托管 WPF 应用程序的二进制输出。您需要创建一个 STA 线程,以便与 WPF 项目进行通信。

public partial class _Default : System.Web.UI.Page
{
 private Byte[] _bufferMain;
 public Byte[] BufferMain
{
 get { return _bufferMain; }
 set { _bufferMain = value; }
}
public void Page_Load(object sender, EventArgs e)
{
 if (!IsPostBack)
 {
  DisplayDialog();
  Response.Clear();
  Response.ContentType = @"image/jpeg";
  Response.BufferOutput = true;
  Response.BinaryWrite(BufferMain);
  Response.Flush();
 }
}
[STAThread]
public void DisplayDialog()
{
 try
 {
    Thread worker = new Thread(new ThreadStart(DisplayDialogInternal));
  worker.SetApartmentState(ApartmentState.STA);
  worker.Name = "DisplayDialog";
  worker.Start();
  worker.Join();
 }
catch (Exception exp)
{
 Response.Write(exp.ToString());
}
}
public void DisplayDialogInternal()
{
 try
 {
  string fileName = @"C:\TestWeb.jpg";
  RenderBookingGridImage image = new RenderBookingGridImage();
  //en-code image to transport 
  BufferMain = image.RenderImage("TESTVALUE");
  Stream st = new MemoryStream(BufferMain);
  //de-code view
  JpegBitmapDecoder jpeg = new JpegBitmapDecoder(st, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
  using (Stream stm = File.Create(fileName))
  {
   JpegBitmapEncoder encoder = new JpegBitmapEncoder();
   encoder.Frames.Add(jpeg.Frames[0]);
   encoder.Save(stm);
  }
 }
 catch (Exception exp)
 {
  ///ExceptionUtil.DisplayError(exp);
 }
}
}
        

图像将通过 WPF 类生成,但在开发过程中遇到了一些非常重要的问题。在使用画布控件时,必须先测量安排画布,然后才能使用它来渲染图像。如果未这样做,您可能会得到一个空白画布。

下面是需要在 WPF 项目中渲染图像的代码。

public Byte[] RenderImage(string passedInVar)

{

//ADDING CONTROLS VIA CODE >>>>>

TextBlock tb = new TextBlock();

tb.Width = (double)400;

tb.Height = (double)200;

tb.TextAlignment = TextAlignment.Center;

tb.Text = "Text added via code...";

tb.FontSize = (Double)30;

tb.Foreground = Brushes.Blue;

InnerCanvas.Children.Add(tb);

//When adding children you might need to update the layout for controls...

InnerCanvas.UpdateLayout();

 

//NB : You have to force the canvas to reload for it to

//re-render correctly when calling in from another source

Canvas canvas = (Canvas)this.FindName("InnerCanvas");

canvas.Measure(new Size((int)canvas.Width, (int)canvas.Height));

canvas.Arrange(new Rect(new Size((int)canvas.Width, (int)canvas.Height)));

int Height = ((int)(InnerCanvas.ActualHeight));

int Width = ((int)(InnerCanvas.ActualWidth));



RenderTargetBitmap rtb = new RenderTargetBitmap(Width, Height, 96, 96, PixelFormats.Pbgra32);

rtb.Render(InnerCanvas);

JpegBitmapEncoder jpg = new JpegBitmapEncoder();

jpg.Frames.Add(BitmapFrame.Create(rtb));

Byte[] tmpArry;

using (MemoryStream ms = new MemoryStream())

{

jpg.Save(ms);

tmpArry = ms.ToArray();

}

///NB : You need to clean up the thread manually

///as they will still reside in memory if they are not flagged 

///for termination....Thread count will go through the roof on

///the server if you dont invoke the following calls. 

if (jpg.Dispatcher.Thread.IsAlive)

{

jpg.Dispatcher.InvokeShutdown();

}

if (rtb.Dispatcher.Thread.IsAlive)

{

rtb.Dispatcher.InvokeShutdown();

}

 

jpg = null;

rtb = null;

return tmpArry;

}

关注点

由于某种原因,WPF 中的线程处理对我们的服务器造成了真正的混乱,因为如果您以非传统方式调用 WPF 应用程序,它会留下在服务器上运行的线程。因此,在打开某些控件时请务必小心。

确保在使用任何使用调度器的对象时调用以下内容。

.Dispatcher.InvokeShutdown()

© . All rights reserved.