简体   繁体   中英

RenderTargetBitmap does not render included images

I want to render an XAML control to a PNG file with the RenderTargetBitmap functionality. This works great so far.

But if the XAML contains any Images these images are not rendered in the PNG file. This is my XAML:

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Name="LayoutRoot"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  d:DesignHeight="336"
  d:DesignWidth="336"
  Background="DarkGray">

<TextBlock Text="This is my Text"
           FontSize="35"
           HorizontalAlignment="Center" />
<Image Source="http://www.myurl.com/image.png"
       Height="200" /></Grid>

And this is my code:

private BitmapSource RenderToBitmap(FrameworkElement target)
    {
        Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
        RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)target.ActualWidth,
          (int)target.ActualHeight,
          96, 96, PixelFormats.Pbgra32);

        DrawingVisual visual = new DrawingVisual();
        using (DrawingContext context = visual.RenderOpen())
        {
            VisualBrush brush = new VisualBrush(target);
            context.DrawRectangle(brush, null, new Rect(new Point(), bounds.Size));
        }
        renderBitmap.Render(visual);
        return renderBitmap;
    }

Also tried with referenced images like "/Assets/image.png" and as Resource. Every time the image is missing in the rendered PNG File.

Firstly what happens is that you render your target, but your ImageControl loads BitmapSource asynchronously and isn't ready yet at that moment. If you would wait until ImageControl finishes loading its source you will get what you expect. Fun thing is, ImageControl doesn't inform you when it has finished loading.

So, short answer would be: If you want to render existing and already loaded visual to image, just wait until nested image controls are loaded.

Like this:

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        img.LayoutUpdated += img_LayoutUpdated;
    }

    void img_LayoutUpdated(object sender, EventArgs e)
    {
        img.LayoutUpdated -= img_LayoutUpdated;
        var rt = RenderToBitmap(img);
        Save(rt);
    }

As far as I know there is no easy way to force ImageControl to load its source. And when ImageControl throws "Loaded" event it doesn't mean that the image is loaded. If you would have you ImageControl.Source created as BitmapImage, you could have done something like this:

            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.DecodePixelHeight = 100;
            bi.CacheOption = BitmapCacheOption.OnLoad;
            bi.UriSource = new Uri(@"http://www.myurl.com/image.png");
            bi.EndInit();
            bi.DownloadCompleted += bi_DownloadCompleted; //will be ready to be saved to image when event will fire
            img.Source = bi;

or download your image by yourself like this

    WebClient wc = new WebClient();
    using (MemoryStream stream = new MemoryStream(wc.DownloadData(@"http://www.google.ru/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png")))
    {
        BitmapImage myBitmapImage = new BitmapImage();
        myBitmapImage.BeginInit();
        myBitmapImage.StreamSource = stream;
        myBitmapImage.DecodePixelWidth = 200;
        myBitmapImage.EndInit();
        img.Source = myBitmapImage;
        // ready to be saved to image
    }

But in my opinion it doesn't suit you well

If your purpose is to render visual in memory, then you should take a look at this question: Drawing a WPF UserControl with DataBinding to an Image

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM