Pass parameters to WebClient.DownloadFileCompleted event
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In older .NET code that still uses WebClient, DownloadFileCompleted is an event callback, so the handler often needs some context about which download just finished. The standard way to pass that context is the userToken argument on DownloadFileAsync, which later appears as e.UserState in the completion event.
That is much safer than storing per-download state in global variables or shared fields. It also scales correctly when several downloads are running at the same time.
Use the userToken Overload
DownloadFileAsync has an overload that accepts a third argument for caller-provided state:
This is the intended pattern for attaching request-specific data to the later completion event.
Why UserState Matters for Multiple Downloads
If several downloads happen concurrently, the completion handler needs a reliable way to distinguish them. Using a token object per request solves that:
Each completion callback receives the matching context through e.UserState. That avoids race conditions that appear when all downloads share the same mutable field.
Check Success, Failure, and Cancellation Separately
Do not treat DownloadFileCompleted as automatic success. The event fires for completion, failure, and cancellation, so the handler should check:
- '
e.Cancelled' - '
e.Error' - success only if both are absent
That is especially important in legacy event-driven code, where it is easy to log "done" without checking the actual outcome.
A Small Helper Pattern
If the codebase still uses several WebClient downloads, a small wrapper can make the event pattern less repetitive:
That keeps the context creation and event-state pattern consistent across call sites.
WebClient Is Legacy
For new code, HttpClient with task-based async APIs is usually a better choice. It is easier to compose, test, and cancel cleanly:
Still, when you are maintaining an existing event-based codebase, userToken plus e.UserState is the right pattern for passing parameters into DownloadFileCompleted.
Common Pitfalls
The biggest mistake is storing per-download context in shared fields instead of passing it through userToken. That falls apart as soon as multiple downloads overlap.
Another common issue is ignoring e.Error and e.Cancelled and assuming that the completion event means success.
Developers also sometimes cast e.UserState without checking for null or the wrong type. If different callers reuse the same handler, be defensive.
Finally, mixing old event-driven WebClient code with newer async patterns without a plan can make the codebase harder to follow. If migration is underway, isolate legacy downloads behind a small adapter.
Summary
- Pass request-specific parameters through the
userTokenoverload ofDownloadFileAsync. - Read the token back from
e.UserStateinsideDownloadFileCompleted. - Use a separate context object for each concurrent download.
- Check cancellation and error states explicitly before treating a download as successful.
- Prefer
HttpClientfor new development, but useUserStatecorrectly in legacyWebClientcode.

