【问题标题】:Using WPF on a DLL to create Images Dynamically (instead of GDI+)在 DLL 上使用 WPF 动态创建图像(而不是 GDI+)
【发布时间】:2010-10-23 13:03:02
【问题描述】:

我需要动态生成图像,在阅读教程here 后,我意识到我可以使用 WPF 中的所有控件和布局来生成我的渲染,然后将其保存为 JPG。 这个想法是使用它来代替非常原始的 GDI+。

问题是,如何创建一个常规的 dll 文件,该文件将以编程方式生成 WPF 画布,以便我可以向其中添加控件,然后将其输出到图像文件。请记住,它会被 ASP.NET 应用程序使用。

有人有什么想法吗?

【问题讨论】:

标签: c# asp.net wpf image gdi+


【解决方案1】:

这个例子有一个好的开始,但我发现它有很多不需要的垃圾。最主要的是你不需要有一个单独的 WPF 项目。

这是怎么做的:

  • 在您的 Web 项目中引用 PresentationCore、PresentationFramework 和 WindowsBase。
  • 在 STA 线程中以编程方式创建 Canvas 和其他 WPF 对象。
  • 对它们调用一些特殊方法,以确保它们在 WPF 应用程序的上下文之外进行更新。
  • 使用 RenderTargetBitmap 将它们渲染为图像。
  • 关闭线程的调度程序。
  • 设置mime类型,用ASP.NET输出图片。

为了提高效率,您可以重复使用同一个线程,而不是为每个图像创建一个新线程。在这种情况下,您只需要在关闭线程时清理调度程序。

这是我拥有的完整工作代码:

using System;
using System.Web;
using System.Threading;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Windows.Documents;

public partial class _Default : System.Web.UI.Page
{
    private byte[] imageBuffer;

    public void Page_Load(object sender, EventArgs e)
    {
        this.RenderImage();

        Response.Clear();
        Response.ContentType = @"image/png";
        Response.BufferOutput = true;
        Response.BinaryWrite(this.imageBuffer);
        Response.Flush();
    }

    public void RenderImage()
    {
        Thread worker = new Thread(new ThreadStart(this.RenderImageWorker));
        worker.SetApartmentState(ApartmentState.STA);
        worker.Name = "RenderImageWorker";
        worker.Start();
        worker.Join();
    }

    public void RenderImageWorker()
    {
        Canvas imageCanvas = new Canvas { Width = 600, Height = 200, Background = Brushes.Azure };

        TextBlock tb = new TextBlock();
        tb.Width = (double)400;
        //tb.Height = (double)200;
        tb.TextAlignment = TextAlignment.Center;
        tb.Inlines.Add(new Run("This is "));
        tb.Inlines.Add(new Bold(new Run("bold")));
        tb.Inlines.Add(new Run(" text."));
        tb.FontSize = 30;
        tb.Foreground = Brushes.Blue;

        imageCanvas.Children.Add(tb);

        // Update layout
        imageCanvas.Measure(new Size(imageCanvas.Width, imageCanvas.Height));
        imageCanvas.Arrange(new Rect(new Size(imageCanvas.Width, imageCanvas.Height)));

        RenderTargetBitmap bitmapRenderer = new RenderTargetBitmap((int)imageCanvas.ActualWidth, (int)imageCanvas.ActualHeight, 96, 96, PixelFormats.Pbgra32);
        bitmapRenderer.Render(imageCanvas);

        PngBitmapEncoder png = new PngBitmapEncoder();
        png.Frames.Add(BitmapFrame.Create(bitmapRenderer));

        using (MemoryStream memoryStream = new MemoryStream())
        {
            png.Save(memoryStream);
            this.imageBuffer = memoryStream.ToArray();
        }

        if (bitmapRenderer.Dispatcher.Thread.IsAlive)
        {
            bitmapRenderer.Dispatcher.InvokeShutdown();
        }
    }
}

【讨论】:

  • 会更好 if(!worker.Join(timeoutelapsed)) { worker.Abort(); } 以防万一出现问题?
  • 在线程上调用 Abort() 通常不是一个好的编程习惯:它会导致资源泄漏并且通常会使程序处于不良状态。编写在线程中止时表现良好的代码是相当困难的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-21
  • 2014-05-13
  • 2011-04-29
  • 2012-07-30
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多