2015-11-16 29 views
1

我是wpf newb,因此此問題可能很小。 我想從一個文件夾複製文件到另一個文件夾。我想在複製過程中顯示一個進度條。WPF在異步文件複製期間顯示進度欄

我的代碼是這樣的:

if (!System.IO.File.Exists(basemapDest)) 
{ 
    await Copier.CopyFiles(new Dictionary<string, string> 
    { 
     {basemapSrc, basemapDest}, 
    }, prog => prgBaseMap.Value = prog); 
} 

public static class Copier 
{ 
    public static async Task CopyFiles(Dictionary<string, string> files, Action<int> progressCallback) 
    { 
     for (var x = 0; x < files.Count; x++) 
     { 
      var item = files.ElementAt(x); 
      var from = item.Key; 
      var to = item.Value; 

      using (var outStream = new FileStream(to, FileMode.Create, FileAccess.Write, FileShare.Read)) 
      { 
       using (var inStream = new FileStream(from, FileMode.Open, FileAccess.Read, FileShare.Read)) 
       { 
        long fileLength = from.Length; 
        await inStream.CopyToAsync(outStream); 
       } 
      } 

      progressCallback((int)((x + 1)/files.Count) * 100); 
     } 
    } 
} 

我的XAML代碼:

<StackPanel> 
    <ProgressBar x:Name="prgBaseMap" Height="10" Visibility="Collapsed"/> 
</StackPanel> 

儘管這適用於報告被複制它不顯示進度,而我做了副本的文件。我究竟做錯了什麼 ?

***編輯,這不是stream.copyto with progress bar reporting

引用的問題是使用這些天被很多人視爲過時,一個BackgroundWorker的副本。這個問題是關於使用.NET的新異步模型。我希望提供的解決方案對其他人也有用。

+0

什麼是'prgBaseMap'?你錯過了那個類的'INotifyPropertyChanged'實現嗎? – torvin

+1

你想要顯示什麼樣的進展?您發佈的代碼將在每個文件被複制後更新進度。您只複製一個文件,所以我不希望在屏幕上看到任何有用的進度顯示。如果您希望在文件複製過程中更新進度,則必須使用更明確的方法進行復制,並在文件的較小塊被讀取並寫入新文件時更新進度。 –

+0

不幸的是,你提出的問題並不十分清楚。請提供可靠地重現問題的[良好,_minimal_,_complete_代碼示例](http://stackoverflow.com/help/mcve),以及該代碼的具體說明以及與您想要的不同之處去做。 –

回答

4

這裏是一個解決方案,讓您的文件被複制顯示進度:

public static class Copier 
{ 
    public static async Task CopyFiles(Dictionary<string, string> files, Action<double> progressCallback) 
    { 
     long total_size = files.Keys.Select(x => new FileInfo(x).Length).Sum(); 

     long total_read = 0; 

     double progress_size = 10000.0; 

     foreach(var item in files) 
     { 
      long total_read_for_file = 0; 

      var from = item.Key; 
      var to = item.Value; 

      using (var outStream = new FileStream(to, FileMode.Create, FileAccess.Write, FileShare.Read)) 
      { 
       using (var inStream = new FileStream(from, FileMode.Open, FileAccess.Read, FileShare.Read)) 
       { 
        await CopyStream(inStream , outStream, x => 
        { 
         total_read_for_file = x; 
         progressCallback(((total_read + total_read_for_file)/ (double)total_size) * progress_size); 
        }); 
       } 
      } 

      total_read += total_read_for_file; 
     } 
    } 

    public static async Task CopyStream(Stream from, Stream to, Action<long> progress) 
    { 
     int buffer_size = 10240; 

     byte[] buffer = new byte[buffer_size]; 

     long total_read = 0; 

     while (total_read < from.Length) 
     { 
      int read = await from.ReadAsync(buffer, 0, buffer_size); 

      await to.WriteAsync(buffer, 0, read); 

      total_read += read; 

      progress(total_read); 
     } 
    } 

} 

而且你可以使用它像這樣:

var dictionary = new Dictionary<string, string> 
{ 
    {"c:\\source_file1.dat", "c:\\destination_file1.dat"}, 
    {"c:\\source_file2.dat", "c:\\destination_file2.dat"}, 
}; 

prgBaseMap.Maximum = 10000.0; 

await Copier.CopyFiles(dictionary, prog => prgBaseMap.Value = prog); 

此解決方案通過手動複製文件內容每次10k字節(CopyStream方法)。每次更新進度條。

在開始時,它總和源文件的總長度以便能夠計算相對進度。

CopyFiles方法將通過調用返回相關的進度來向調用者報告進度,該進度相對於10000.0。這就是爲什麼進度條需要達到10000.0的最大值。

除了使用雙值的10000.0,您可以使用輸入文件的長度之和。這使您可以報告複製字節的總數。

在這種情況下,您將不得不計算呼叫者的長度總和。

+0

謝謝,這件作品很有魅力。我只是期待一個指針或提示,一個完美的工作解決方案和一個很好的解釋真的使我的一天。謝謝,我希望這對任何對未來異步複製文件感興趣的人都有幫助。 – w2olves