2010-04-09 101 views
3

我已经写了一个应用程序,将两个文件夹同步到一起。该程序的问题在于它在复制文件时停止响应。快速搜索堆栈溢出告诉我,我需要使用称为后台工作者的东西。我已经在网上阅读了几页关于此的内容,但是由于我对编程很陌生,所以很难理解。我怎样才能简单地将所有的File.Copy(....)命令放入他们自己的后台工作器中(如果这甚至是它的工作原理)?下面是运行子过程的按钮单击事件代码和我希望在所有File.Copy行上使用后台工作程序的子过程。无法让我的头在.NET中的后台工作人员

任何帮助将大大赞赏,因为在此之后,程序会几乎完成:d

编辑

使用德维尔的意见,但是,下面我不断收到follwoing错误,我已经改变了我相应的代码:

错误CS1061:类型Gtk.ProgressBar' does not contain a definition for InvokeRequired '没有扩展方法InvokeRequired' of type Gtk.ProgressBar' 可以找到(是否缺少using指令或程序集引用?)(CS1061)(SYNC-GUI V2)

错误CS1061:Gtk.ProgressBar' does not contain a definition for类型的BeginInvoke '和没有扩展方法BeginInvoke' of type Gtk.ProgressBar' 可以找到(是否缺少using指令或程序集引用?)(CS1061)(SYNC-GUI V2)

下面是我的码。

按钮单击事件:

protected virtual void OnBtnSyncClicked (object sender, System.EventArgs e) 
{ 
    //sets progress bar to 0 
    prgProgressBar.Fraction = 0; 

    //resets values used by progressbar 
    dblCurrentStatus = 0; 
    dblFolderSize = 0; 

    //tests if user has entered the same folder for both target and destination 
    if (fchFolder1.CurrentFolder == fchFolder2.CurrentFolder) 
    { 
     //creates message box 
     MessageDialog msdSame = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Close, "You cannot sync two folders that are the same"); 
     //sets message box title 
     msdSame.Title="Error"; 
     //sets respone type 
     ResponseType response = (ResponseType) msdSame.Run(); 
     //if user clicks on close button or closes window then close message box 
     if (response == ResponseType.Close || response == ResponseType.DeleteEvent) { 
      msdSame.Destroy(); 
     } 
     return; 
    } 

    //tests if user has entered a target folder that is an extension of the destination folder 
    // or if user has entered a desatination folder that is an extension of the target folder 
    if (fchFolder1.CurrentFolder.StartsWith(fchFolder2.CurrentFolder) || fchFolder2.CurrentFolder.StartsWith(fchFolder1.CurrentFolder)) 
    { 
     //creates message box 
     MessageDialog msdContains = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Close, "You cannot sync a folder with one of its parent folders"); 
     //sets message box title 
     msdContains.Title="Error";   
     //sets respone type and runs message box 
     ResponseType response = (ResponseType) msdContains.Run(); 
     //if user clicks on close button or closes window then close message box 
     if (response == ResponseType.Close || response == ResponseType.DeleteEvent) 
     { 
      msdContains.Destroy(); 
     } 
     return; 
    } 

    //creates background worker 
    BackgroundWorker bwBackgroundWorker = new BackgroundWorker(); 
    bwBackgroundWorker.DoWork += new DoWorkEventHandler(bwBackgroundWorkerDoWorkFolder1); 
    //starts background worker 
    bwBackgroundWorker.RunWorkerAsync(); 

    //informs user process is complete 
    prgProgressBar.Text = "Finished"; 
} 

背景工人做工作事件:

private void bwBackgroundWorkerDoWorkFolder1 (object sender, DoWorkEventArgs e) 
{ 
    //Gets total file size of folder 1 
    TotalFileSizeFolder1(fchFolder1.CurrentFolder); 
    //Syncs folder 1 
    SyncFolder1(fchFolder1.CurrentFolder, fchFolder2.CurrentFolder); 
} 

TotalFileSizeFolder1子程序:

protected void TotalFileSizeFolder1 (string strCurrentDirectory) 
{ 
    //inform user that file sizes are being gathered 
    if (prgProgressBar.InvokeRequired) 
    { 
     prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Text="Getting total size of " + strCurrentDirectory;})); 
    } 

    //string array of all the directories in directory 
    string[] staAllDirectories = Directory.GetDirectories(strCurrentDirectory); 
    //string array of all the files in directory 
    string[] staAllFiles = Directory.GetFiles(strCurrentDirectory); 

    foreach (string strFile in staAllFiles) 
    { 
     //saves new file info called FileSize 
     FileInfo FileSize = new FileInfo(strFile); 
     //adds file size 
     dblFolderSize = dblFolderSize + FileSize.Length; 
     //pulses progress bar to indicate some movement 
     if (prgProgressBar.InvokeRequired) 
    { 
     prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Pulse();})); 
    } 

    } 


    foreach (string strDirectory in staAllDirectories) 
    { 
     TotalFileSize(strDirectory); 
    } 
    //delete text from progress bar 

    if (prgProgressBar.InvokeRequired) 
    { 
     prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Text="";})); 
    } 

} 

SyncFolder1子程序:

protected void SyncFolder1 (string strFolder1, string strFolder2) 
{ 
    //string array of all the directories in directory 
    string[] staAllDirectories = Directory.GetDirectories(strFolder1); 
    //string array of all the files in directory 
    string[] staAllFiles = Directory.GetFiles(strFolder1); 

    //loop over each file in directory 
    foreach (string strFile in staAllFiles) 
    { 
     //string of just the file's name and not its path 
     string strFileName = System.IO.Path.GetFileName(strFile); 
     //string containing directory in target folder 
     string strDirectoryInsideFolder1 = System.IO.Path.GetDirectoryName(strFile).Substring(strFolder1.Length); 

     //inform user as to what file is being copied 
     if (prgProgressBar.InvokeRequired) 
     { 
      prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Text="Syncing " + strFile;})); 
     } 

     //tests if file does not exist in destination folder 
     if (!File.Exists(fchFolder2.CurrentFolder + "/" + strDirectoryInsideFolder1 + "/" + strFileName)) 
     { 
      //if file does not exist copy it to destination folder, the true below means overwrite if file already exists 
      File.Copy (strFile, strFolder2 + "/" + strDirectoryInsideFolder1 + "/" + strFileName, true); 
     } 

     //tests if file does exist in destination folder 
     if (File.Exists(strFolder2 + "/" + strDirectoryInsideFolder1 + "/" + strFileName)) 
     { 
      //long (number) that contains date of last write time of target file 
      long lngFolder1FileDate = File.GetLastWriteTime(strFile).ToFileTime(); 
      //long (number) that contains date of last write time of destination file 
      long lngFolder2FileDate = File.GetLastWriteTime(strFolder2 + "/" + strDirectoryInsideFolder1 + "/" + strFileName).ToFileTime(); 

      //tests if target file is newer than destination file 
      if (lngFolder1FileDate > lngFolder2FileDate) 
      { 
       //if it is newer then copy file from target folder to destination folder 
       File.Copy (strFile, strFolder2 + "/" + strDirectoryInsideFolder1 + "/" + strFileName, true); 
      } 
     } 
     //gets current file size 
     FileInfo FileSize = new FileInfo(strFile); 
     //sets file's filesize to dblCurrentStatus and adds it to current total of files 
     dblCurrentStatus = dblCurrentStatus + FileSize.Length; 
     double dblPercentage = dblCurrentStatus/dblFolderSize; 
     if (prgProgressBar.InvokeRequired) 
     { 
      prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Fraction = dblPercentage;})); 
     } 
    } 

    //loop over each folder in target folder 
    foreach (string strDirectory in staAllDirectories) 
    { 
     //string containing directories inside target folder but not any higher directories 
     string strDirectoryInsideFolder1 = strDirectory.Substring(strFolder1.Length); 
     //tests if directory does not exist inside destination folder 
     if (!Directory.Exists(strFolder2 + "/" + strDirectoryInsideFolder1)) 
     { 
      //it directory does not exisit create it 
      Directory.CreateDirectory(strFolder2 + "/" + strDirectoryInsideFolder1); 
     } 
     //run sync on all files in directory 
     SyncFolders(strDirectory, strFolder2); 
    } 

} 
+1

不要忘记考虑它对您的用户界面的影响。偶尔检查BackgroundWorker是否仍在运行(在UI线程上有一个System.Windows.Forms.Timer),以及何时完成,让用户知道UI。你会同时允许多个后台任务吗?长期来看,应该考虑如何处理后台线程中的错误并告知用户。 – 2010-04-09 14:06:31

回答

7

初始化您的后台工作对象

BackgroundWorker bw = new BackgroundWorker(); 
bw.DoWork += new DoWorkEventHandler(bw_DoWork); 

使用此代码

bw.RunWorkerAsync(); // Calls the bw_DoWork method 

//runs SyncTarget procedure  
SyncTarget(fchTarget.CurrentFolder); 
//gets folder size of destination folder    
FileSizeOfDestination(fchDestination.CurrentFolder);    

的地方定义的DoWork方法

private void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 
    SyncTarget(fchTarget.CurrentFolder); 
    FileSizeOfDestination(fchDestination.CurrentFolder); 
} 

我不认为在这里使用两个后台工作人员是必要的,因为这两种方法都涉及IO操作。

你也可以很好地利用RunWorkerCompleted和ProgressChanged

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

而且我也看到你访问的UI元素在你的SyncTarget方法。你不能从另一个线程访问你的UI元素。你必须使用BeginInvoke方法来完成这项

if (prgProgressBar.InvokeRequired) 
{ 
    prgProgressBar.BeginInvoke(new MethodInvoker(delegate { prgProgressBar.Text="Syncing " + strFile; })); 
} 

您也可以做到这一点使用dispatcher

Dispatcher UIDispatcher = Dispatcher.CurrentDispatcher; // Use this code in the UI thread 

UIDispatcher.Invoke(DispatcherPriority.Normal, new Action(() => 
{ 
    // access your prgProgressBar here 
})); 

对于调度程序和跨线程操作,SO中存在很多问题。你可以浏览它们。

我用prgProgressBar作为例子。但我建议你在ProgressChanged方法中使用进度条。

bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged); 

private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    prgProgressBar.Value = e.ProgressPercentage; 
} 
+0

嗨我已经设置我的程序使用后台工作人员,并使用您提到的if语句与后台工作人员的进度条对话,但是所有if语句都会出现以下两个错误: – Connel 2010-04-14 12:35:47

+0

错误CS1061:类型'Gtk.ProgressBar'不包含'InvokeRequired'的定义,并且没有找到'Gtk.ProgressBar'类型的扩展方法'InvokeRequired'(你是否缺少使用指令或程序集引用?)(CS1061)(Sync- GUI v2) 和 和 错误CS1061:类型'Gtk.ProgressBar'不包含'BeginInvoke'的定义,并且没有找到'Gtk.ProgressBar'类型的扩展方法'BeginInvoke'(你是否缺少using指令或者一个程序集引用?)(CS1061)(Sync-GUI v2) – Connel 2010-04-14 12:54:24

+1

检查我的答案。我使用调度程序代码和ProgressChanged方法更新了它。您可以更好地使用完成BackgroundWorker类需求含义的ProgressChanged方法。 – Amsakanna 2010-04-14 14:37:48

2

创建一个BackgroundWorker对象,并为DoWork事件放入要在后台运行的所有代码。然后,当您需要使用它时,请在对象上调用RunWorkerAsync()。

+0

在这一点上不要过多考虑它。做到这一点,然后当它运行并调试项目。在worker方法中设置断点并使用您的Threads窗口查看单独运行的UI线程和工作线程。你可以来回切换(UI线程可能在框架中,可能旋转消息泵)。 – Will 2010-04-09 13:13:53