Share via


How to make a Simple fast WPF mouse tail (or tracer stream) (en-US)

This article shows how to add "Tracer" objects, that follow the mouse around in a stream / tracer effect.

The concept is to move away from a "controller" type solution where a monitor loop adds and removes dots.

In this example, each dot is responsible for it's own actions, similar to how objects can group and swarm together, depending on their AI.

In this screenshot, the black lines are just "mouse-down" drawn on the InkCanvas. The red line is the tracer line following and decaying behind the mouse.

The TracerAnt

class TracerAnt : Border 
   { 
       FrameworkElement myCanvas; 
       Timer timer; 
 
       public TracerAnt(FrameworkElement parent, int delay) 
       { 
           Width = Height = 2; 
           Background = Brushes.Red; 
           HorizontalAlignment = System.Windows.HorizontalAlignment.Left; 
           VerticalAlignment = System.Windows.VerticalAlignment.Top; 
           IsHitTestVisible = false; 
           Point p = Mouse.GetPosition(myCanvas); 
           Margin = new  Thickness(p.X, p.Y, 0, 0); 
           myCanvas = parent; 
           timer = new  Timer(selfControlledBorderTick, this, 0, delay); 
       } 
 
       void selfControlledBorderTick(object state) 
       { 
           Application.Current.Dispatcher.Invoke(DispatcherPriority.Send, new  Action(() => 
           { 
               Point p = Mouse.GetPosition(myCanvas); 
               Margin = new  Thickness(p.X, p.Y, 0, 0); 
           })); 
       } 
 
       public void  Kill() 
       { 
           timer.Change(Timeout.Infinite, Timeout.Infinite); 
       } 
   }

When the timer ticks, it simply changes the margin to the mouse position (in relation to the container)

Note the use of the Application.Current.Dispatcher, a failsafe way to get back to the UI thread

Also note the Horizontal and VerticalAlignment set to left and top, as a centered or stretched control won;t be positioned the same as the mouse.

On MouseEnter

 

private void  MyCanvas_MouseEnter(object sender, MouseEventArgs e) 
{ 
    AddAntTracers(numberOfAnts, millisecondsTillCheck); 
} 
 
private void  AddAntTracers(int  count, int  totalDelay) 
{ 
    int gap = totalDelay / count; 
   
    Task.Factory.StartNew(()=> 
        { 
            for (var a = 0; a < count; a++) 
            { 
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Send, new  Action(() => 
                { 
                    MyInkCanvas.Children.Add(new TracerAnt(MyInkCanvas, totalDelay)); 
                })); 
                Thread.Sleep(gap); 
            } 
        }); 
}

 

AddAntTracers takes the total time between an ant tick/check and divides by the number of ants desired. this gives the interval between adding that means a perfect stream is produced.

You can play with the totalDelay and count to make the stream longer or shorter and the ants closer or father apart.

Notice also the use of .net4's Task class, a very powerful replacement for the BackgroundWorker, although you easily use a Background worker if you are still on .net 3.5

On MouseLeave

private void  MyCanvas_MouseLeave(object sender, MouseEventArgs e) 
{ 
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Send, new  Action(() => 
    { 
        foreach (TracerAnt ant in MyInkCanvas.Children) 
            ant.Kill(); 
 
        MyInkCanvas.Children.Clear(); 
    })); 
}

Finally, when the mouse leaves, the collection is removed.

However, each TracerAnt still has an active timer, which may prevent tear-down and garbage recycling and a memory leak, so first itterate through each child and kill the timer.

The Kill method shown in the TracerAnt snippet uses Timeout.Infinate for delay and interval, which kills the timer.

This available in a demo project here.

This small article is part of a series of WPF "How To" articles, in response to real user questions on the MSDN WPF Forum.