如何直接从WPF BackgroundWorker的线程访问UI线程? [英] How to directly access the UI thread from the BackgroundWorker thread in WPF?
问题描述
我要创建WPF中的备份实用程序,并有关于线程 一般性的问题:
在该方法的 BackgroundWorker的。 DoWork的(),语句Message2.Text =...给出了错误,因为不同的线程拥有它的调用线程不能访问该对象。
有没有办法让我直接访问内backgroundWorker.DoWork(),即改变文本的UI线程在XAML文本框在这一点?或者,我需要所有的显示信息存储在一个内部变量,然后在 backgroundWorker.ProgressChanged显示它(),因为我曾与例如做?percentageFinished
XAML:
<窗口x:类=TestCopyFiles111.Window1
的xmlns =http://schemas.microsoft.com/winfx/2006/xaml/presentation
的xmlns:X =HTTP:// schemas.microsoft.com/winfx/2006/xaml
标题=窗口1HEIGHT =350WIDTH =525>
< DockPanel中LastChildFill =真的HorizontalAlignment =左VerticalAlignment =评出的
保证金=10>
< StackPanel的方向=横向DockPanel.Dock =评出的>
<按钮X:NAME =Button_Start
的HorizontalAlignment =左
DockPanel.Dock =评出的
含量=开始复制
点击= Button_Start_Click
高度=25
保证金=0 0 5 0
WIDTH =200/>
<按钮X:NAME =Button_Cancel
的HorizontalAlignment =左
DockPanel.Dock =评出的
含量=取消
=单击 Button_Cancel_Click
高度=25
WIDTH =200/>
< / StackPanel的>
<进度X:NAME =进度
DockPanel.Dock =评出的
的HorizontalAlignment =左
保证金=0 10 0 0
高度=23
WIDTH =405
最短=0
最大=100
/>
< TextBlock的DockPanel.Dock =评出的X:名称=消息保证金=10 0 0 0/>
< TextBlock的DockPanel.Dock =评出的X:NAME =CurrentFileCopying保证金=0 10 0 0/>
< TextBlock的DockPanel.Dock =评出的X:名称=消息2保证金=0 10 0 0/>
< / DockPanel中>
< /窗GT;
后台代码:
使用System.Windows;
使用System.ComponentModel;
使用的System.Threading;
:使用System.IO;
使用System.Collections.Generic;
使用系统;
命名空间TestCopyFiles111
{
公共部分类窗口1:窗口
{
私人的BackgroundWorker BackgroundWorker的;
浮动percentageFinished = 0;
私人INT totalFilesToCopy = 0;
INT filesCopied = 0;
串currentPathAndFileName;
私人列表< CopyFileTask> copyFileTasks =新的List< CopyFileTask>();
私有列表<串GT; foldersToCreate =新的List<串GT;();
公共窗口1()
{
的InitializeComponent();
Button_Cancel.IsEnabled = FALSE;
Button_Start.IsEnabled = TRUE;
ProgressBar.Visibility = Visibility.Collapsed;
}
私人无效Button_Start_Click(对象发件人,RoutedEventArgs E)
{
Button_Cancel.IsEnabled = TRUE;
BackgroundWorker的=新的BackgroundWorker();
backgroundWorker.WorkerReportsProgress = TRUE;
backgroundWorker.WorkerSupportsCancellation = TRUE;
ProgressBar.Visibility = Visibility.Visible;
AddFilesFromFolder(@C:\test,@C:\test2);
Message.Text =正在准备拷贝...;
MakeSureAllDirectoriesExist();
CopyAllFiles();
}
无效AddFilesFromFolder(字符串sourceFolder,串destFolder)
{
如果(!Directory.Exists(destFolder))
Directory.CreateDirectory(destFolder);
的String []文件= Directory.GetFiles(sourceFolder);
的foreach(在文件中字符串的文件)
{
字符串名称= Path.GetFileName(文件);
串DEST = Path.Combine(destFolder,名);
copyFileTasks.Add(新CopyFileTask(文件,DEST));
totalFilesToCopy ++;
}
的String []文件夹= Directory.GetDirectories(sourceFolder);
的foreach(在文件夹中的文件夹弦)
{
字符串名称= Path.GetFileName(文件夹);
串DEST = Path.Combine(destFolder,名);
foldersToCreate.Add(DEST);
AddFilesFromFolder(文件夹,DEST);
}
}
无效MakeSureAllDirectoriesExist()
{
的foreach(在foldersToCreate VAR folderToCreate)
{
如果(! Directory.Exists(folderToCreate))
Directory.CreateDirectory(folderToCreate);
}
}
无效CopyAllFiles()
{
BackgroundWorker的=新的BackgroundWorker();
backgroundWorker.WorkerReportsProgress = TRUE;
backgroundWorker.WorkerSupportsCancellation = TRUE;
backgroundWorker.DoWork + =(S,参数)=>
{
filesCopied = 0;
的foreach(在copyFileTasks VAR copyFileTask)
{
如果(backgroundWorker.CancellationPending)
{
args.Cancel = TRUE;
的回报;
}
的DateTime sourceFileLastWriteTime = File.GetLastWriteTime(copyFileTask.SourceFile);
的DateTime targetFileLastWriteTime = File.GetLastWriteTime(copyFileTask.TargetFile);
如果(sourceFileLastWriteTime = targetFileLastWriteTime!)
{
Message2.Text =日期是不一样的;
}
,否则
{
Message2.Text =日期是相同的;
}
如果
File.Copy(copyFileTask.SourceFile,copyFileTask.TargetFile)(File.Exists(copyFileTask.TargetFile)!);
currentPathAndFileName = copyFileTask.SourceFile;
UpdatePercentageFinished();
backgroundWorker.ReportProgress((INT)percentageFinished);
filesCopied ++;
}
};
backgroundWorker.ProgressChanged + =(S,参数)=>
{
percentageFinished = args.ProgressPercentage;
ProgressBar.Value = percentageFinished;
Message.Text = percentageFinished +说完%;
CurrentFileCopying.Text = currentPathAndFileName;
};
backgroundWorker.RunWorkerCompleted + =(S,参数)=>
{
Button_Start.IsEnabled = TRUE;
Button_Cancel.IsEnabled = FALSE;
ProgressBar.Value = 0;
UpdatePercentageFinished();
CurrentFileCopying.Text =;
如果(percentageFinished< 100)
{
Message.Text =的String.Format(取消了在{0:0}%完成,percentageFinished);
}
,否则
{
Message.Text =所有文件复制。
}
};
backgroundWorker.RunWorkerAsync();
}
无效UpdatePercentageFinished()
{
percentageFinished =(filesCopied /(浮点)totalFilesToCopy)* 100F;
}
类CopyFileTask
{
公共字符串的SourceFile {搞定;组; }
公共字符串的TargetFile {搞定;组; }
公共CopyFileTask(字符串的资源文件,字符串的TargetFile)
{
的SourceFile =的资源文件;
的TargetFile =的TargetFile;
}
}
私人无效Button_Cancel_Click(对象发件人,RoutedEventArgs E)
{
backgroundWorker.CancelAsync();
}
}
}
你有没有看使用Dispatcher.Invoke?
Dispatcher.Invoke(新动作(() => {Button_Start.Content = i.ToString();}));
或者,如果你想要的东西异步发生使用的BeginInvoke。
I'm creating a backup utility in WPF and have a general question about threading:
In the method backgroundWorker.DoWork(), the statement Message2.Text = "..." gives the error "The calling thread cannot access this object because a different thread owns it.".
Is there no way for me to directly access the UI thread within backgroundWorker.DoWork(), i.e. change text in a XAML TextBox at that point? Or do I need to store all display information in an internal variable, and then display it in backgroundWorker.ProgressChanged(), as I had to do with e.g. percentageFinished?
XAML:
<Window x:Class="TestCopyFiles111.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="350" Width="525">
<DockPanel LastChildFill="True" HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="10">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<Button x:Name="Button_Start"
HorizontalAlignment="Left"
DockPanel.Dock="Top"
Content="Start Copying"
Click="Button_Start_Click"
Height="25"
Margin="0 0 5 0"
Width="200"/>
<Button x:Name="Button_Cancel"
HorizontalAlignment="Left"
DockPanel.Dock="Top"
Content="Cancel"
Click="Button_Cancel_Click"
Height="25"
Width="200"/>
</StackPanel>
<ProgressBar x:Name="ProgressBar"
DockPanel.Dock="Top"
HorizontalAlignment="Left"
Margin="0 10 0 0"
Height="23"
Width="405"
Minimum="0"
Maximum="100"
/>
<TextBlock DockPanel.Dock="Top" x:Name="Message" Margin="0 10 0 0"/>
<TextBlock DockPanel.Dock="Top" x:Name="CurrentFileCopying" Margin="0 10 0 0"/>
<TextBlock DockPanel.Dock="Top" x:Name="Message2" Margin="0 10 0 0"/>
</DockPanel>
</Window>
code-behind:
using System.Windows;
using System.ComponentModel;
using System.Threading;
using System.IO;
using System.Collections.Generic;
using System;
namespace TestCopyFiles111
{
public partial class Window1 : Window
{
private BackgroundWorker backgroundWorker;
float percentageFinished = 0;
private int totalFilesToCopy = 0;
int filesCopied = 0;
string currentPathAndFileName;
private List<CopyFileTask> copyFileTasks = new List<CopyFileTask>();
private List<string> foldersToCreate = new List<string>();
public Window1()
{
InitializeComponent();
Button_Cancel.IsEnabled = false;
Button_Start.IsEnabled = true;
ProgressBar.Visibility = Visibility.Collapsed;
}
private void Button_Start_Click(object sender, RoutedEventArgs e)
{
Button_Cancel.IsEnabled = true;
backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
ProgressBar.Visibility = Visibility.Visible;
AddFilesFromFolder(@"c:\test", @"C:\test2");
Message.Text = "Preparing to copy...";
MakeSureAllDirectoriesExist();
CopyAllFiles();
}
void AddFilesFromFolder(string sourceFolder, string destFolder)
{
if (!Directory.Exists(destFolder))
Directory.CreateDirectory(destFolder);
string[] files = Directory.GetFiles(sourceFolder);
foreach (string file in files)
{
string name = Path.GetFileName(file);
string dest = Path.Combine(destFolder, name);
copyFileTasks.Add(new CopyFileTask(file, dest));
totalFilesToCopy++;
}
string[] folders = Directory.GetDirectories(sourceFolder);
foreach (string folder in folders)
{
string name = Path.GetFileName(folder);
string dest = Path.Combine(destFolder, name);
foldersToCreate.Add(dest);
AddFilesFromFolder(folder, dest);
}
}
void MakeSureAllDirectoriesExist()
{
foreach (var folderToCreate in foldersToCreate)
{
if (!Directory.Exists(folderToCreate))
Directory.CreateDirectory(folderToCreate);
}
}
void CopyAllFiles()
{
backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.DoWork += (s, args) =>
{
filesCopied = 0;
foreach (var copyFileTask in copyFileTasks)
{
if (backgroundWorker.CancellationPending)
{
args.Cancel = true;
return;
}
DateTime sourceFileLastWriteTime = File.GetLastWriteTime(copyFileTask.SourceFile);
DateTime targetFileLastWriteTime = File.GetLastWriteTime(copyFileTask.TargetFile);
if (sourceFileLastWriteTime != targetFileLastWriteTime)
{
Message2.Text = "dates are not the same";
}
else
{
Message2.Text = "dates are the same";
}
if (!File.Exists(copyFileTask.TargetFile))
File.Copy(copyFileTask.SourceFile, copyFileTask.TargetFile);
currentPathAndFileName = copyFileTask.SourceFile;
UpdatePercentageFinished();
backgroundWorker.ReportProgress((int)percentageFinished);
filesCopied++;
}
};
backgroundWorker.ProgressChanged += (s, args) =>
{
percentageFinished = args.ProgressPercentage;
ProgressBar.Value = percentageFinished;
Message.Text = percentageFinished + "% finished";
CurrentFileCopying.Text = currentPathAndFileName;
};
backgroundWorker.RunWorkerCompleted += (s, args) =>
{
Button_Start.IsEnabled = true;
Button_Cancel.IsEnabled = false;
ProgressBar.Value = 0;
UpdatePercentageFinished();
CurrentFileCopying.Text = "";
if (percentageFinished < 100)
{
Message.Text = String.Format("cancelled at {0:0}% finished", percentageFinished);
}
else
{
Message.Text = "All files copied.";
}
};
backgroundWorker.RunWorkerAsync();
}
void UpdatePercentageFinished()
{
percentageFinished = (filesCopied / (float)totalFilesToCopy) * 100f;
}
class CopyFileTask
{
public string SourceFile { get; set; }
public string TargetFile { get; set; }
public CopyFileTask(string sourceFile, string targetFile)
{
SourceFile = sourceFile;
TargetFile = targetFile;
}
}
private void Button_Cancel_Click(object sender, RoutedEventArgs e)
{
backgroundWorker.CancelAsync();
}
}
}
Have you looked at using Dispatcher.Invoke?
Dispatcher.Invoke(new Action(() => { Button_Start.Content = i.ToString(); }));
Or use BeginInvoke if you want something to happen asynchronously.
这篇关于如何直接从WPF BackgroundWorker的线程访问UI线程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!