WPF performance?

R

Rainer Queck

Hello NG,

In order to viaualize a production line, transporting a couple thousands
"defects" objects , I wrote a little WPF application, where each object is
reprsented as a little rectangle on a Canvas.
To This "production line" I am adding objects in a Timer.Intervall of 5 mS
The position of these objects is calculated in a variable Timer.Intervall
10...150 milliseconds, depending on the time I need to recalculate the
objects position).

as long as there are only a few object on the line, the behaviour of the
WPF-App is nice. If I decreas the line speed - resulting in more objects on
the line - the persentation of the objects gets very "bumpy". It is far away
of a smoth motion.

I assum , the way I am doing this might be not the most optimal one. So my
question is, what would be the best approach to this task?

Regards
Rainer Queck

P.S: the code with the two most important methods.

/// <summary>
/// This method takes care, that all ribbon objects are painted to
the given canvas.
/// </summary>
public void Paint()
{
GuiInvokeDelegate aDelegate = new
GuiInvokeDelegate(InvokePaint);
ribbonCanvas.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
aDelegate);
}
/// <summary>
/// This mehtod mustbe called in-synch with the gui. It controls all
visual ribbon objects and updates them
/// </summary>
private void InvokePaint()
{
try
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
int i = 0;
ribbon.Update();
while (i < ribbon.DefectList.Count)
{
Defect defect = ribbon.DefectList;
Rectangle rect = null;
if(!dicDefect.TryGetValue(defect, out rect))
{
rect = new Rectangle();
rect.Stroke = Brushes.Black;
rect.Fill = Brushes.Black;
dicDefect[defect] = rect;
rect.MouseDown += new
System.Windows.Input.MouseButtonEventHandler(rect_MouseDown);
ribbonCanvas.Children.Add(rect);
}
rect.Width = MillimeterToCanvas(defect.Width);
rect.Height = MillimeterToCanvas(defect.Height);
Canvas.SetTop(rect, MillimeterToCanvas(defect.YPos));
Canvas.SetLeft(rect,
MillimeterToCanvas(ribbon.CountToMillimeter(defect.XPosCnt)));
i++;
}
// remove no longer needed rechtangles...
Dictionary<Defect, Rectangle>.KeyCollection defects =
dicDefect.Keys;
List<Defect> toBeRemoved = new List<Defect>();
foreach (Defect d in defects)
{
if (!ribbon.DefectList.IsMember(d))
{
toBeRemoved.Add(d);
}
}
foreach (Defect d in toBeRemoved)
{
ribbonCanvas.Children.Remove(dicDefect[d]);
dicDefect.Remove(d);
}
stopWatch.Stop();
if (stopWatch.ElapsedMilliseconds > lastMilliSeconds)
{
lastMilliSeconds = stopWatch.ElapsedMilliseconds;
updateIntervall = Convert.ToInt32(lastMilliSeconds);
}
else
{
double dt = lastMilliSeconds -
stopWatch.ElapsedMilliseconds;
if (dt > 10)
{
lastMilliSeconds--;
updateIntervall = Convert.ToInt32(lastMilliSeconds);
}
}
}
catch (Exception ex)
{
string eMsg = String.Format("Exception in
RibbonVisualizer.InvokePaint: {0}",ex.Message);
FileDiagnose.Instance.ReportError(eMsg);
}
}
 
N

not_a_commie

A few things:

1. You should probably post this on the MS Avalon forum for better
help.
2. The Children.Add/Remove calls are expensive. It is much faster to
hide/unhide objects than it is to create them afresh. Do you know the
maximum number of Rectangles you would need?
3. It would be much faster in general to just draw rectangles directly
in the OnRender function and then use the "hit" helper functions to
handle the mouse down stuff.
4. Assuming the above function is the one getting called over and over
by your animator, I'm not sure why you would always invoke. That seems
a little scary to me. I still think you'd do much better overriding
the OnRender function.
5. Get some WPF books. Look at the list of books for such provided
elsewhere in this forum.
 
W

Walter Wang [MSFT]

Thank you "not_a_commie" for your informative input.

Hi Rainer,

Changing the visual tree is expensive, when the elements count exceeds some
magic number(the actual number is not important here), the performance will
downgrade quickly. This a known issue of WPF v1 and is currently being
addressed by our product team. Hopefully this will be improved in next
version.

For your specific scenario, I concur that using custom draw will be more
appropriate unless you need the rectangle to be a real element due to other
requirements.


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
R

Rainer Queck

Hello Walter,

thank you very much for your answer! I am glad to here, that it is a known
issue which already is "under repair" ;-)
For your specific scenario, I concur that using custom draw will be more
appropriate unless you need the rectangle to be a real element due to
other
requirements.
Actually it would be nice to have the rectangles to be real elements, but it
is not a "must".
I assume, you are talking about a DrawinGroup?
I also received this hint in the german NG and it sounds like a solution, to
put the rectangles into a drawing group and them move this like "one
rectangle".
My next try will go into this direction.

Regards
Rainer
 
W

Walter Wang [MSFT]

Hi Rainer,

Thanks for your quick update.

The bottom line is that we should avoid adding/removing elements in the
visual tree rapidly when the tree is very big. This is because layout pass
is mathematically-intensive process:

#Optimizing WPF Application Performance
http://msdn2.microsoft.com/en-us/library/aa970683.aspx#layout_and_design

Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
R

Rainer Queck

Hello Walter,

somehow I can't get my little project to a point where I get acceptable
performance.

I did some experiments and found, that I can build a GeometryGroup in a
background thread, containing more than 10000 EllipseGeometrie object, and
it takes me less then 100 ms,
But how can I get them from my background thread to the GUI thread?
An what would be a good way to get this Group to adequate control so that
"painting" gets done in a acceptable time too?

Any suggestions are very welcome.

Regards
Rainer
 
W

Walter Wang [MSFT]

Hi Rainer,

Well, I don't think using 10000 objects here will have good performance.
Someone told me that it's recommended keep the count under 3000 to get
reasonable performance. You may try to use the light-weight StreamGeometry
to see if could help.

For the threading issue, since the GeometryGroup is an IFreezable and
IFreezable can be safely marshalled cross thread boundaries as long as it's
in frozen state, you can call IFreezable.Freeze and pass it to the GUI
thread.


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
R

Rainer Queck

Hi Walter,

thanks for your answer.

I changed my strategy a little, by "hiding" smaller objects if the total
amount of them is too much.
I am limiting the count now to 3000 - 4000 objects.
I also changed my app to use a DispatcherTimer insted of a
System.Timers.Timer and then calling the Dispatcher.Invoke.
It looks like this lets the application "move" much smother now. Still I am
a little concerned about the CPU usage wich is at 90% and therefor I hope
that "this known issue of WPF v1" will be solved by your production team
soon ;-)

Thanks for your help!

Regards
Rainer
 
R

Rainer Queck

Hello Walter,

I now did some testing on the target system, where I would like to run my
WPF app, if I get the necessary performance.
Currently it looks not so good. The target is a 800 MHz single core box PC,
with a very poor (compared to todays standard) grafic card.
But I am still hoping, that I can improve my software design, to avoid the
KO creterias.

Reading throught the different "papers" I get the impresseion, that the
layout pass is triggered every time I change the koordinates off one of my
object.
Is that true? If so, is there a way to lock this mechanism, make all
necessary changes and then unlock it again, some thing like
- BeginLayoutEdit
- EndLayoutEdit
that would inhibit the layout pass while I am doing the neccessary changes?

Regards
Rainer
 
W

Walter Wang [MSFT]

Hi Rainer,

Unfortunately we don't have such methods in WPF like
SuspendLayout/ResumeLayout in WinForms:

#SuspendLayout in WinFX like Winforms? - MSDN Forums
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=105535&SiteID=1


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
R

Rainer Queck

Hi Walter,

thanks for your information.
I read the forum thread you gave me the link to.
This actally means, that Suspend/Resume is not necessary since the layout
pass is done after the UI thread returns.
This then means, that all the changes I do are done in one "washup".
This also means, that the statos quo in my case is nailed donw till v2 of
WPF shows up.
When will this be (approximately) ?

Regards
Rainer
 
W

Walter Wang [MSFT]

Hi Rainer,

As far as I know, currently we haven't announced any detailed timeline of
the next release.


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top