Back to articles

WPF vs. GDI+

The problem

In one of our projects in Kvinivel we need to draw a huge table of results of some physical experiments. The table can be 100×100 and can be 1000×1000. The application is usually 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 the user needs to zoom out the table to see every cell as a single pixel, in this case, visualization gives us nothing.

Solution #1

One of the first ideas was to create our own control derived from FrameworkElement or UIElement and implement a drawing of the table inside the 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 the implementation of proof of concept with following OnRender:

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");
}

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

Solution #2

After failing with the first solution, I’ve tried to get a 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, 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 then just draw a bitmap with WPF:

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");
}

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

Conclusion

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

It also could be interesting to You

How do we achieve the highest efficiency in software development in Kvinivel

As many of our clients already know, we are doing fixed-price projects. Starting from requirements preparation and finishing with delivery and initial support. We have a lot of success stories, and all our customers are…

Hello World in Visual Studio Code on Linux

This post adds a sample in an addition to my post about VS Code previous post Setup Install the latest mono as described at http://www.mono-project.com/docs/getting-started/install/linux/#debian-ubuntu-and-derivatives. Install Visual Studio Code from https://code.visualstudio.com Just unpack and start.…