C# 在 WPF 中在运行时渲染图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/589173/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 09:22:25  来源:igfitidea点击:

Rendering an image at runtime in WPF

c#wpfimage

提问by user70192

I have posted several questions related to this problem I am having and I am starting to believe this cannot be done. Here is the back story.

我已经发布了几个与我遇到的这个问题相关的问题,我开始相信这是不可能的。这是背景故事。

I have an ASP.NET application from which I want to generate a .png image. This .png image needs to be constructed from either XAML or a WPF Visual Tree. Because of this, I must generate the .png image in an STA thread. Everything works fine until my XAML/WPF Visual Tree includes an Image (as in a System.Windows.Controls.Image). My .png file gets generated correctly except the Image element does not show the referenced picture. The referenced picture is located at a remote URL. No errors or exceptions are thrown.

我有一个 ASP.NET 应用程序,我想从中生成一个 .png 图像。此 .png 图像需要从 XAML 或 WPF 可视化树构造。因此,我必须在 STA 线程中生成 .png 图像。一切正常,直到我的 XAML/WPF 可视化树包含一个图像(如在 System.Windows.Controls.Image 中)。我的 .png 文件得到正确生成,除了 Image 元素不显示引用的图片。引用的图片位于远程 URL。不会抛出任何错误或异常。

How do I create a .png image from some XAML/WPF Visual Tree that includes a System.Windows.Controls.Image element? The resulting .png must include the picture referenced in the Image element. I have tried the following code in a variety of ways:

如何从包含 System.Windows.Controls.Image 元素的某些 XAML/WPF 可视化树创建 .png 图像?生成的 .png 必须包含 Image 元素中引用的图片。我以多种方式尝试了以下代码:

string address = "http://imgtops.sourceforge.net/bakeoff/o-png24.png";

WebClient webClient = new WebClient();
byte[] imageContent = webClient.DownloadData(address);

Image image = new Image();
using (MemoryStream memoryStream = new MemoryStream(imageContent))
{
  BitmapImage imageSource = new BitmapImage();
  imageSource.BeginInit();
  imageSource.StreamSource = memoryStream;
  imageSource.EndInit();
  image.Source = imageSource;
}

// Set the size
image.Height = 200;
image.Width = 300;

// Position the Image within a Canvas
image.SetValue(Canvas.TopProperty, 1.0);
image.SetValue(Canvas.LeftProperty, 1.0);

Canvas canvas = new Canvas();
canvas.Height = 200;
canvas.Width = 300;
canvas.Background = new SolidColorBrush(Colors.Purple);
canvas.Children.Add(image);

// Create the area
Size availableSize = new Size(300, 200);
frameworkElement.Measure(availableSize);
frameworkElement.Arrange(new Rect(availableSize));

// Convert the WPF representation to a PNG file            
BitmapSource bitmap = RenderToBitmap(frameworkElement);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));

// Generate the .png
FileStream fileStream = new FileStream(filename, FileMode.Create);
encoder.Save(fileStream);


public BitmapSource RenderToBitmap(FrameworkElement target)
{
  int actualWidth = 300;
  int actualHeight = 200;

  Rect boundary = VisualTreeHelper.GetDescendantBounds(target);
  RenderTargetBitmap renderBitmap = new RenderTargetBitmap(actualWidth, actualHeight, 96, 96, PixelFormats.Pbgra32);

  DrawingVisual drawingVisual = new DrawingVisual();
  using (DrawingContext context = drawingVisual.RenderOpen())
  {
    VisualBrush visualBrush = new VisualBrush(target);
    context.DrawRectangle(visualBrush, null, new Rect(new Point(), boundary.Size));
  }

  renderBitmap.Render(drawingVisual);
  return renderBitmap;
}

Thank you for your help.

感谢您的帮助。

采纳答案by Simon Buchan

You are rendering the output bitmap correctly, it is just the input bitmap you are screwwing up :).

您正在正确渲染输出位图,它只是您正在搞砸的输入位图:)。

BitmapImage requires access to the StreamSource property until it fires the DownloadCompleted event, but the 'using' block Dispose()s of the MemoryStream before it has a chance! You could simply unwrap the MemoryStream from the using block and let the GC handle it (if you do, I would recommend setting the BitmapImage.CacheOption to BitmapCacheOption.None, so it uses the stream directly, rather than a copy), but I would use the UriSource property and wait for the DownloadComplete event before rendering.

BitmapImage 需要访问 StreamSource 属性,直到它触发 DownloadCompleted 事件,但Dispose()在它有机会之前需要访问MemoryStream的 'using' block !您可以简单地从 using 块中解开 MemoryStream 并让 GC 处理它(如果您这样做,我建议将 BitmapImage.CacheOption 设置为 BitmapCacheOption.None,因此它直接使用流,而不是副本),但我会使用 UriSource 属性并在呈现之前等待 DownloadComplete 事件。