WPF Instantiate User control programmatically to r

2019-03-20 20:55发布

问题:

I want to instantiate a user control programmatically in a DLL to save it afterwards as PNG file. This is generally no problem with PngBitmapEncoder and RenderTargetBitmap.

This are my questions:

  • How do I instantiate the control? Simply with the new-operator?
  • Do I have to instantiate it in an seperate thread?
  • How do I force the control to update all its children and to render itself again?

This is my code to instantiate the user control and save it as PNG-file (LetterFrequency is the user control):

    [STAThread]
    static void Main(string[] args)
    {
        LetterFrequency let = new LetterFrequency();
        let.Width = 600;
        let.Height = 400;
        let.Background = Brushes.White;

        let.Measure(new Size(let.Width, let.Height));
        let.Arrange(new Rect(new Size(let.Width, let.Height)));

        let.UpdateLayout();

        RenderTargetBitmap bitmap = new RenderTargetBitmap((int)let.Width, (int)let.Height, 96d, 96d, PixelFormats.Pbgra32);
        bitmap.Render(let);

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

        using (Stream stm = File.Create("test.png"))
        {
            png.Save(stm);
        }
    }

If you run the app this way, it generates the PNG file, but the data, which will be added in the XAML are not visible, if you look into the XAML Designer, you can see the chart with some bubbles. The png file contains only the chart area, but no bubbles? Why that? I think is is a Update/rendering problem, but how to solve this?

Here is the visual studio solution, it contains the Console Project, which renders the user control to a PNG file and two other projects of the WPF toolkit for the chart.

Have a look at it, it will generate the PNG file in the bin/Debug respectively in the exe-folder: http://www.file-upload.net/download-1904406/ChartRenderBitmap.zip.html

Hope it works without problems!

Thanks!

回答1:

The data in your chart is not rendered in your PNG file because there is an animation applied to the reveal of the data points. Take a look at your LetterFrequency control in a Window, and you'll see the points gradually reveal themselves.

Your code takes a snapshot of the control immediately after its creation, so you see no data.

You could:

  1. wrap all this in a window and tell it to take the snapshot after X seconds
  2. disable all the animations in any controls you're going to snapshot
  3. maybe there's a way to "fast-forward" animations programmatically, but i couldn't find one

Here's solution 1, and it works:

    public partial class Window1 : Window
{
    System.Windows.Threading.DispatcherTimer snapshotTimer;

    public Window1()
    {
        InitializeComponent();

        this.Width = 600;
        this.Height = 400;
        let.Width = 600;
        let.Height = 400;
        let.Background = Brushes.White;     

        this.Loaded += new RoutedEventHandler(Window1_Loaded);
    }

    void Window1_Loaded(object sender, RoutedEventArgs e)
    {
        this.snapshotTimer = new System.Windows.Threading.DispatcherTimer();
        this.snapshotTimer.Interval = TimeSpan.FromSeconds(2);
        this.snapshotTimer.Tick += new EventHandler(snapshotTimer_Tick);
        this.snapshotTimer.IsEnabled = true;
    }

    void snapshotTimer_Tick(object sender, EventArgs e)
    {
        this.snapshotTimer.IsEnabled = false;
        WritePng();
    }

    void WritePng()
    {
        RenderTargetBitmap bitmap = new RenderTargetBitmap((int)let.Width, (int)let.Height, 96d, 96d, PixelFormats.Pbgra32);
        bitmap.Render(let);

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

        using (Stream stm = File.Create("test.png"))
        {
            png.Save(stm);
        }

        this.Close();
    }
}