7

I have a simple UserControl called UserControl1 that contains a TextBlock:

  <UserControl x:Class="WpfApplication2.UserControl1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
     <Grid>
         <TextBlock Text="{Binding}"/>
     </Grid>
</UserControl>

I initialized a new instance of it and gave it a DataContext in code. when the window is closing I have to draw this control to an image file. The UserControl does not render the bounded text in the file that been created.

enter image description here

and this is my code using the usercontrol:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Closing += MainWindow_Closing;
    }

    void MainWindow_Closing(object sender, CancelEventArgs e)
    {
        UserControl1 uc = new UserControl1();
        uc.DataContext = "hello";
        uc.Height = 100;
        uc.Width = 100;
        uc.Background = Brushes.LightBlue;
        DrawToImage(uc);
    }

    private void DrawToImage(FrameworkElement element)
    {
        element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        element.Arrange(new Rect(element.DesiredSize));

        RenderTargetBitmap bitmap = new RenderTargetBitmap((int)element.Width, (int)element.Height,
                                                           120.0, 120.0, PixelFormats.Pbgra32);
        bitmap.Render(element);

        BitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(bitmap));

        using (Stream s = File.OpenWrite(@"C:\555.png"))
        {
            encoder.Save(s);
        }
    }
}

I Hope It's clear enough, any help will be very appreciated.

Erez
  • 6,405
  • 14
  • 70
  • 124

5 Answers5

6

You just forgot to force a Layout update on your control after manually Measuring/Arrangeing it (which will not be enough to force binding resolving).

A simple call to UpdateLayout makes it work :

private void DrawToImage(FrameworkElement element)
{
    element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
    element.Arrange(new Rect(element.DesiredSize));
    element.UpdateLayout();

    RenderTargetBitmap bitmap = new RenderTargetBitmap((int)element.Width, (int)element.Height,
                                                        120.0, 120.0, PixelFormats.Pbgra32);
    bitmap.Render(element);

    BitmapEncoder encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bitmap));

    using (Stream s = File.OpenWrite(@"C:\555.png"))
    {
        encoder.Save(s);
    }
}

Edit : More on when bindings are resolved : link

Community
  • 1
  • 1
Sisyphe
  • 4,626
  • 1
  • 25
  • 39
  • Great answer, I am new to WPF can you please provide me link for helpful tutorials, i have to mainly deal with bitmap image creation having text over this. i will be very thnakful to u for this – Jot Dhaliwal Jul 04 '14 at 14:10
0

Try to call the function SaveImage() on userControl1.Loaded event

user1064519
  • 2,180
  • 12
  • 13
  • first of all, Thanks. The problem with the Loaded event is that I'm actually have to save into an image when the window is closed - so I put the code in the close event of the window, and the Usercontrol1 does not get to fire the Loaded event. Any idea how to do it different? – Erez Dec 27 '12 at 17:35
  • I don't know maybe I should update the question, and add the Closed event, at first the idea was to simplify the code before uploading it here. – Erez Dec 27 '12 at 17:37
  • ye,makes sense. if you call SaveImage() in Closing event it sould work too. – user1064519 Dec 27 '12 at 17:46
0

If I do this it works, not sure this is what you want though:

<Window x:Class="DrawImage.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DrawImage="clr-namespace:DrawImage"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DrawImage:UserControl1 x:Name="uc" Visibility="Hidden"/>
    </Grid>
</Window>

void MainWindow_Closing(object sender, CancelEventArgs e)
{
    uc.DataContext = "hello";
    uc.Height = 100;
    uc.Width = 100;
    uc.Background = Brushes.LightBlue;
    uc.Visibility = Visibility.Visible;
    DrawToImage(uc);
}
Johan Larsson
  • 17,112
  • 9
  • 74
  • 88
  • Thanks Johan Larsson, I don't want to add the UserControl1 to my UI tree, just to render it into an image, drawing it into the tree and giving it a hidden visibility is not seems to me as an appropriate way to solve the problem. P.S - I'm going to to this for many elements and don't want to add them all to my tree. – Erez Dec 30 '12 at 12:01
0

EDIT

I am now able to reproduce the issue. If I set the DataContext in the Window contstructor then it works. If I set it in the Winndow_Closed event I get the exact same result that you get.

I guess there might no workaround since the WPF needs some time to actually render the text on the UI thread. If you render the PNG before the WPF has rendered the text on the UI thread it will not appear on the PNG. A workaround does not seem to exist since the window will be destroyed when the Closed event handlers has been running. There is no way to block the UI thread on the one hand to prvevent to window from beeing detroyed when you on the other hand want the UI thread to render the control.

I'd suggest to render the image as soon as the control has been rendered and save the image file when the window is closed.

GameScripting
  • 16,092
  • 13
  • 59
  • 98
0

I posted an Article in my Blog (putting in the account the png Transparancy (causes the black background)): Saving FrameworkElement as Image

FrameworkElement element = myControl.Content;
// you can set the size as you need.
Size theTargetSize = new Size(1500,2000)
element.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(theTargetSize ));
// to affect the changes in the UI, you must call this method at the end to apply the new changes
element.UpdateLayout();

You can find the full cod in the Blog Post.

Henka Programmer
  • 433
  • 6
  • 18