歡迎堆棧溢出...
通過檢查你的循環很明顯,可以有效地防止循環語句中的UI線程。
while (!downloader.IsAlive) ;
while (!downloadFavIcon_Completed) ;
是您的UI鎖定的原因。理想情況下,這對於後臺工作人員來說是一份工作,因爲它被設計爲在後臺運行,並提供事件以便返回到UI線程。這可以使用Thread編寫,但是應該使用後臺工作器。
現在,如果你真的想用Thread
對象來寫這個,那麼我建議你爲你的下載器創建一個類並創建事件。因此,我們可以寫簡單的事件,如
- Completed事件
- 取消事件
- UI進度事件
我不推薦這種方法(讀進一步下跌)
簡單首先創建一個新類Downloader
,這裏是一個示例(最小值)
public class Downloader
{
/// <summary>
/// Delegate Event Handler for the downloading progress
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void DownloaderProgressEventHandler(Downloader sender, DownloaderProgressEventArgs e);
/// <summary>
/// Delegate Event Handler for the completed event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void DownloaderCompletedEventHandler(Downloader sender, DownloaderCompletedEventArgs e);
/// <summary>
/// The completed event
/// </summary>
public event DownloaderCompletedEventHandler Completed;
/// <summary>
/// The cancelled event
/// </summary>
public event EventHandler Cancelled;
/// <summary>
/// the progress event
/// </summary>
public event DownloaderProgressEventHandler Progress;
/// <summary>
/// the running thread
/// </summary>
Thread thread;
/// <summary>
/// the aborting flag
/// </summary>
bool aborting = false;
//the addresses
String[] addr = new String[] {
"http://google.com/favicon.ico",
"http://microsoft.com/favicon.ico",
"http://freesfx.com/favicon.ico",
"http://yahoo.com/favicon.ico",
"http://downloadha.com/favicon.ico",
"http://hp.com/favicon.ico",
"http://bing.com/favicon.ico",
"http://webassign.com/favicon.ico",
"http://youtube.com/favicon.ico",
"https://twitter.com/favicon.ico",
"http://cc.com/favicon.ico",
"http://stackoverflow.com/favicon.ico",
"http://vb6.us/favicon.ico",
"http://facebook.com/favicon.ico",
"http://flickr.com/favicon.ico",
"http://linkedin.com/favicon.ico",
"http://blogger.com/favicon.ico",
"http://blogfa.com/favicon.ico",
"http://metal-archives.com/favicon.ico",
"http://wordpress.com/favicon.ico",
"http://metallica.com/favicon.ico",
"http://wikipedia.org/favicon.ico",
"http://visualstudio.com/favicon.ico",
"http://evernote.com/favicon.ico"
};
/// <summary>
/// Starts the downloader
/// </summary>
public void Start()
{
if (this.aborting)
return;
if (this.thread != null)
throw new Exception("Already downloading....");
this.aborting = false;
this.thread = new Thread(new ThreadStart(runDownloader));
this.thread.Start();
}
/// <summary>
/// Starts the downloader
/// </summary>
/// <param name="addresses"></param>
public void Start(string[] addresses)
{
if (this.aborting)
return;
if (this.thread != null)
throw new Exception("Already downloading....");
this.addr = addresses;
this.Start();
}
/// <summary>
/// Aborts the downloader
/// </summary>
public void Abort()
{
if (this.aborting)
return;
this.aborting = true;
this.thread.Join();
this.thread = null;
this.aborting = false;
if (this.Cancelled != null)
this.Cancelled(this, EventArgs.Empty);
}
/// <summary>
/// runs the downloader
/// </summary>
void runDownloader()
{
Bitmap favCollection = new Bitmap(96, 64);
Graphics g = Graphics.FromImage(favCollection);
for (var i = 0; i < this.addr.Length; i++)
{
if (aborting)
break;
using (System.Net.WebClient client = new System.Net.WebClient())
{
try
{
byte[] dl = client.DownloadData(addr[i]);
using (var stream = new MemoryStream(dl))
{
using (var dlImg = new Bitmap(stream))
{
g.DrawImage(dlImg, (i % 6) * 16, (i/6) * 16, 16, 16);
}
}
}
catch (Exception)
{
using (var dlImg = new Bitmap(Properties.Resources.defaultFacIcon))
{
g.DrawImage(dlImg, (i % 6) * 16, (i/6) * 16, 16, 16);
}
}
}
if (aborting)
break;
if (this.Progress != null)
this.Progress(this, new DownloaderProgressEventArgs
{
Completed = i + 1,
Total = this.addr.Length
});
}
if (!aborting && this.Completed != null)
{
this.Completed(this, new DownloaderCompletedEventArgs
{
Bitmap = favCollection
});
}
this.thread = null;
}
/// <summary>
/// Downloader progress event args
/// </summary>
public class DownloaderProgressEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the completed images
/// </summary>
public int Completed { get; set; }
/// <summary>
/// Gets or sets the total images
/// </summary>
public int Total { get; set; }
}
/// <summary>
/// Downloader completed event args
/// </summary>
public class DownloaderCompletedEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the bitmap
/// </summary>
public Bitmap Bitmap { get; set; }
}
}
現在這是代碼的分配,但讓我們快速看看它。首先我們爲我們的Completed和Progress事件定義了2個代表。這些代表接受下載器實例作爲底部列出的發件人和特殊事件參數類。隨後是我們的3個事件(如上所列),這些事件將用於向下載器發出信號變化。
接下來我們定義了我們的字段。
Thread thread;
這是對調用'Start()`方法時將創建的線程的引用。
bool aborting = false;
這是一個簡單的標誌,用來向線程表示我們應該中止的信號。現在我決定使用一個標誌,讓線程正常完成,而不是調用Thread.Abort()
方法。這確保了所有的清理工作都可以正常進行。
string[] addres =....
我們的初始地址。
到目前爲止,這一切都很簡單。接下來是我們的Start()
方法。我提供了兩種不同的方法。其中一種方法接受一個新的地址下載(不重要)string[]
。
您將在此方法
/// <summary>
/// Starts the downloader
/// </summary>
public void Start()
{
if (this.aborting)
return;
if (this.thread != null)
throw new Exception("Already downloading....");
this.aborting = false;
this.thread = new Thread(new ThreadStart(runDownloader));
this.thread.Start();
}
我們做的第一件事是檢查是否中止標誌設置通知。如果它是忽略開始呼叫(你可以拋出異常)。接下來我們檢查線程是否不爲空。如果線程不爲空,那麼我們的下載程序正在運行。最後,我們簡單地將我們的中止標誌重置爲false
並開始我們的新Thread
。
向下移動到Abort()
方法。該方法將首先檢查是否設置了aborting
標誌。如果是這樣,那麼什麼都不做。接下來,我們將aborting
標誌設置爲true。下一步,我會提醒你這個將阻止你的調用線程調用方法Thread.Join()
這將加入到我們的調用線程的線程。基本上等待線程退出。
最後,我們只需將線程實例設置爲空,將aborting
標誌重置爲false並觸發Cancelled
事件(如果訂閱)。
接下來是下載的主要方法。首先你會注意到我移動了你的變量,並使用using
語句來處理一次性對象。 (這是另一個話題)。
runDownloader()
方法的一大特點是它會定期檢查'中止'標誌。如果此標誌設置爲true
,則downloader
停在此處。現在請注意,您可能會遇到在WebClient
下載圖像時調用中止的情況。理想情況下,您會讓WebClient
完成請求,正確處置然後退出循環。
每次下載圖像後,都會觸發進度事件(如果訂閱)。最後,當迭代完成並下載所有圖像時,「已完成」事件將與編譯後的位圖圖像一起觸發。
停頓了BREATHE
現在,這是所有偉大的..但你如何使用它。簡單地說,我使用按鈕,進度條和圖片框創建了一個表單。該按鈕將用於啓動和停止下載器,進度條處理進度事件和完成圖像的圖片框。
這裏是我已經評論過它的一部分的示例程序。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.progressBar1.Visible = false;
this.progressBar1.Enabled = false;
}
Downloader downloader;
/// <summary>
/// starts \ stop button pressed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
//if downloader is not null then abort it
if (downloader != null)
{
downloader.Abort();
return;
}
//setup and start the downloader
this.progressBar1.Value = 0;
this.progressBar1.Minimum = 0;
this.progressBar1.Enabled = true;
this.progressBar1.Visible = true;
this.downloader = new Downloader();
this.downloader.Progress += downloader_Progress;
this.downloader.Completed += downloader_Completed;
this.downloader.Cancelled += downloader_Cancelled;
this.downloader.Start();
}
/// <summary>
/// downloader cancelled event handler
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void downloader_Cancelled(object sender, EventArgs e)
{
this.unhookDownloader();
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate
{
this.progressBar1.Enabled = false;
this.progressBar1.Visible = false;
MessageBox.Show(this, "Cancelled");
});
else
{
this.progressBar1.Enabled = false;
this.progressBar1.Visible = false;
MessageBox.Show(this, "Cancelled");
}
}
/// <summary>
/// downloader completed event handler
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void downloader_Completed(Downloader sender, Downloader.DownloaderCompletedEventArgs e)
{
this.unhookDownloader();
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate
{
this.progressBar1.Enabled = false;
this.progressBar1.Visible = false;
this.pictureBox1.Image = e.Bitmap;
MessageBox.Show(this, "Completed");
});
else
{
this.progressBar1.Enabled = false;
this.progressBar1.Visible = false;
this.pictureBox1.Image = e.Bitmap;
MessageBox.Show(this, "Completed");
}
}
/// <summary>
/// downloader progress event handler
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void downloader_Progress(Downloader sender, Downloader.DownloaderProgressEventArgs e)
{
if (this.progressBar1.InvokeRequired)
this.progressBar1.Invoke((MethodInvoker)delegate
{
this.progressBar1.Value = e.Completed;
this.progressBar1.Maximum = e.Total;
});
else
{
this.progressBar1.Value = e.Completed;
this.progressBar1.Maximum = e.Total;
}
}
/// <summary>
/// unhooks the events handlers and sets the downloader to null
/// </summary>
void unhookDownloader()
{
this.downloader.Progress -= downloader_Progress;
this.downloader.Completed -= downloader_Completed;
this.downloader.Cancelled -= downloader_Cancelled;
this.downloader = null;
}
}
這是一個簡單的實現,你怎麼可以使用Thread
對象做你的工作。在我看來,這是太多的工作。現在讓我們在後臺工作人員中完成。
我強烈推薦這種方法
爲什麼你可能會說什麼?那麼後臺工作者向我們提供的測試和支持方法(加上更多),我們試圖實現。你會注意到下載圖像和檢測取消的實際工作是相同的。但是您會注意到,在發佈已完成和進度事件時,我並不擔心(儘可能多)有關跨線程問題。
private void button2_Click(object sender, EventArgs e)
{
if (this.worker != null && this.worker.IsBusy)
{
this.worker.CancelAsync();
return;
}
string[] addr = new string[] {".... our full addresss lists" };
this.progressBar1.Maximum = addr.Length;
this.progressBar1.Value = 0;
this.progressBar1.Visible = true;
this.progressBar1.Enabled = true;
this.worker = new BackgroundWorker();
this.worker.WorkerSupportsCancellation = true;
this.worker.WorkerReportsProgress = true;
this.worker.ProgressChanged += (s, args) =>
{
this.progressBar1.Value = args.ProgressPercentage;
};
this.worker.RunWorkerCompleted += (s, args) =>
{
this.progressBar1.Visible = false;
this.progressBar1.Enabled = false;
if (args.Cancelled)
{
MessageBox.Show(this, "Cancelled");
worker.Dispose();
worker = null;
return;
}
var img = args.Result as Bitmap;
if (img == null)
{
worker.Dispose();
worker = null;
return;
}
this.pictureBox1.Image = img;
MessageBox.Show(this, "Completed");
worker.Dispose();
worker = null;
};
this.worker.DoWork += (s, args) =>
{
Bitmap favCollection = new Bitmap(96, 64);
Graphics g = Graphics.FromImage(favCollection);
for (var i = 0; i < addr.Length; i++)
{
if (worker.CancellationPending)
break;
using (System.Net.WebClient client = new System.Net.WebClient())
{
try
{
byte[] dl = client.DownloadData(addr[i]);
using (var stream = new MemoryStream(dl))
{
using (var dlImg = new Bitmap(stream))
{
g.DrawImage(dlImg, (i % 6) * 16, (i/6) * 16, 16, 16);
}
}
}
catch (Exception)
{
using (var dlImg = new Bitmap(Properties.Resources.defaultFacIcon))
{
g.DrawImage(dlImg, (i % 6) * 16, (i/6) * 16, 16, 16);
}
}
}
if (worker.CancellationPending)
break;
this.worker.ReportProgress(i);
}
if (worker.CancellationPending)
{
g.Dispose();
favCollection.Dispose();
args.Cancel = true;
return;
}
args.Cancel = false;
args.Result = favCollection;
};
worker.RunWorkerAsync();
}
我希望這有助於理解一些可能的方法來實現您想實現的目標。
-Nico
應該存在運行時錯誤,因爲您正在從後臺線程'downloader'訪問UI元素,這是不允許的。你如何處理這個問題?你是否通過設置'this.CheckForIllegalCrossThreadCalls = false'來壓制它? – kennyzx
什麼UI元素?你在談論passAddDisplay嗎? –
是的,我的意思是Picturebox。 – kennyzx