Click here to Skip to main content
15,886,091 members
Please Sign up or sign in to vote.
1.00/5 (2 votes)
I create a game in WPF.
My goal is to create ellipses at random free positions of a canvas.
I need to exclude positions overlaid by created ellipses and create new ellipses on free random positions.
I tried VisualTreeHelper.HitTest and Geometry FillContains, but they are slow.

How can i do it in WPF?

Code (fixed timer):
C#
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.Loaded += MainWindow_Loaded;
    }

    public Canvas canvas;
    Random random = new Random();
    int add = 5;

    Timer timerEllipses;

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        canvas = new Canvas();
        canvas.Background = Brushes.Black;
        this.Content = canvas;

        timerEllipses = new Timer();
        timerEllipses.Interval = 1;
        timerEllipses.Start();

        Timer timer = new Timer();
        timer.Interval = 2000;
        timer.Elapsed += timer_Elapsed;
        timer.Start();
    }

    void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        this.Dispatcher.Invoke((Action)(() =>
        {
            int add = this.add;

            int x = random.Next((int)canvas.ActualWidth);
            int y = random.Next((int)canvas.ActualHeight);

            for (int i1 = 0; i1 < canvas.ActualWidth; i1++)
                for (int i2 = 0; i2 < canvas.ActualHeight; i2++)
                    VisualTreeHelper.HitTest(canvas, new Point(i1, i2)); // Slows down

            Ellipse ellipse = new Ellipse();
            ellipse.Width = 1;
            ellipse.Height = 1;
            ellipse.Stroke = Brushes.White;
            canvas.Children.Add(ellipse);

            timerEllipses.Elapsed += delegate
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    ellipse.Width += add;
                    ellipse.Height += add;

                    ellipse.Margin = new Thickness(x - (ellipse.Width / 2), y - (ellipse.Height / 2), 0, 0);

                }));
            };
        }));
    }
}


You can see how a separate thread doesn't help in my code when i hit test the canvas to get free points.
Code (separate thread):
C#
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.Loaded += MainWindow_Loaded;
    }

    public Canvas canvas;
    Random random = new Random();

    Ellipse ellipse;
    int add = 5;
    double x = 0;
    double y = 0;

    Stopwatch stopwatch = new Stopwatch();
    public double elapsed = 10;

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        canvas = new Canvas();
        canvas.Background = Brushes.Black;
        this.Content = canvas;

        ellipse = new Ellipse();
        ellipse.Width = 1;
        ellipse.Height = 1;
        ellipse.Stroke = Brushes.White;
        canvas.Children.Add(ellipse);

        new System.Threading.Thread(Thread1).Start();
    }

    void Thread1()
    {
        stopwatch.Start();

        while (true)
        {
            if (stopwatch.Elapsed.TotalMilliseconds >= elapsed)
            {
                stopwatch.Stop();

                this.Dispatcher.Invoke((Action)(() =>
                {
                    for (int i1 = 0; i1 < canvas.ActualWidth; i1++)
                        for (int i2 = 0; i2 < canvas.ActualHeight; i2++)
                            VisualTreeHelper.HitTest(canvas, new Point(i1, i2)); // Slows down

                    ellipse.Width += add;
                    ellipse.Height += add;

                    ellipse.Margin = new Thickness(x - (ellipse.Width / 2), y - (ellipse.Height / 2), 0, 0);
                }));

                stopwatch.Restart();
            }
        }
    }
}
Posted
Updated 23-Aug-15 21:07pm
v8
Comments
[no name] 21-Aug-15 10:41am    
Only for ellipses? Than it should be easy. But you have to dive a "little bit" into math.
Sergey Alexandrovich Kryukov 21-Aug-15 10:50am    
Why is it slow: many thousands ellipses? If not, I never spotted WPF was slow. Perhaps we would need to see how you made is slow...
Filling in Geometry can be slow, but why not coloring each ellipse individually?
—SA
Ziya1995 21-Aug-15 12:16pm    
> Why is it slow: many thousands ellipses?
No, it gets slow after a few ellipses.

> Perhaps we would need to see how you made is slow...
I added the code.
Sergey Alexandrovich Kryukov 21-Aug-15 15:11pm    
Then something is fundamentally wrong. Few ellipses is nothing at all, should be blazing fast. Again, using a timer is a bad idea, using a separate thread is much more reliable.
—SA
Ziya1995 22-Aug-15 3:51am    
> Then something is fundamentally wrong. Few ellipses is nothing at all, should be blazing fast.
You don't understand, you even don't know what causes the problem, but i repeat:
"WPF: Hit testing is slow." - the topic name shows what is a cause of slow performance.
The code has a certain line commented "// Slows down" you can see as a cause of slow performance.

Solution 2 is no solution at all having nothing related to the problem.
Do you have a solution or not?

Ahhh! I see. How can I ansee it? You are doing awful thing in principle: under Invoke, on each timer event, you create a new timer, set it up to fire other events, and other crazy thing. This is insane. Don't do anything like that. The problem is amazingly simple, even with a timer. I don't even want to discuss it. Just throw out it all and write something reasonable.

For example, thread solution will be the simplest. Just create a thread with a loop and some Thread.Sleep. Create this thread only once and use it during all the runtime. In each loop, do all the calculation and do under Invoke only one thing: pure UI manipulation: creation of ellipse and adding it to canvas (removal from canvas, anything like that). This is not simple, just extremely simple.

—SA
 
Share this answer
 
Comments
CPallini 21-Aug-15 16:50pm    
5.I agree, the posted code is wrong. However, it looks many people is disappointed by WPF performance.
Sergey Alexandrovich Kryukov 21-Aug-15 18:25pm    
Thank you Carlo. Maybe I've done not too much of WPF development, never face performance problems.
—SA
Ziya1995 22-Aug-15 2:53am    
My code shows the line which slows down the app and it is noted by the comment: "// Slows down". If you remove this line, you can see no problem, but with the line you get slow performance.

> on each timer event, you create a new timer
You said the problem is Timer, i fixed it, but no difference, it works same well until i add the line VisualTreeHelper.HitTest.

> thread solution will be the simplest
Thread is no option, it increases CPU usage.

Do you have solution or not?
Sergey Alexandrovich Kryukov 22-Aug-15 5:44am    
How did you "fix it"?
—SA
Ziya1995 22-Aug-15 6:53am    
I updated the code where i create Timer once.
Is it ok?
Possibly this Code Project article might interest you: "Solutions for WPF Performance Issue"[^].
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 21-Aug-15 16:36pm    
Carlo,

What performance are you talking about so seriously? Just take a closer look at the "code". This is so much of gibberish that it's hard to imagine that it can work at all. See also Solution 2.

—SA
CPallini 21-Aug-15 16:42pm    
There was no code, if I remember well.
Sergey Alexandrovich Kryukov 21-Aug-15 18:24pm    
I see; this is v.3 now...
—SA
do some things:
1. don't subscribe on Timer.Elapsed events many times. Throw away subscription and second Dispatcher.BeginInvoke in event handler;
2. don't test every screen point as you do now. I think, you only need to hit test single random point. And if this place is not empty, the second one. Not whole canvas.
Yes, hittesting is expensive, especially if you do it every time for every pixel
3. try to use DispatcherTimer instead of Timer. In this case you won't need BeginInvoke in event handler at all
 
Share this answer
 
v2
Comments
Ziya1995 26-Aug-15 5:13am    
> don't test every screen point as you do now.
But i need to do so.

> Yes, hittesting is expensive, especially if you do it every time for every pixel
I consider it as impossible to do in WPF.
Irina Pykhova 26-Aug-15 7:22am    
no need at all. I can imagine several ways to find empty place without that. For example, look at this: http://stackoverflow.com/questions/10352555/how-can-i-find-uielements-in-a-rectangle-in-wpf
The idea is to suggest some place for new ellipse (your approach with random points) and check whether it is intersecting with existing ellipses. You can start from checking rectangle bounds, it would be not very accurate for ellipses, but will give you the starting point for further improvements. The number of ellipses should be much smaller than number of pixels in canvas, so it will be more effective.
In my opinion, your code from the first post will work bad on any platform. It is not related to WPF

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900