views:

1513

answers:

4

hey all,

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!

A: 

Still working on a solution, but I've found this to be of relevance.

luvieere
+2  A: 

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();
    }
}
telno
Thank you for your interesting solution, but the generation of the chart should be done as fast as possible, so no animation would be the best. To override the animation, I simply created a new control template for the bubble data point, but there is now another problem with the legend item. Have a look at my answer post!
rbee
A: 

My solution is to simply override the default control template of the bubble data point. If you override it, the animation will be also overridden.

This is a part of my XAML Code:

<Grid>
    <Grid.Resources>
        <local:StockDataCollection x:Key="StockDataCollection"/>

        <Style x:Key="BubbleDataPointStyle" TargetType="chartingToolkit:BubbleDataPoint">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="chartingToolkit:BubbleDataPoint">
                        <Grid>
                            <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}"/>
                            <ToolTipService.ToolTip>
                                <ContentControl Content="{TemplateBinding FormattedDependentValue}" />
                            </ToolTipService.ToolTip>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Grid.Resources>

    <chartingToolkit:Chart x:Name="Chart">

        <chartingToolkit:Chart.Axes>
            <chartingToolkit:CategoryAxis Orientation="X"/>
            <chartingToolkit:LinearAxis Orientation="Y" Location="Left" Minimum="0" ShowGridLines="True" />
        </chartingToolkit:Chart.Axes>

        <chartingToolkit:BubbleSeries
            Title="Test"
            DataPointStyle="{StaticResource BubbleDataPointStyle}"
            ItemsSource="{StaticResource StockDataCollection}"
            SizeValueBinding="{Binding Path=Volume}"
            DependentValueBinding="{Binding Path=Price}"
            IndependentValueBinding="{Binding Path=Date}"/>

    </chartingToolkit:Chart>
</Grid>

Simply paste it in the LetterFrequency.xaml file on the right place and run the program again. Now it works, almost! There is still one little problem left. Look at the generated picture.

Rendered image after the updates

Beside the legend item "Test", there should be a little blue rectangle to see which bubble belongs to which legend item, but there is no one. As I have seen in the Generic.xaml-theme, there are no animations defined for the legend items.

So what is the problem now? Any ideas what it could be?

This is the legend item xaml-code of the Generic.xaml-theme of the toolkit:

    <Style TargetType="charting:LegendItem">
    <Setter Property="IsTabStop" Value="False" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="charting:LegendItem">
                <StackPanel Orientation="Horizontal">
                    <Rectangle Width="8" Height="8" Fill="{Binding Background}" Stroke="{Binding BorderBrush}" StrokeThickness="1" Margin="0,0,3,0" />
                    <datavis:Title Content="{TemplateBinding Content}" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

If I change the values of Fill and Stroke of the Rectangle to fix values (e.g. Blue and Gray), they appear on the rendered image. Why that? Is this a kind of a binding problem?

rbee
A: 

I have the same problem and would also like an answer.

Rickard Robin