Your Privacy Matters: We use our own and third-party cookies to improve your experience on our website. By continuing to use the website we understand that you accept their use. Cookie Policy
115
XamChart RenderTargetBitmap and DataBinding: Broken...
posted
I am writing an application where I must render a bitmap of a chart without in any way visually displaying it (so no adding it to the visual tree.) The only technique I have seen so far involves using a RenderTargetBitmap to do so.

In a previous post, [Infragistics] Andrew Smith posted code which purported to solve this problem. It worked, but only if you did no data binding. I have modified it to the following state to demonstrate the problem. (You should simply be able to paste this code into a class file, and add the appropriate references. The class extends window, so simply run the project to see the result.)

using System;

using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Infragistics.Windows.Chart;

public class VisualChildWindow : Window {
public VisualChildWindow() {
XamChart testChart = new XamChart();
testChart.DataBind += chart_DataBind;

testChart.BeginInit();
testChart.Width = 400;
testChart.Height = 200;
testChart.EndInit();

addSeriesAndData(testChart);

testChart.Measure(new Size(400, 200));

testChart.Refresh();
testChart.Arrange(new Rect(0, 0, 400, 200));
testChart.UpdateLayout();

RenderTargetBitmap bmp = new RenderTargetBitmap(400, 200, 96, 96, PixelFormats.Pbgra32);
bmp.Render(testChart);
Image image = new Image() { Source = bmp };
image.Stretch = Stretch.None;
// Content = testChart;
Content = image;
Panel panel;
}

void chart_DataBind(object sender, DataBindEventArgs e) {

}

private void addSeriesAndData(XamChart chart) {
var series = new Series();
series.DataSource = new ArrayList { new{number = 1}, new{number = 2}, new{number = 4.6} };
series.DataMapping = "Value=number";
chart.Series.Add(series);
chart.DataSourceRefresh();
}

[STAThread]
public static void Main() {
var window = new VisualChildWindow();
var app = new Application();
app.Run(window);
}

}

Near the bottom of the main method, you will see two different lines, one setting Content to the chart, the other the image. You can swap them back and forth. If you set the chart as the content, you can verify that it is rendering correctly. Swap them back to test the image.

Finally, you will see a call to addSeriesAndDatabind(). This method sets up the databinding. If you comment it out, you get the default rendering of the chart.

Now to the problem. Comment out the call to addSeriesAndDatabind(), and run. You will see the chart gets rendered correctly as an image. Restore the call to addSeriesAndDatabind() and run, and you will see the chart render itself, but with no data. If you put a breakpoint on chart_DataBind() (which I have wired up as a handler for DataBind()) you will see that it never gets called. Notice also that I am already calling chart.DataSourceRefresh(), which according to the documentation is supposed to force a refresh.

It looks for all the world as though if only I could force the chart to databind, I'd be home free. I can't seem to do that, however. Perhaps somebody at infragistics can give a little more insight as to the life of this control. Or better yet, take the above source code and make it work.

Thanks,
Jody
  • 115
    posted
    If anybody is still reading this thread, I found the solution. The class below shows what I believe are the Canonical steps to get a XamChart to render to a bitmap. Note that this is done within a test, and the class does not extend Window. This means you can do this entirely behind the scenes, without reference to visual or logical trees.

    The magic is in the getFixedChart method. This method shows the order of operations, and order is crucial. You must call BeginInit & EndInit, then Measure & Arrange, then DataSourceRefresh. This call will fire DataBind(), so be aware. After DataBind() returns (assuming you are trapping that event) you must finally call Refresh and UpdateLayout. At this point, the chart should be fully constructed, and ready to go.

    The first test ("RunIt") shows the sequence.

    There is nothing wrong between XamChart and RenderTargetBitmap. I believe all the previous troubles reported were due to the fact that the chart was simply not rendering. Once the chart is forced to render, it goes to a bitmap with no troubles. It turns out that XamChart is working correctly.

    BTW, Infragistics, it would have been nice to find some of this in the documentation. Maybe a little less Xaml and little more meat next time, eh? It would also be nice if we could make the Chart throw a useful exception, rather simply render blank.



    using System;
    using System.Collections;
    using System.IO;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using Infragistics.Windows.Chart;
    using NUnit.Framework;

    namespace DataBindingTests {
    [TestFixture]
    public class Canonical {
    private const string imageFileName = "imageCan.png";


    [Test]
    public void runIt() {
    XamChart chart = getFixedChart(800, 500);
    BitmapSource bmp = getBitmap(chart, 800, 500);
    writeToFile(bmp);
    }



    private XamChart getFixedChart(int width, int height) {
    XamChart chart = new XamChart();

    chart.BeginInit();
    chart.Width = width;
    chart.Height = height;
    chart.View3D = false;
    getDataSeries(chart);
    chart.EndInit();

    chart.Measure(new Size(width, height));
    chart.Arrange(new Rect(0, 0, width, height));

    chart.DataSourceRefresh();

    chart.Refresh();
    chart.UpdateLayout();
    return chart;
    }

    private BitmapSource getBitmap(Visual testChart, int width, int height) {
    RenderTargetBitmap bmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
    bmp.Render(testChart);
    return bmp;
    }

    private void getDataSeries(XamChart chart) {
    var series = new Series();
    series.DataSource = new ArrayList { new { number = 1, name = "Peter" }, new { number = 2, name = "Susan" }, new { number = 4.6, name = "Edmund" }, new { number = 14.6, name = "Lucy" } };
    series.DataMapping = "Value=number; Label=name";
    series.Label = "Test Data";
    chart.Series.Add(series);
    }

    private void writeToFile(BitmapSource bmp) {
    File.Delete(imageFileName);
    PngBitmapEncoder encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bmp));
    FileStream stream = new FileStream(imageFileName, FileMode.Create);
    encoder.Save(stream);
    stream.Flush();
    stream.Close();
    }

    }
    }