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.