我写了一个同步两个文件夹的应用程序。该程序的问题是它在复制文件时停止响应。快速搜索堆栈溢出告诉我,我需要使用一个称为后台工作程序的东西。我已经在网上阅读了几篇关于此的内容,但发现它很难理解,因为我对编程很新。我怎样才能简单地将所有File.Copy(....)命令放入他们自己的后台工作程序中(如果它甚至是如何工作的话)?下面是运行子过程的按钮单击事件的代码,以及我希望在所有File.Copy行上使用后台工作程序的子过程。
任何帮助将不胜感激,因为在此之后程序将完成:D
修改
根据以下Veer的建议,我相应地更改了我的代码,但是我不断遇到以下错误:
错误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 Sub Routine:
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);
}
}
答案 0 :(得分: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
我还看到您在SyncTarget方法中访问UI元素。您无法从另一个线程访问您的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;
}
答案 1 :(得分:2)
创建BackgroundWorker对象,对于DoWork事件,输入要在后台运行的所有代码。然后,当您需要使用它时,请在对象上调用RunWorkerAsync()。