WPF vs. GDI+

The problem

In one of our projects in Tesseris Pro we need to draw huge table of results of some physical experiment. The table can be 100×100 and can be 1000×1000. The application is usual Windows desktop application built with WPF. And as usual we’ve tried to use some 3rd party grid. And as you can imagine, we’ve got unacceptable performance even with visualization. The most problematic thing is that user needs to zoom-out the table to see every cell as single pixel in this case visualization gives us nothing.

Solution #1

One of the first idea was to create our own control derived from FrameworkElement or UIElement and implement drawing of the table inside overridden OnRender method. As we expected this should give us maximum performance. And keeping in mind that WPF is based on DirectX and we will get performance as in 3D games I’ve started implementation of proof of concept with following OnRender:

[code lang=csharp]
protected override void OnRender(DrawingContext dc)
{
Debug.WriteLine("OnRender…");

for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
int x = width * i;
int y = height * j;

// Draw cell rect
dc.DrawRectangle(
Brushes.Green,
pen,
new Rect(x, y, width, height));

// Draw some text in cell
dc.DrawText(
new FormattedText(string.Format("{0},{1}", i, j),
CultureInfo.InvariantCulture,
FlowDirection.LeftToRight,
typeface,
10,
Brushes.Black),
new Point(x, y));
}
}

Debug.WriteLine("OnRender finish");
}
[/code]

But performance was still unacceptable even for table of 100×100 cells. UI was refreshed in about 10 seconds. And when I measured time elapsed by OnRender I have got strange result 800ms. UI stuck for 10 seconds but OnRender takes only 800ms. I’ve got this results because WPF never draw everything immediately inside OnRender. With dc.Draw*** you just told infrastructure to draw something. Than WPF draws all required things in some other moment. So real drawing of 100×100 table requires about 10 seconds.

Solution #2

After fail with first solution I’ve tried to get DirectDraw surface and draw everything by myself. And it is not so easy with WPF. I have not found any built functionality for this. In blogs I found that the only way to use DirectDraw is to call it through COM interop. Some nightmare for as for me!
After that I’ve decided to check GDI+ (System.Drawing namespace) and try to draw the table with System.Drawing.Bitmap and than just draw bitmap with WPF:

[code lang=csharp]
protected override void OnRender(DrawingContext dc)
{
Debug.WriteLine("OnRender…");
using (var bmp = new System.Drawing.Bitmap(
columns * width,
rows * height))
{
using (var g = System.Drawing.Graphics.FromImage(bmp))
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
int x = width * i;
int y = height * j;

g.FillRectangle(
System.Drawing.Brushes.Green,
x,
y,
width,
height);

g.DrawRectangle(
System.Drawing.Pens.DarkGray,
x,
y,
width,
height);

g.DrawString(
string.Format("{0},{1}", i, j),
font,
System.Drawing.Brushes.Black,
new System.Drawing.PointF(x, y));
}
}
}

// Create Image from bitmap and draw it
var options = BitmapSizeOptions.FromEmptyOptions();
var img = Imaging.CreateBitmapSourceFromHBitmap(
bmp.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
options);

dc.DrawImage(img, new Rect(0, 0, bmp.Width, bmp.Height));
}
Debug.WriteLine("OnRender finish");
}
[/code]

When I started the app I decided that something is going wrong. UI was refreshed in less then one second. More that 10 times faster that with WPF drawing!

Conclusion

WPF gives us complex layout and device independence and other sweet things. But old GDI+ sometimes give much more – performance and simplicity!