2015-11-04 38 views
0

我試圖使用WebClient.DownloadFileAsync方法下載某些文件。 只要UI未顯示,它就可以正常工作。使用WebClient進行文件下載時UI凍結

UI是一個帶有標籤和ProgressBar的窗體。

在DownloadProgressChanged-事件中,我想顯示當前進度。 爲了做到這一點,我調用一個int參數的方法。 這些方法下載:

private void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 
{ 
    if(progressDialog!=null){ 
     progressDialog.setFileProgress(e.ProgressPercentage); 
    } 
    Trace.WriteLine(String.Format("downloaded {0} of {1} bytes. {2} % complete...", 
     e.BytesReceived, 
     e.TotalBytesToReceive, 
     e.ProgressPercentage)); 
} 


private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) 
{    
    if(progressDialog!=null){    
     progressDialog.setFileProgress(100); 
    } 
    are.Set(); 
} 


private AutoResetEvent are = new AutoResetEvent(false); 
public void DownloadFiles(List<DownloadObject> objects){ 
    Trace.WriteLine("Start Download"); 
    wc.DownloadProgressChanged += DownloadProgressChanged; 
    wc.DownloadFileCompleted += DownloadFileCompleted; 
    try{ 
     foreach(DownloadObject dlo in objects){     
      currentFile = dlo; 
      String url = dlo.DownloadURL; 
      String path = dlo.LocalPath; 
      Uri uri = new Uri(url); 
      //GET 
      Thread thread = new Thread(() => wc.DownloadFileAsync(uri,path)); 
      //thread.SetApartmentState(ApartmentState.STA); 
      thread.Start();     
      are.WaitOne(); 
      DeleteFile(dlo.ID); 
     }    
     Trace.WriteLine("FileDownload finished"); 
    }catch(Exception ex){ 
     Trace.WriteLine("FileDownload failed: "+ex.Message); 
    }finally{ 
     wc.Dispose(); 
    }   
} 

這是相關methodsinside的ProgressDialog表格:

public delegate void dummy(); 
public void setFileProgress(int progress){   
    if(prgFile.InvokeRequired){ 
     Trace.WriteLine("Invoke required"); 
     prgFile.Invoke(new dummy(() => prgFile.Value = progress)); 
    }else{    
     Trace.WriteLine("Invoke not required");    
     prgFile.Value = progress; 
    }   
} 

public static ProgressDialog getInstance(IWin32Window owner){ 
    ProgressDialog pd = new ProgressDialog(); 
    //pd.Show(owner); // 
    return pd; 
} 

現在發生的事情是這樣的: 如果pd.Show()不叫一切正好。進度得到改變,我得到了輸出「不需要調用」以及下載的每一步。

但是,如果pd.Show()被調用時,我得到的輸出「調用需要」多次不之間的任何下載的消息。

所以我調試通過代碼的一部分,似乎progressDialog.setFileProgress()被調用,但DownloadProgressChanged事件直接調用prgFile.Invoke方法後直接觸發。

如果我切換調用,調用BeginInvoke來,我再次得到所有正確的消息,但ProgressDialog凍結,直至所有下載完成後,我就不會顯示任何進展。

我在那裏錯過了什麼?我通讀了大量有關此問題和線索的文章,但無法使其運行。

我使用的SharpDevelop與.net框架4.0

回答

0

因爲你的「DownloadFiles」的方法是同步的,這阻止UI線程。你開始一個新的線程下載文件,但

are.WaitOne(); 

阻塞當前線程(UI線程),直到are設置(在下載完成的事件)。

正確的解決辦法是把這些工作在整個事件下載(例如的DeleteFile)之後。

+0

問題是,DeleteFile方法刪除服務器上的文件。因此,如果DownloadFileAsync尚未完成,我可能會遇到問題。此外,我需要使用Asnyc,因爲如果沒有它,我將無法獲得進度信息,但在刪除文件之前需要等待。我怎樣才能實現這個沒有AutoResetEvent?如果我不等待下載完成,則會出現Web客戶端無法同時下載多個文件的錯誤。 –

+0

某處我讀了應該有一個webclient爲每個文件,但我認爲我的ProgressDialog會爆炸,如果我這樣做。 –

+0

現在我試着把刪除放入完整的事件中,它發生了我的預測。首先我得到了WebClient無法同時下載多個文件的消息。然後我爲每個文件創建了一個新的Web客戶端,這導致了ProgressDialog的解鎖,但是進度條看起來像一個迪斯科舞會。也許我應該重新思考ProgressDialog並在數據網格中顯示多個文件或其他內容。 –

0

由於DownloadFiles -method是同步的,在UI線程正在運行。因此,如果你執行該方法,你的UI線程將被阻塞,直到方法完成。

大約有這兩種方法:

多線程

只是讓在不同的任務中的方法運行將解決你的UI凍結。然而,正如你所提到的,在你的情況下,這將是一個可怕的方法,因爲你不能操縱你的UI跨線程。如果你想進行狀態更新,你不應該這樣做。

異步/等待

這裏的更好的方法。雖然看起來很簡單,但異步/等待實際上很難做到。互聯網上有很多資源,我建議從dotnetperls開始,這是對異步/等待的詳細介紹。這將需要一些時間,但肯定是最好的方法。

相關問題