Here is my scenario: The user clicks a WPF button which initiates an open-ended period for the collection of points on a map. When the user clicks the "finished collecting" button, I want the CollectPoints() task to complete.
Here are pieces of my SegmentRecorder class:
private CancellationTokenSource _cancellationToken;
public virtual async void RecordSegment(IRoadSegment segment)
{
_cancellationToken = new CancellationTokenSource();
var token = _cancellationToken.Token;
// await user clicking points on map
await CollectPoints(token);
// update the current segment with the collected shape.
CurrentSegment.Shape = CurrentGeometry as Polyline;
}
// collect an arbitrary number of points and build a polyline.
private async Task CollectPoints(CancellationToken token)
{
var points = new List<MapPoint>();
while (!token.IsCancellationRequested)
{
// wait for a new point.
var point = await CollectPoint();
points.Add(point);
// add point to current polyline
var polylineBuilder = new PolylineBuilder(points, SpatialReferences.Wgs84);
CurrentGeometry = polylineBuilder.ToGeometry();
// draw points
MapService.DrawPoints(CurrentGeometry);
}
}
// collect a point from map click.
protected override Task<MapPoint> CollectPoint()
{
var tcs = new TaskCompletionSource<MapPoint>();
EventHandler<IMapClickedEventArgs> handler = null;
handler = (s, e) =>
{
var mapPoint = e.Geometry as MapPoint;
if (mapPoint != null)
{
tcs.SetResult(new MapPoint(mapPoint.X, mapPoint.Y, SpatialReferences.Wgs84));
}
MapService.OnMapClicked -= handler;
};
MapService.OnMapClicked += handler;
return tcs.Task;
}
public void StopSegment(){
// interrupt the CollectPoints task.
_cancellationToken.Cancel();
}
Here are the relevant parts of my view model:
public SegmentRecorder SegmentRecorder { get; }
public RelayCommand StopSegment { get; }
public ViewModel(){
StopSegment = new RelayCommand(ExecuteStopSegment);
SegmentRecorder = new SegmentRecorder();
}
// execute on cancel button click.
public void ExecuteStopSegment(){
SegmentRecorder.StopSegment();
}
When I put a breakpoint on the line while (!token.IsCancellationRequested) and click the cancel button, I never get to that point.
Am I using the cancellation token in the correct way here?
The CollectPoints method will return whenever it hits the while condition !token.IsCancellationRequested the first time after you have called the Cancel() method of the CancellationTokenSource.
The task won't be cancelled while the code inside the while loop is still executing.
As @JSteward suggests in his comment, you should cancel or complete the TaskCompletionSource in your StopSegment() method.
Something like this:
public virtual async void RecordSegment(IRoadSegment segment)
{
_cancellationToken = new CancellationTokenSource();
var token = _cancellationToken.Token;
// await user clicking points on map
await CollectPoints(token);
// update the current segment with the collected shape.
CurrentSegment.Shape = CurrentGeometry as Polyline;
}
// collect an arbitrary number of points and build a polyline.
private async Task CollectPoints(CancellationToken token)
{
var points = new List<MapPoint>();
while (!token.IsCancellationRequested)
{
try
{
// wait for a new point.
var point = await CollectPoint(token);
//...
}
catch (Exception) { }
}
}
private TaskCompletionSource<MapPoint> tcs;
protected override Task<MapPoint> CollectPoint()
{
tcs = new TaskCompletionSource<MapPoint>();
//...
return tcs.Task;
}
public void StopSegment()
{
// interrupt the CollectPoints task.
_cancellationToken.Cancel();
tcs.SetCanceled();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With