Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

render WPF user control in memory, not on screen

Tags:

c#

wpf

I have a complex user control that shows an image and overlay certain tags above it with shadows and stuff. I want to render all those things in memory, then make an image of this map, and then use this image in the real user interface. This is how the thing looks at the end.

enter image description here

I do that because the interface started to move slowly with all those elements around and I'm trying to simplify it. Am I going the correct way??

The problem here is that I create the brainMap, feed it with data, then try to create the imagen and BAM! it cannot be done because the whole component is not rendered, the ActualWith is zero.

This is how I extract the image from the control (The method works perfect when the control is rendered in the screen)

    /// <summary>
    /// The controls need actual size, If they are not render an "UpdateLayout()" might be needed.
    /// </summary>
    /// <param name="control"></param>
    /// <param name="Container"></param>
    public static System.Windows.Controls.Image fromControlToImage(System.Windows.FrameworkElement control)
    {
        if (control.ActualWidth == 0)
            throw new Exception("The control has no size, UpdateLayout is needed");
     // Here is where I get fired if the control was not actually rendered in the screen

        RenderTargetBitmap rtb = new RenderTargetBitmap((int)control.ActualWidth, (int)control.ActualHeight, 96, 96, PixelFormats.Pbgra32);
        rtb.Render(control);

        var bitmapImage = new BitmapImage();
        var bitmapEncoder = new PngBitmapEncoder();
        bitmapEncoder.Frames.Add(BitmapFrame.Create(rtb));

        using (var stream = new System.IO.MemoryStream())
        {
            bitmapEncoder.Save(stream);
            stream.Seek(0, System.IO.SeekOrigin.Begin);

            bitmapImage.BeginInit();
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.StreamSource = stream;
            bitmapImage.EndInit();
        }
        System.Windows.Controls.Image testImage = new System.Windows.Controls.Image();
        testImage.Source = bitmapImage;
        return testImage;
    }

If the control was part of the layout at the beginning, adding a UpdateLayout does solve the problem (that's why I added an exception for myself there) but when the control is created from code and never reached the page UpdateLayout won't help at all.

What can I do for ensuring the rendering of all the elements in memory and then rendering an image of it without entering the page?? (fixed size is aceptable)

like image 276
javirs Avatar asked Feb 03 '16 09:02

javirs


1 Answers

In your case the solution would be something like this:

 public static System.Windows.Controls.Image fromControlToImage(System.Windows.FrameworkElement control)
 {
    Size size = new Size(100, 100); // Or what ever size you want...
    control.Measure(size);
    control.Arrange(new Rect(size));
    control.UpdateLayout();
    ...
 }

Rending WPF in Memory is quite often asked here at SO:

Drawing Control to Memory

As the control has no parent container, you need to call Measure and Arrange in order to do a proper layout.

WPF Get Size of UIElement in Memory

You need to force a render of the item, or wait for the item to be rendered. You can then use the ActualHeight and ActualWidth properties.

An additional for your purpose:

Force Rendering of a WPF Control in Memory

Propably you can use a ViewBox to render in memory

like image 186
Smartis Avatar answered Sep 22 '22 10:09

Smartis