System.NotSupportedException: A collection's thread ownership seems changed at runtime - c#

I have a UpFile class to monitor file change at specific application directory:
class UpFile
{
public string file { get; set; }
public int id { get; set; }
public int long1 { get; set; }
public int short1 { get; set; }
static public System.Collections.ObjectModel.ObservableCollection<UpFile> _upfiles =
new System.Collections.ObjectModel.ObservableCollection<UpFile>();
}
and FileWatcher :
class FileWatcher
{
public void Start(Callback callback)
{
_callback = callback;
_watcher.Path = Config._upDirectory;
_watcher.NotifyFilter = NotifyFilters.LastWrite;
_watcher.Filter = "*.up";
_watcher.IncludeSubdirectories = false;
_watcher.Created += new FileSystemEventHandler(OnNewFile);
_watcher.Changed += new FileSystemEventHandler(OnChangeFile);
_watcher.EnableRaisingEvents = true;
_started = true;
}
private void OnChangeFile(object sender, FileSystemEventArgs args)
{
Logger.I("OnChangeFile " + args.FullPath);
CheckFile(args.FullPath);
}
private void CheckFile(string fileName)
{
Logger.I("New UpFile:" + mbplcflow);
UpFile newFile = new UpFile { id = mbplcflow, file = fileName, long1 = long1, short1 = short1 };
UpFile._upfiles.Add(newFile);
_callback();
}
}
And the delegate is on UI thread to show all changed items in DataGrid control, it is in the MainWindow class:
private void UpFileCreated()
{
if (Dispatcher.CheckAccess())
dgUp.ItemsSource = UpFile._upfiles;
else
Dispatcher.Invoke(delegate { dgUp.ItemsSource = UpFile._upfiles; });
}
The first file change runs perfectly, I see file change item is added to the UI DataGrid control. But when the second file change comes, I got "System.NotSupportedException" at the line:
UpFile._upfiles.Add(newFile);
So I don't understand why the _upfiles's thread ownership is changed over time? How can I make it not owned by the UI thread?
BTW this is the log I got:
I 16:47:48.169 OnChangeFile c:\test\test\23732_1657874868168.up
I 16:47:48.175 New UpFile:19
But when second file change comes it stopped at the exception.

Related

Disable a button, when a Task is completed

I have a method, that checks if a button can be pressed or not, if my file path or the language is null, I don't enable the button, and of course, when my file path is selected, I raise the event.
So right now I am doing some work on Azure, and I want to disable the button when I start my work and enable it when I finished my work.
I tried to raise the event, before the call o the method and after the call, but it doesn't enable the button
public string? FilePath { get; set; }
public bool IsWorking { get; set; }
public Dictionary<int, Languages>? LanguagesDictionary { get; set; }
public Visibility CanShow { get; set; }
public DialogHelper DialogHelper { get; }
public FolderHelper FolderHelper { get; }
public AudioHelper AudioHelper { get; }
public AzureTranscriptionService AzureTranscription { get; }
public Command PickFileCommad { get; set; }
public Command StartCommand { get; set; }
private string? _SelectedItem;
public string SelectedItem {
get => _SelectedItem!;
set {
if (_SelectedItem != value) {
_SelectedItem = value;
StartCommand.RaiseCanExecuteChanged();
}
}
}
public AudioPageViewModel() {
InitListLanguages();
AzureTranscription = new AzureTranscriptionService();
DialogHelper = new DialogHelper();
FolderHelper = new FolderHelper();
AudioHelper = new AudioHelper();
CanShow = Visibility.Hidden;
PickFileCommad = new Command(PickFileAction);
StartCommand = new Command(StartAction, CanStartAction);
}
private bool CanStartAction(object arg) {
if (string.IsNullOrEmpty(SelectedItem) ||
string.IsNullOrEmpty(FilePath) ||
IsWorking == true) {
return false;
}
return true;
}
private async void StartAction(object obj) {
var FileWithoutExtension = Path.GetFileNameWithoutExtension
(FilePath);
var AudioPath = FolderHelper.CreateFolder(ConstantsHelpers.AUDIO);
var DocumentPath = FolderHelper.CreateFolder();
var AudioFileNamePath = Path.Combine(AudioPath, $"{FileWithoutExtension}{ConstantsHelpers.WAV}");
var ConvertedAudioPath = AudioHelper.Converter(FilePath!, AudioFileNamePath);
var DocumentName = Path.Combine(DocumentPath, $"{FileWithoutExtension}{ConstantsHelpers.DOCX}");
IsWorking = true;
StartCommand.RaiseCanExecuteChanged();
await AzureTranscription.ConvertToTextAsync(ConvertedAudioPath,
SelectedItem, DocumentName);
IsWorking = false;
StartCommand.RaiseCanExecuteChanged();
}
private void PickFileAction() {
var FullPath = DialogHelper.GetFilePath(ConstantsHelpers.AUDIO);
FilePath = FullPath;
StartCommand?.RaiseCanExecuteChanged();
}
public async Task ConvertToTextAsync(
string FilePath,
string Language,
string WordDocName) {
// Configure speech service
var config = SpeechConfig.FromSubscription(ConstantsHelpers.AZURE_KEY, ConstantsHelpers.AZURE_REGION);
config.SpeechRecognitionLanguage = Language;
// Configure speech recognition
var taskCompleteionSource = new TaskCompletionSource<int>();
using var audioConfig = AudioConfig.FromWavFileInput(FilePath);
using var speechRecognizer = new SpeechRecognizer(config, audioConfig);
speechRecognizer.Recognizing += SpeechRecognizer_Recognizing;
speechRecognizer.Recognized += SpeechRecognizer_Recognized;
speechRecognizer.SessionStarted += SpeechRecognizer_SessionStarted;
speechRecognizer.SessionStopped += SpeechRecognizer_SessionStopped;
await speechRecognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);
Task.WaitAny(new[] { taskCompleteionSource.Task });
await speechRecognizer.StopContinuousRecognitionAsync().ConfigureAwait(false);
}
private void SpeechRecognizer_SessionStopped(object? sender, SessionEventArgs e) {
Debug.WriteLine("Stepped");
var sb = new StringBuilder();
foreach (var item in Letters) {
sb.Append(item);
}
}
private void SpeechRecognizer_SessionStarted(object? sender, SessionEventArgs e) {
Debug.WriteLine("Started");
}
private void SpeechRecognizer_Recognized(object? sender, SpeechRecognitionEventArgs e) {
if (e.Result.Reason == ResultReason.RecognizedSpeech) {
foreach (var item in e.Result.Text) {
Letters.Add(item);
}
}
}
private void SpeechRecognizer_Recognizing(object? sender, SpeechRecognitionEventArgs e) {
Debug.WriteLine(e.Result.Text);
}
}
When I start working, I execute this code
Many thanks
If the below refactorings don't help, debug your program:
check if CanStartAction is actually called and returns the expected result.
probably the asynchronous method returns too fast for your eye to see the button being disabled. Modify your code as follows to test this:
await AzureTranscription.ConvertToTextAsync(ConvertedAudioPath,
SelectedItem, DocumentName);
await Task.Delay(TimeSpan.FromSeconds(5));
In general, move the RaiseCanExecuteChanged request to the relevant property setters to keep your code clean.
private bool isBusy;
private bool IsBusy
{
get => this.isBusy;
set
{
this.isBusy = value;
this.StartCommand.RaiseCanExecuteChanged();
}
}
private bool CanStartAction(object arg)
{
return !string.IsNullOrEmpty(SelectedItem) &&
!string.IsNullOrEmpty(FilePath) &&
!this.IsBusy
}
private async void StartAction(object obj)
{
this.IsBusy = true;
var fileWithoutExtension = Path.GetFileNameWithoutExtension
(FilePath);
var audioPath = FolderHelper.CreateFolder(ConstantsHelpers.AUDIO);
var documentPath = FolderHelper.CreateFolder();
var audioFileNamePath = Path.Combine(audioPath, $"{FileWithoutExtension}{ConstantsHelpers.WAV}");
var convertedAudioPath = AudioHelper.Converter(FilePath!, audioFileNamePath);
var documentName = Path.Combine(documentPath, $"{fileWithoutExtension}{ConstantsHelpers.DOCX}");
await AzureTranscription.ConvertToTextAsync(convertedAudioPath,
this.SelectedItem, documentName);
this.IsBusy = false;
}
public async Task ConvertToTextAsync(string FilePath,
string Language,
string WordDocName)
{
...
// Using Task.Wait, Task.WaitAny and Task.WaitAll will execute a Task synchronously and introduces a potential deadlock.
// To avoid this, always await a Task and use Task.WhenAny and Task.WhenAll instead
await Task.WhenAny(new[] { taskCompleteionSource.Task });
// Or because you only have to wait for a single Task write
await taskCompleteionSource.Task;
...
}
See C# Naming guidelines

How to manually close connection to database and cancel querying data running in background process?

I have Windows Forms application that connects to Oracle database using a BackgrwoundWorker.
public partial class MainForm : Form, IDataFetcher
{
public EntryForm EntryForm { get; set; }
public ApplicationSettings Settings { get; set; }
private DbDataBackgroundWorker BackgroundWorker { get; set; }
enum QueryStatus { RUNNING, NOT_STARTED }
private QueryStatus;
public BillingForm()
{
InitializeComponent();
// configure background actual opearations
BackgroundWorker = new OpearationBackgroundWorker()
{
OnDataFetch = OnDataFetch,
OnDataFetchCompleted = OnDataFetchCompleted
};
}
public BillingForm(EntryForm parentForm, ApplicationSettings appSettings) : this()
{
EntryForm = parentForm;
Settings = appSettings;
queryStatus = QueryStatus.NOT_STARTED;
}
private void ExecuteButton_Click(object sender, EventArgs e)
{
switch (queryStatus)
{
case QueryStatus.NOT_STARTED:
BackgroundWorker.Start();
break;
case QueryStatus.RUNNING:
BackgroundWorker.Stop();
break;
}
}
public void OnDataFetchCompleted(object sender, RunWorkerCompletedEventArgs e)
{
queryStatus = QueryStatus.NOT_STARTED;
if (e.Error != null)
{
MessageBox.Show(e.Error.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
public void OnDataFetch(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
if (worker.CancellationPending)
{
e.Cancel = true;
//MessageBox.Show("Запрос остановлен");
return;
}
// create database handler
var db = new DatabaseConnector(Settings.DbConnectionString);
var model = new BillingModel(db)
{
PostTitle = PostTitle,
StartDate = StartDateTimePicker.Value,
EndDate = EndDateTimePicker.Value,
};
try
{
model.Query((datatable) =>
{
Invoke(new Action(() =>
{
QueryResultDataGridView.DataSource = null;
QueryResultDataGridView.DataSource = datatable;
}));
});
}
catch (Exception exception)
{
Invoke(new Action(() => {
queryStatus = QueryStatus.NOT_STARTED;
}));
}
}
}
When I click on "Start" button (method ExecuteButton_Click) program fetches data in background and put them into a DataGridView.
// this class starts operations in background
public class OpearationBackgroundWorker
{
public Action<object,DoWorkEventArgs> OnDataFetch { get; set; }
public Action<object, RunWorkerCompletedEventArgs> OnDataFetchCompleted { get; set; }
//public Action<object, ProgressChangedEventArgs> OnDataFetchProgress { get; set; }
private BackgroundWorker backgroundWorker = new BackgroundWorker()
{
WorkerSupportsCancellation = true
};
public void Start()
{
backgroundWorker.DoWork += new DoWorkEventHandler(OnDataFetch);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnDataFetchCompleted);
if ( !backgroundWorker.IsBusy )
backgroundWorker.RunWorkerAsync();
else
throw new Exception("Process running!");
}
public void Stop()
{
backgroundWorker.CancelAsync();
backgroundWorker.Dispose();
}
}
public class SimpleModel : AbstractModel
{
public IDatabaseFetcher Db { get; set; }
public string PostTitle { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public BillingModel(IDatabaseFetcher db)
{
Db = db;
}
public override void Query(Action<DataTable> callback)
{
var query = "SELECT * FROM posts WHERE post_titile = " + PostTitle;
Db.Query = query;
Db.Fetch(data => callback(data));
}
}
The program uses C# using construct to automatically handle the database connection.
public interface IDataFetcher
{
void OnDataFetch(object sender, DoWorkEventArgs e);
void OnDataFetchCompleted(object sender, RunWorkerCompletedEventArgs e);
}
class DatabaseConnector : IDatabaseFetcher
{
private string connectionString = "";
public string Query { get; set; }
public DatabaseConnector(string connectionString)
{
this.connectionString = connectionString;
}
// here i query data from db
public void Fetch(Action<DataTable> action)
{
using (var connection = new OracleConnection(connectionString))
using (var cmd = new OracleCommand(Query, connection))
{
connection.Open();
using (var reader = cmd.ExecuteReader())
{
if (!reader.HasRows)
{
throw new Exception("Empty result!");
}
var dataTable = new DataTable();
dataTable.Load(reader);
// run callback processor
action(dataTable);
}
}
}
}
But now, I want to slightly change the program's behaviour and add the ability manually cancel an operation, and close the connection to the database.
I already know how to cancel the BackgrwoundWorker execution, but I can't find solution for how to manually close connection to database and cancel querying of data running in a background process, when user clicks on the "Stop" button.
How to do this?

C# Adding items to MainForm ListBox from a different class

I'm still fairly new to programming, and have started a project where I'm trying to seperate functionality of the program into classes, where each class handles most everything related to that specific part of the program.
I have one class, called DirectoryMonitors, that creates an object that monitors a directory with FileSystemWatcher. I'm trying to add items to a ListBox on the MainForm from an instance of this DirectoryMonitors class. However, it seems I'm unable to call the method in MainForm unless it's static - but if it's static, I can't access my ListBox.
Relevant part of my DirectoryMonitor class:
public class DirectoryMonitorData
{
public bool WatcherActive { get; set; } = true;
public string EQVersion { get; set; }
public string FolderLocation { get; set; }
}
public class DirectoryMonitor : DirectoryMonitorData
{
private void FolderWatcher_Changed(object sender, FileSystemEventArgs e)
{
FileInfo fi = new FileInfo(e.FullPath);
if (!IsFileLocked(fi))
{
int startPos = e.FullPath.LastIndexOf("\\") + 1;
int endPos = e.FullPath.IndexOf("-Inventory") - startPos;
string character = e.FullPath.Substring(startPos, endPos);
MessageBox.Show(character);
string[] delimiters = { ControlChars.Tab.ToString() };
using (TextFieldParser parser = Microsoft.VisualBasic.FileIO.FileSystem.OpenTextFieldParser(e.FullPath, delimiters))
{
// Process the file's lines.
while (!parser.EndOfData)
{
try
{
string[] fields = parser.ReadFields();
MainForm.addLogToListBox(fields[0]);
for (int i = 1; i <= 5; i++)
{
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
}
private bool IsFileLocked(FileInfo file)
{
...
}
}
Relevant part of my MainForm class:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
public void addLogToListBox(string logMessage)
{
logsListBox.Items.Insert(0, logMessage);
}
}
UPDATED CODE:
public FileSystemWatcher FolderWatcher = new FileSystemWatcher();
public DirectoryMonitor()
{
FolderWatcher.NotifyFilter = NotifyFilters.LastWrite;
FolderWatcher.Filter = "*-Inventory.txt";
FolderWatcher.Changed += FolderWatcher_Changed;
}
public void setupDirectoryMonitorList()
{
foreach (DirectoryMonitorData dmd in MainForm.directoryMonitorList)
{
DirectoryMonitor dm = new DirectoryMonitor()
{
WatcherActive = dmd.WatcherActive,
FolderLocation = dmd.FolderLocation,
EQVersion = dmd.EQVersion
};
if (dm.WatcherActive)
{
dm.FolderWatcher.Path = dm.FolderLocation;
dm.FolderWatcher.EnableRaisingEvents = true;
dm.FolderWatcher.NotifyFilter = NotifyFilters.LastWrite;
dm.FolderWatcher.Filter = "*-Inventory.txt";
dm.FolderWatcher.Changed += FolderWatcher_Changed;
}
MainForm.directoryMonitorObjects.Add(dm);
}
}
Add a property to your DirectoryMonitorData class and pass list box to it:
public class DirectoryMonitorData
{
public bool WatcherActive { get; set; } = true;
public string EQVersion { get; set; }
public string FolderLocation { get; set; }
public ListBox Logs {get; set;}
}
and then:
DirectoryMonitor monitor = new DirectoryMonitor { Logs = logsListBox };
now in your class you can simply add anything to that listbox:
Logs.Items.Add(Something);
The way I normally do that is to add a constructor to the class, that takes a 'MainForm' parameter, then save the 'MainForm' parameter in a field.
public class DirectoryMonitor : DirectoryMonitorData
{
public DirectoryMonitor(MainForm form)
{
this.mainForm = form;
}
private MainForm mainForm;
}
Now you can access all public methods an properties of MainForm by using the field mainForm.
Alternative:
Create an eventhandler in your class (with a custom EventArgs). Then from your 'MainForm', subscribe to that event. Now the class does not have to know anything about the form. You just need to Invoke the eventhandler in your class.

How do I load multiple XML files and read them?

I've a problem. In my code I'm looking for all xml files in one directory. Finally it works but I don't know how to read them. A single one is loaded with the string xmlFile. I think I need to replace or edit that in this way that my code now that xmlFile is not only one file, but all files who are found in the directory.
What should I be doing?
namespace WindowsFormsApplication11
{
public partial class Form1 : Form
{
private const string xmlFile = "C:\Games\games.xml"; // single xml file works
public Form1()
{
this.InitializeComponent();
this.InitializeListView();
this.LoadDataFromXml();
listView.Items.AddRange(Directory.GetFiles("C:\Games\", "*.xml")
.Select(f => new ListViewItem(f))
.ToArray());
}
private void LoadDataFromXml()
{
if (File.Exists(xmlFile))
{
XDocument document = XDocument.Load(xmlFile);
if (document.Root != null)
{
foreach (XElement gameElement in document.Root.Elements("game"))
{
string gamename = gameElement.Element("gamename").Value;
string launchpath = gameElement.Element("launchpath").Value;
string uninstallpath = gameElement.Element("uninstallpath").Value;
string publisher = gameElement.Element("publisher").Value;
// check if gameElement.Element(ELEMENTNAME) is not null
Game game = new Game(gamename, launchpath, uninstallpath, publisher);
AddGameToListView(game);
}
}
}
}
private void AddGameToListView(Game game)
{
ListViewItem item = CreateGameListViewItem(game);
this.listView.Items.Add(item);
}
private ListViewItem CreateGameListViewItem(Game game)
{
ListViewItem item = new ListViewItem(game.Gamename);
item.SubItems.Add(game.Launchpath);
item.SubItems.Add(game.Uninstallpath);
item.SubItems.Add(game.Publisher);
item.Tag = game;
return item;
}
private void InitializeListView()
{
this.listView.View = View.Details;
this.listView.GridLines = true;
this.listView.MultiSelect = false;
this.listView.FullRowSelect = true;
this.listView.Columns.AddRange(new[]
{
new ColumnHeader{Text = "Gamename", Width = 200},
new ColumnHeader{Text = "Launchpath"},
new ColumnHeader{Text = "Uninstallpath"},
new ColumnHeader{Text = "Publisher"}
});
this.listView.MouseDoubleClick += ListViewOnMouseDoubleClick;
}
private void ListViewOnMouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && this.listView.SelectedItems.Count > 0)
{
Game game = (Game)((this.listView.SelectedItems[0].Tag);
try
{
Process.Start(game.Launchpath);
}
catch (Exception ex)
{
MessageBox.Show("Can not start game.\nDetails:\n" + ex.Message, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
}
}
internal class Game
{
public Game()
{
}
public Game(string gamename, string launchpath, string uninstallpath, string publisher)
{
this.Gamename = gamename;
this.Launchpath = launchpath;
this.Uninstallpath = uninstallpath;
this.Publisher = publisher;
}
public string Gamename { get; set; }
public string Launchpath { get; set; }
public string Uninstallpath { get; set; }
public string Publisher { get; set; }
}
}
UPDATE:
This is my current code. how can i send f to LoadDataFromXml ?
public Form1()
{
this.InitializeComponent();
this.InitializeListView();
this.Height = Screen.PrimaryScreen.WorkingArea.Height;
var files = Directory.GetFiles(#"C:\Games\", "*.xml").Select(f => new ListViewItem(f)).ToArray(); listView.Items.AddRange(files);
foreach (var f in files)
{
this.LoadDataFromXml(f);
}
}
private void LoadDataFromXml(//What Do I need to enter here?)
{
foreach (XElement gameElement in f.Root.Elements("game"))
{
string gamename = gameElement.Element("gamename").Value;
string launchpath = gameElement.Element("launchpath").Value;
string portablesave = gameElement.Element("portablesave").Value;
string publisher = gameElement.Element("publisher").Value;
string gameid = gameElement.Element("gameID").Value;
string update = gameElement.Element("update").Value;
// check if gameElement.Element(ELEMENTNAME) is not null
Game game = new Game(gamename, launchpath, portablesave, publisher, gameid, update);
AddGameToListView(game);
}
}
Simple use Directory functions to get all your XML files, and loop through them, by calling LoadDataFromXml for each file. Note: You will need to refactor your code a little bit.
You need to modify your LoadDataFromXml to take file as a parameter. And change your Form1 constructor to something like this
public Form1()
{
this.InitializeComponent();
this.InitializeListView();
var files = Directory.GetFiles("C:\Games\", "*.xml")
.Select(f => new ListViewItem(f))
.ToArray();
listView.Items.AddRange(files);
foreach(var f in files)
{
this.LoadDataFromXml(f);
}
}

Show downloading progress in c#

I have created this program which sends a html request to twitter and downloads tweets into a database. This program is in console and is coded using c#. I am not sure on the displaying the downloading progress of the user information.
Example: I am downloading screenname, location, etc for user alice and am writing it into database
Question: How can I show the user the progress as in 10% completed, something like that.
My code for the web request (small example) as of follows :
WebClient wc = new WebClient();
string url = "https://api.twitter.com/1/users/lookup.json?screen_name=" +username;
It just so happens that I wrote a custom subclass of WebClient that can show update progress. It raises an event every 1MB (because that was my need, see NotifyMegabyteIncrement) but could easily be modified.
public class MyWebClient : WebClient, IDisposable
{
public int Timeout { get; set; }
public int TimeUntilFirstByte { get; set; }
public int TimeBetweenProgressChanges { get; set; }
public long PreviousBytesReceived { get; private set; }
public long BytesNotNotified { get; private set; }
public string Error { get; private set; }
public bool HasError { get { return Error != null; } }
private bool firstByteReceived = false;
private bool success = true;
private bool cancelDueToError = false;
private EventWaitHandle asyncWait = new ManualResetEvent(false);
private Timer abortTimer = null;
const long ONE_MB = 1024 * 1024;
public delegate void PerMbHandler(long totalMb);
public event PerMbHandler NotifyMegabyteIncrement;
public MyWebClient(int timeout = 60000, int timeUntilFirstByte = 30000, int timeBetweenProgressChanges = 15000)
{
this.Timeout = timeout;
this.TimeUntilFirstByte = timeUntilFirstByte;
this.TimeBetweenProgressChanges = timeBetweenProgressChanges;
this.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(MyWebClient_DownloadFileCompleted);
this.DownloadProgressChanged += new DownloadProgressChangedEventHandler(MyWebClient_DownloadProgressChanged);
abortTimer = new Timer(AbortDownload, null, TimeUntilFirstByte, System.Threading.Timeout.Infinite);
}
protected void OnNotifyMegabyteIncrement(long totalMb)
{
if (NotifyMegabyteIncrement != null) NotifyMegabyteIncrement(totalMb);
}
void AbortDownload(object state)
{
cancelDueToError = true;
this.CancelAsync();
success = false;
Error = firstByteReceived ? "Download aborted due to >" + TimeBetweenProgressChanges + "ms between progress change updates." : "No data was received in " + TimeUntilFirstByte + "ms";
asyncWait.Set();
}
void MyWebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
if (cancelDueToError) return;
long additionalBytesReceived = e.BytesReceived - PreviousBytesReceived;
PreviousBytesReceived = e.BytesReceived;
BytesNotNotified += additionalBytesReceived;
if (BytesNotNotified > ONE_MB)
{
OnNotifyMegabyteIncrement(e.BytesReceived);
BytesNotNotified = 0;
}
firstByteReceived = true;
abortTimer.Change(TimeBetweenProgressChanges, System.Threading.Timeout.Infinite);
}
public bool DownloadFileWithEvents(string url, string outputPath)
{
asyncWait.Reset();
Uri uri = new Uri(url);
this.DownloadFileAsync(uri, outputPath);
asyncWait.WaitOne();
return success;
}
void MyWebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (cancelDueToError) return;
asyncWait.Set();
}
protected override WebRequest GetWebRequest(Uri address)
{
var result = base.GetWebRequest(address);
result.Timeout = this.Timeout;
return result;
}
void IDisposable.Dispose()
{
if (asyncWait != null) asyncWait.Dispose();
if (abortTimer != null) abortTimer.Dispose();
base.Dispose();
}
}
private WebClient mWebClient = new WebClient();
//...
mWebClient.DownloadProgressChanged += (sender, e) => progressPercentageChanged(e.ProgressPercentage); //your method to display the percentage
mWebClient.DownloadFileCompleted += (sender, e) => yourMethodToProcessTheFile();
mWebClient.DownloadFileAsync(uri, fileNameOnDisk);
//you need to keep the instance of webclient, so it does not get garbage collected

Categories

Resources