I have a situation where all I need is a label that switches between "Ready" and "In progress" when a user clicks a button. The label is intially in the "Ready" state. When the user clicks a button, the label should read "In progress" then some tasks need to be performed, like copying files etc. After the tasks are completed successfully the label should once again read "Ready". Right now I am using this piece of code and the label status does not change. How can I make this work. Please help.
private void button1_Click(object sender, EventArgs e)
{
status.Text = "In Progress";
if (listBox1.Items.Count == 0)
{
MessageBox.Show("Please select a file to upload");
}
FtpClient client = new FtpClient("*******", "*******", "******");
string fileName = getFileNameFromPath(listBox1.Items[0].ToString());
string localFile = listBox1.Items[0].ToString();
string remoteFile = "**********/"+fileName;
string link = client.upload(remoteFile, localFile);
listBox1.Items.RemoveAt(0);
textBox1.Text = link;
status.Text = "Ready";
}
You're blocking the UI thread during your long running process, both preventing the UI from updating the text value, or receiving user input, or doing anything for that matter.
You need to do the long running work asynchronously so as to not block the UI thread.
Ideally you'd have an asynchronous method provided by your FtpClient (and even better, it would return a Task). This would allow you to write something like this:
private async void button1_Click(object sender, EventArgs e)
{
status.Text = "In Progress";
if (listBox1.Items.Count == 0)
{
MessageBox.Show("Please select a file to upload");
}
FtpClient client = new FtpClient("*******", "*******", "******");
string fileName = getFileNameFromPath(listBox1.Items[0].ToString());
string localFile = listBox1.Items[0].ToString();
string remoteFile = "**********/" + fileName;
string link = await client.uploadAsync(remoteFile, localFile);
listBox1.Items.RemoveAt(0);
textBox1.Text = link;
status.Text = "Ready";
}
And then you'd be done. If it doesn't provide any asynchronous methods then, as a work around, you can just start up a new task to do the work in the background:
private async void button1_Click(object sender, EventArgs e)
{
status.Text = "In Progress";
if (listBox1.Items.Count == 0)
{
MessageBox.Show("Please select a file to upload");
}
FtpClient client = new FtpClient("*******", "*******", "******");
string fileName = getFileNameFromPath(listBox1.Items[0].ToString());
string localFile = listBox1.Items[0].ToString();
string remoteFile = "**********/" + fileName;
string link = await Task.Run(() => client.upload(remoteFile, localFile));
listBox1.Items.RemoveAt(0);
textBox1.Text = link;
status.Text = "Ready";
}
If you don't have C# 5.0 and .NET 4.5 to be able to use await then you can use a BackgroundWorker:
private void button1_Click(object sender, EventArgs e)
{
status.Text = "In Progress";
if (listBox1.Items.Count == 0)
{
MessageBox.Show("Please select a file to upload");
}
string fileName = getFileNameFromPath(listBox1.Items[0].ToString());
string localFile = listBox1.Items[0].ToString();
string remoteFile = "**********/" + fileName;
var worker = new BackgroundWorker();
worker.DoWork += (s, args) =>
{
FtpClient client = new FtpClient("*******", "*******", "******");
args.Result = client.upload(remoteFile, localFile);
};
worker.RunWorkerCompleted += (s, args) =>
{
listBox1.Items.RemoveAt(0);
textBox1.Text = args.Result as string;
status.Text = "Ready";
};
worker.RunWorkerAsync();
}
Related
Description:
So I have this following script to download a file from the internet with a progress bar showing what percentage the download is at and custom message boxes. Now I have the files being saved to the users %TEMP% path. And it uses events to prevent people from clicking on the button again and starting a new download.
Problem:
I want to give the user a choice of where to save the file, but show his temp path as the default location. (Like a Save-file-dialog Box)
I'm still fairly new to coding and don't know where to exactly start.
What I tried:
I didn't try any new code, but I did go around google and try to find a solution. Here are some websites that I found that might be useful:
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-save-files-using-the-savefiledialog-component
https://www.c-sharpcorner.com/UploadFile/mahesh/savefiledialog-in-C-Sharp/
They explain it really well. But I don't know how to incorporate it into this script.
And I dont want to write a brand new script. Any Help would be Appreciated!
private bool _isBusy = false;
private void button1_Click(object sender, EventArgs e)
=> DownloadFile("someurl1", "somefilename1.exe");
private void button2_Click(object sender, EventArgs e)
=> DownloadFile("someurl2", "somefilename2.exe");
private void button3_Click(object sender, EventArgs e)
=> DownloadFile("someurl3", "somefilename3.exe");
private void button4_Click(object sender, EventArgs e)
=> DownloadFile("someurl4", "somefilename4.exe");
private void DownloadFile(string url, string fileName)
{
if(_isBusy) return;
_isBusy = true;
var output = Path.Combine(Path.GetTempPath(), fileName);
MessageBox.Show($"{fileName} will start downloading from {url}");
using (WebClient client = new WebClient())
{
client.DownloadFileCompleted += (sender, args) =>
{
MessageBox.Show($"{fileName} Complete!");
Process.Start(output);
_isBusy = false;
};
client.DownloadProgressChanged += (sender, args) => progressBar1.Value = args.ProgressPercentage;
client.DownloadFileAsync(new Uri(url), output);
}
}
Maybe something like?
private SaveFileDialog save = new SaveFileDialog();
private void DownloadFile(string url, string fileName)
{
if (_isBusy) return;
save.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
save.FileName = fileName;
if (save.ShowDialog() == DialogResult.OK)
{
_isBusy = true;
var output = save.FileName;
MessageBox.Show($"{fileName} will start downloading from {url}");
using (WebClient client = new WebClient())
{
client.DownloadFileCompleted += (sender, args) =>
{
MessageBox.Show($"{fileName} Complete!");
Process.Start(output);
_isBusy = false;
};
client.DownloadProgressChanged += (sender, args) => progressBar1.Value = args.ProgressPercentage;
client.DownloadFileAsync(new Uri(url), output);
}
}
}
I implemented a function in a windows form application to capture and read needed tabular data from a file (sourcedata.data) and save it in another file (result.data ).
How i and by using the application can capture a real time stream data like such available here :https://data.sparkfun.com/streams in csv or .data file to use it.
Or are there any direct waya to read the stream data directly from the website source periodically ?
private void button5_Click(object sender, EventArgs e)
{
List<string[]> rows = new List<string[]>();
int[] indexes = { 0, 1, 3, 5, 6, 7, 8, 9 };
using (var reader = new StreamReader(#"sourcedata.data"))
{
using (StreamWriter writetext = new StreamWriter("result.data"))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line.IndexOf(',') == -1)
continue;
string[] values = line.Split(',');
string[] row = new string[indexes.Length];
int insertIndex = 0;
for (int i = 0; i < values.Length; i++)
{
string val = values[i];
if (val.Trim() == "?")
goto BREAK;
if (indexes.Contains(i))
row[insertIndex++] = val;
}
rows.Add(row);
writetext.WriteLine(String.Join(",", row));
BREAK:;
}
}
}
}
You have two split your problem into two separated sub problems:
Write a method public static string DownloadData(...) which will download the data from the source. This can be done by any HTTP client or library you can find like System.Net.Http.HttpClient or System.Net.WebClient.
See How to download a file from a URL in C#?
Add/start a timer which calls this method periodically. You can use classes like System.Windows.Forms.Timer or System.Timers.Timer.
See What is the best way to implement a "timer"?
#Progman
It is the code
public partial class Download : Form
{
public Download()
{
InitializeComponent();
}
WebClient client;
private void btnDownload_Click(object sender, EventArgs e)
{
string url = txtUrl.Text;
if (!string.IsNullOrEmpty(url))
{
Thread thread = new Thread(() =>
{
Uri uri = new Uri(url);
string filename = System.IO.Path.GetFileName(uri.AbsolutePath);
client.DownloadFileAsync(uri, Application.StartupPath + "/" + filename);
});
thread.Start();
}
}
private void Download_Load(object sender, EventArgs e)
{
client = new WebClient();
client.DownloadProgressChanged += Client_DownloadProgressChanged;
client.DownloadFileCompleted += Client_DownloadFileCompleted;
}
private void Client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
MessageBox.Show("Download Completed.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Invoke(new MethodInvoker(delegate ()
{
progressBar.Minimum = 0;
double recieve = double.Parse(e.BytesReceived.ToString());
double total = double.Parse(e.TotalBytesToReceive.ToString());
double percentage = recieve / total * 100;
lblStatus.Text = $"Download {string.Format("{0:0.##}", percentage)}%";
progressBar.Value = int.Parse(Math.Truncate(percentage).ToString());
}));
}
}
I want to show the progress of a downloading process on my ProgressBar. I tried to do somethings like this code for upload, but I failed. Here is an example of my failed attempts
private void button5_Click(object sender, EventArgs e)
{
Task.Run(() => Download());
}
private void Download()
{
try
{
int Port = (int)numericUpDown1.Value;
string Host = comboBox1.Text;
string Username = textBox3.Text;
string Password = textBox4.Text;
string SourcePath = textBox5.Text;
string RemotePath = textBox6.Text;
string FileName = textBox7.Text;
using (var file = File.OpenWrite(SourcePath + FileName))
using (var Stream = new FileStream(SourcePath + FileName, FileMode.Open))
using (var Client = new SftpClient(Host, Port, Username, Password))
{
Client.Connect();
progressBar1.Invoke((MethodInvoker)
delegate
{
progressBar1.Maximum = (int)Stream.Length;
});
Client.DownloadFile(RemotePath + FileName, /*file*/ Stream, DownloadProgresBar);
Client.Disconnect();
}
}
catch (Exception Ex)
{
System.Windows.Forms.MessageBox.Show(Ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void DownloadProgresBar(ulong Downloaded)
{
progressBar1.Invoke((MethodInvoker)
delegate
{
progressBar1.Value = (int)Downloaded;
});
}
Thank you in advance
As you correctly did, similarly to the code for displaying progress of file upload, you have to provide a callback to the downloadCallback argument of SftpClient.DownloadFile.
public void DownloadFile(
string path, Stream output, Action<ulong> downloadCallback = null)
Also you correctly download on a background thread. Alternatively, you could use an asynchronous upload (SftpClient.BeginDownloadFile).
What is wrong and needs change:
You have to open/create the local file for writing (FileMode.Create).
You have to retrieve a size of the remote file, not the local one (not existing yet). Use SftpClient.GetAttributes.
Example using a background thread (task):
private void button1_Click(object sender, EventArgs e)
{
// Run Download on background thread
Task.Run(() => Download());
}
private void Download()
{
try
{
int Port = 22;
string Host = "example.com";
string Username = "username";
string Password = "password";
string RemotePath = "/remote/path/";
string SourcePath = #"C:\local\path\";
string FileName = "download.txt";
string SourceFilePath = SourcePath + FileName;
using (var stream = new FileStream(SourceFilePath, FileMode.Create))
using (var client = new SftpClient(Host, Port, Username, Password))
{
client.Connect();
string RemoteFilePath = RemotePath + FileName;
SftpFileAttributes attrs = client.GetAttributes(RemoteFilePath);
// Set progress bar maximum on foreground thread
int max = (int)attrs.Size;
progressBar1.Invoke(
(MethodInvoker)delegate { progressBar1.Maximum = max; });
// Download with progress callback
client.DownloadFile(RemoteFilePath, stream, DownloadProgresBar);
MessageBox.Show("Download complete");
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
private void DownloadProgresBar(ulong uploaded)
{
// Update progress bar on foreground thread
progressBar1.Invoke(
(MethodInvoker)delegate { progressBar1.Value = (int)uploaded; });
}
For upload see:
Displaying progress of file upload in a ProgressBar with SSH.NET
I've got a problem. How can I cancel a download ?
client.CancelAsync();
Doesn't work for me, because if I cancel a download and start a new one the code still tries to access the old download file. You've to know in my code is a part when a download is done it should unzip the file which has been downloaded. Example.zip like this :)
So, when I cancel my download and start a new one you know my script tries to unzip my old Example.zip, but it should kick this ....
For Unzipping, I'm using Iconic.Zip.dll (http://dotnetzip.codeplex.com/)
How to get it work?
UPDATE:
This is my Downloader Form
private void button3_Click_1(object sender, EventArgs e)
{
DialogResult dialogResult = MessageBox.Show("This will cancel your current download ! Continue ?", "Warning !", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
cancelDownload = true;
URageMainWindow.isDownloading = false;
this.Close();
}
else if (dialogResult == DialogResult.No)
{
}
}
This is my Main form this happens when you start downloading something
private void checkInstall(object sender, WebBrowserDocumentCompletedEventArgs e)
{
string input = storeBrowser.Url.ToString();
// Test these endings
string[] arr = new string[]
{"_install.html"};
// Loop through and test each string
foreach (string s in arr)
{
if (input.EndsWith(s) && isDownloading == false)
{
// MessageBox.Show("U.Rage is downloading your game");
Assembly asm = Assembly.GetCallingAssembly();
installID = storeBrowser.Document.GetElementById("installid").GetAttribute("value");
// MessageBox.Show("Name: " + installname + " ID " + installID);
installname = storeBrowser.Document.GetElementById("name").GetAttribute("value");
installurl = storeBrowser.Document.GetElementById("link").GetAttribute("value");
isDownloading = true;
string install_ID = installID;
string Install_Name = installname;
// MessageBox.Show("New Update available ! " + " - Latest version: " + updateversion + " - Your version: " + gameVersion);
string url = installurl;
WebClient client = new WebClient();
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_InstallProgressChanged);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_InstallFileCompleted);
client.DownloadFileAsync(new Uri(url), #"C:\U.Rage\Downloads\" + installID + "Install.zip");
if (Downloader.cancelDownload == true)
{
//MessageBox.Show("Downloader has been cancelled");
client.CancelAsync();
Downloader.cancelDownload = false;
}
notifyIcon1.Visible = true;
notifyIcon1.ShowBalloonTip(2, "Downloading !", "U.Rage is downloading " + installname, ToolTipIcon.Info);
System.Media.SoundPlayer player = new System.Media.SoundPlayer(#"c:/U.Rage/Sounds/notify.wav");
player.Play();
storeBrowser.GoBack();
igm = new Downloader();
igm.labelDWGame.Text = installname;
// this.Hide();
igm.Show();
return;
}
if (input.EndsWith(s) && isDownloading == true)
{
System.Media.SoundPlayer player = new System.Media.SoundPlayer(#"c:/U.Rage/Sounds/notify.wav");
player.Play();
MessageBox.Show("Please wait until your download has been finished", "Warning");
storeBrowser.GoBack();
}
}
}
This happens when the download has been finished
void client_InstallFileCompleted(object sender, AsyncCompletedEventArgs e)
{
if(Downloader.cancelDownload == false)
{
using (ZipFile zip = ZipFile.Read(#"C:\U.Rage\Downloads\" + installID + "Install.zip"))
{
//zip.Password = "iliketrains123";
zip.ExtractAll("C:/U.Rage/Games/", ExtractExistingFileAction.OverwriteSilently);
}
System.IO.File.Delete(#"C:/U.Rage/Downloads/" + installID + "Install.zip");
notifyIcon1.Visible = true;
notifyIcon1.ShowBalloonTip(2, "Download Completed !", "Installing was succesful !", ToolTipIcon.Info);
System.Media.SoundPlayer player = new System.Media.SoundPlayer(#"c:/U.Rage/Sounds/notify.wav");
player.Play();
this.Show();
igm.Close();
isDownloading = false;
listView.Items.Clear();
var files = Directory.GetFiles(#"C:\U.Rage\Games\", "*.ugi").Select(f => new ListViewItem(f)).ToArray();
foreach (ListViewItem f in files)
{
this.LoadDataFromXml(f);
}
}
}
Here is the method for async data download that supports cancellation:
private static async Task<byte[]> downloadDataAsync(Uri uri, CancellationToken cancellationToken)
{
if (String.IsNullOrWhiteSpace(uri.ToString()))
throw new ArgumentNullException(nameof(uri), "Uri can not be null or empty.");
if (!Uri.IsWellFormedUriString(uri.ToString(), UriKind.Absolute))
return null;
byte[] dataArr = null;
try
{
using (var webClient = new WebClient())
using (var registration = cancellationToken.Register(() => webClient.CancelAsync()))
{
dataArr = await webClient.DownloadDataTaskAsync(uri);
}
}
catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled)
{
// ignore this exception
}
return dataArr;
}
When you call CancelAsync, the AsyncCompletedEventArgs object passed to the completed callback will have the Cancelled property set to true. So you could write:
void client_InstallFileCompleted(object sender, AsyncCompletedEventArgs e)
{
if(e.Cancelled)
{
// delete the partially-downloaded file
return;
}
// unzip and do whatever...
using (ZipFile zip = ZipFile.Read(#"C:\U.Rage\Downloads\" + installID + "Install.zip"))
See the documentation for more info.
The selected answer didn't work properly for me. Here's what I did:
When they click the cancel button I call the
Client.CancelAsync();
And then in the Web.Client DownloadFileCompleted:
Client.DownloadFileCompleted += (s, e) =>
{
if (e.Cancelled)
{
//cleanup delete partial file
Client.Dispose();
return;
}
}
And then when you try to re-download just instantiate a new client:
Client = WebClient();
This way the old async parameters won't be maintained.
Eventhough i specify a different location the file gets saved in mydocuments. How to resolve this issue. Pls share your ideas if any.Here is the code.
if (externalButton.Checked == true)
{
// int i = 1;
saveFileDialog.Title = "Save the Proofer Report";
saveFileDialog.Filter = "Document Files (*.doc)|*.doc|Document Files (*.docx)|*.docx";
saveFileDialog.FilterIndex = 0;
saveFileDialog.InitialDirectory = "MyDocuments";
saveFileDialog.FileName = "Proofer Report -- " + Path.GetFileName((string)fileName) + ".doc";
//i.tostring()
saveFileDialog.DefaultExt = ".doc";
saveFileDialog.ShowHelp = true;
// saveFileDialog.ShowDialog();
var thread = new Thread(new ParameterizedThreadStart(param => { saveFileDialog.ShowDialog(); }));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
fname = saveFileDialog.FileName;
You are showing dialog assynchronously on new thread and code after starting the thread executes before dialog is shown (most of the time).
Either wait for thread completion or move saving to that thread after dialog is closed.
Why You Are Showing saveFileDialog in different thread?
if you show save dialog in diffrent thread fname = saveFileDialog.FileName; is always return null.dont use separate thread.or Call this event after thread start
saveFileDialog1.FileOk += new CancelEventHandler(saveFileDialog1_FileOk);
void saveFileDialog1_FileOk(object sender, CancelEventArgs e)
{
string fname = null;
fname = saveFileDialog1.FileName;
}
Edited
Example
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
_SaveFileDialog.FileOk += new CancelEventHandler(_SaveFileDialog_FileOk);
}
string filename = null;
SaveFileDialog _SaveFileDialog = new SaveFileDialog();
private void savebtn_Click(object sender, EventArgs e)
{
_SaveFileDialog.Title = "Save the Proofer Report";
_SaveFileDialog.Filter = "Document Files (*.doc)|*.doc|Document Files (*.docx)|*.docx";
_SaveFileDialog.FilterIndex = 0;
_SaveFileDialog.InitialDirectory = "MyDocuments";
_SaveFileDialog.FileName = "Proofer Report -- .doc";
_SaveFileDialog.DefaultExt = ".doc";
_SaveFileDialog.ShowHelp = true;
var thread = new Thread(new ParameterizedThreadStart(param => { _SaveFileDialog.ShowDialog(); }));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
void _SaveFileDialog_FileOk(object sender, CancelEventArgs e)
{
filename = _SaveFileDialog.FileName;
}
}