WPF -- 从其他线程中修改更新控件
大家都知道,多线程可以大大提高WPF的反应效率,但是不幸的是,UI控件不能被多个线程同时更改。
比如:
代码
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "A Checkbox";
System.Threading.Thread thread = new System.Threading.Thread(
new System.Threading.ThreadStart(
delegate()
{
txtUpdateDownloadInfo.Text += "update KB download begin...." + Environment.NewLine;
}
));
thread.Start();
}
}
则会抛出: The calling thread cannot access this object because a different thread owns it.的异常。
在Winform中我们可以是设置: Control.CheckForIllegalCrossThreadCalls = false; 在加上lock可以解决此问题,但是WPF中是没有这个属性的。
而幸运的是WPF同样提供了一个功能更大---Dispatcher。在WPF中大多数控件都是继承于DispacterObject,所以他们都具有Dispatcher属性。
所以,解决方案:
WPF:
代码
private void btnDownload_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(txtUpdates.Text))
{
MessageBox.Show("Error, Please input the update KBNumber");
return;
}
try
{
string[] updates = txtUpdates.Text.Split(new char[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in updates)
{
ParameterizedThreadStart st = new ParameterizedThreadStart(Download);
Thread t = new Thread(st);
t.Start(item);
}
}
catch (Exception ex)
{
MessageBox.Show("Error with :" + ex.Message);
}
}
public void Download(object Kbnumber)
{
CopyUpdates(Kbnumber as string);
}
private void CopyUpdates(string p_updates)
{
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => txtUpdateDownloadInfo.Text += "update KB" + p_updates + " download begin...." + Environment.NewLine));
IEnumerable<KeyValuePair<string, string>> updateLocations = new Dictionary<string, string>();
updateLocations = updateLocations.Union(UpdatesSupport.GetUpdateSource(p_updates));
foreach (KeyValuePair<string, string> kvp in updateLocations)
{
try
{
if (!IO.Directory.Exists(IO.Path.GetDirectoryName(kvp.Value)))
IO.Directory.CreateDirectory(IO.Path.GetDirectoryName(kvp.Value));
if (!IO.File.Exists(kvp.Value) || IO.File.GetLastWriteTimeUtc(kvp.Value) < IO.File.GetLastWriteTimeUtc(kvp.Key))
IO.File.Copy(kvp.Key, kvp.Value);
}
catch
{ }
}
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => txtUpdateDownloadInfo.Text += "update KB" + p_updates + " download completed...." + Environment.NewLine));
}
Winform:
代码
public UpdateInfos()
{
InitializeComponent();
DownloadPanel dp = new DownloadPanel();
dp.Location = new Point(9, 19);
gbxUIODT.Controls.Add(dp);
Control.CheckForIllegalCrossThreadCalls = false;
}
private void btnUIDownload_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(txtUIUpdates.Text))
{
MessageBox.Show("Error, Please input the update KBNumber");
return;
}
try
{
string[] updates = txtUIUpdates.Text.Split(new char[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in updates)
{
ParameterizedThreadStart st = new ParameterizedThreadStart(Download);
Thread t = new Thread(st);
t.Start(item);
}
}
catch(Exception ex)
{
MessageBox.Show("Error with :" + ex.Message);
}
}
public void Download(object Kbnumber)
{
CopyUpdates(Kbnumber as string);
}
public void CopyUpdates(string p_updates)
{
lock (this)
{
txtUIDonwloadInfo.Text += "update KB" + p_updates + " download begin...." + Environment.NewLine;
}
IEnumerable<KeyValuePair<string, string>> updateLocations = new Dictionary<string, string>();
updateLocations = updateLocations.Union(GetUpdateSource(p_updates));
foreach (KeyValuePair<string, string> kvp in updateLocations)
{
try
{
if (!Directory.Exists(Path.GetDirectoryName(kvp.Value)))
Directory.CreateDirectory(Path.GetDirectoryName(kvp.Value));
if (!File.Exists(kvp.Value) || File.GetLastWriteTimeUtc(kvp.Value) < File.GetLastWriteTimeUtc(kvp.Key))
File.Copy(kvp.Key, kvp.Value);
}
catch
{ }
}
lock (this)
{
txtUIDonwloadInfo.Text += "update KB" + p_updates + " download completed...." + Environment.NewLine;
}
}