May/100
Canvas Element Positioning Performance
C# and Silverlight provide a rich set of layout controls that can be used in constructing elegant user interfaces, including a do-it-yourself Canvas class that allows you to manually position elements on it’s surface.
With a Canvas you’re almost always going to want to have some code that positions the child elements. To move things about, the property you are concerned with is not, as you might expect, a Position or Left/Top property on the child element, but rather it is a property of the Canvas that can be set on a child element.
There are two ways I have seen used to set the Canvas.Left or Canvas.Top property on a child element, and in some cases you’ll want to distinguish between the two.
The first way is to call SetValue on the Child element with the DependencyProperty you want to set, Canvas.LeftProperty or Canvas.TopProperty. The second way is to call Canvas.SetLeft or Canvas.SetTop and pass in the UIElement to set it on.
When Speed Matters
In some situations it can be helpful to understand the difference in performance of the various ways of setting these properties.
I could not find any information comparing the performance of these two methods so I wrote a small unit test (below) that could tell me which was faster. It turns out that on average, calling the Canvas.SetLeft/SetTop methods are about 3 times faster than calling Element.SetValue with the DependencyProperty.
When you think about that for a moment, it begins to make sense, because the Element.SetValue method takes a DependencyProperty and a value, which means that it resolve the property to end up at it’s destination. On the other hand the SetLeft/SetTop methods already know what data they’re setting (they even explicitly specify it in the naming!), which means they should have a pretty direct path to the data.
Testing Performance
I’ve provided below the source code that I used to generate the data for this graph so you can try it for yourself.
| 1,000,000 | 500,000 | 100,000 | 10,000 | |
| Canvas.SetLeft | 446 | 220 | 46 | 5 |
| Canvas.SetTop | 456 | 222 | 46 | 5 |
| Canvas.LeftProperty |
1361 | 683 | 139 | 18 |
| Canvas.TopProperty |
1354 | 680 | 137 | 14 |
public void TestCanvasElementPositioning()
{
long elapsedSetValueTop = 0;
long elapsedSetTop = 0;
long elapsedSetValueLeft = 0;
long elapsedSetLeft = 0;
var timer = new Stopwatch();
var testCanvas = new Canvas();
var testChild = new Ellipse();
testCanvas.Children.Add(testChild);
// How many iterations to time
const long iterations = 10000;
// Canvas.SetLeft
timer.Start();
for (int i = 0; i < iterations; i++)
Canvas.SetLeft(testChild, (double)i % 10);
timer.Stop();
elapsedSetLeft = timer.ElapsedMilliseconds;
timer.Reset();
// Element.SetValue(Canvas.LeftProperty)
timer.Start();
for (int i = 0; i < iterations; i++)
testChild.SetValue(Canvas.LeftProperty, (double)i % 10);
timer.Stop();
elapsedSetValueLeft = timer.ElapsedMilliseconds;
timer.Reset();
testCanvas = new Canvas();
testChild = new Ellipse();
GC.Collect();
// Element.SetValue(Canvas.TopProperty)
timer.Start();
for (int i = 0; i < iterations; i++)
testChild.SetValue(Canvas.TopProperty, (double)i % 10);
timer.Stop();
elapsedSetValueTop = timer.ElapsedMilliseconds;
timer.Reset();
// Canvas.SetTop
timer.Start();
for (int i = 0; i < iterations; i++)
Canvas.SetTop(testChild, (double)i % 10);
timer.Stop();
elapsedSetTop = timer.ElapsedMilliseconds;
timer.Reset();
Assert.IsTrue(elapsedSetLeft < elapsedSetValueLeft,
"Expected SetLeft to be less than SetValue");
Assert.IsTrue(elapsedSetTop < elapsedSetValueTop,
"Expected SetTop to be less than SetValue");
}
No comments yet.
Leave a comment
No trackbacks yet.

