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.
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.