所以我正在开发我的第一个多线程WPF应用程序。请记住,我对如何实现多线程几乎没有任何理解 - 我已经在一些应用程序中完成了它并且总是处理现有代码。这是我在WPF中的第一次尝试,它显然与Windows Forms完全不同......
所以基本上我正在研究this example,奇怪的是没有提到你必须实例化一个新线程并启动它 - 我想作者觉得这对我这样的新手来说是不言而喻的。< / p>
在任何情况下,它都可以正常工作,直到我想要更新我的UI控件的属性,此时我被InvalidOperationException
命中,其中调用线程可以因为控件属于不同的线程
所以基本上这不是一种线程安全的工作方式,但我不知道如何解决这个问题......
这是我的代码:
string fn = "";
private void btnBrowse_Click(object sender, RoutedEventArgs e)
{
if (lblActivity.Visibility != System.Windows.Visibility.Hidden)
{
lblActivity.Visibility = System.Windows.Visibility.Hidden;
}
if (lstResult.Items.Count != 0)
{
lstResult.Items.Clear();
}
OpenFileDialog ofd = new OpenFileDialog();
Nullable<bool> result = ofd.ShowDialog();
if (result == true)
{
txtSource.Text = ofd.FileName;
txtDest.Text = ofd.FileName;
fn = ofd.SafeFileName;
PopulateControlsDuingLongRefreshCall("read");
}
}
private void PopulateControlsDuingLongRefreshCall(string action)
{
ShowProcessing();
ThreadStart ts = delegate
{
switch (action)
{
case "format":
Populate();
CleanData();
break;
case "read":
TextReader tr = new StreamReader(txtSource.Text);
txtPreview.Text = tr.ReadToEnd();
break;
}
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (EventHandler)delegate
{
HideProcessing();
}, null, null);
};
Thread thr = new Thread(ts);
thr.Start();
}
private void ShowProcessing()
{
recProcessing.Visibility = Visibility.Visible;
}
private void HideProcessing()
{
recProcessing.Visibility = Visibility.Collapsed;
}
如果有人可以在这里提出解决方案或者更多地解释我正在使用的概念(我仍然很模糊),这样我就能够理解到足以找到自己的解决方案,我将非常感激
提前致谢!
修改
如果我获得UI控件属性TextReader tr = new StreamReader(txtSource.Text);
也会抛出异常,那么也会出现错误。
答案 0 :(得分:1)
在您的委托中,不要直接访问txtSource,请尝试使用以下函数返回streamreader:
private StreamReader GetTxtSource()
{
if (!this.Dispatcher.CheckAccess())
{
return this.Dispatcher.Invoke(new Func<StreamReader>(this.GetTxtSource)) as StreamReader;
}
return new StreamReader(txtSource.Text);
}
您可能需要使用txtSource.Dispatcher.CheckAccess()代替。
答案 1 :(得分:0)
这里你真正需要理解的是,你不能从UI线程以外的其他线程访问任何控件。
所以当你做
时 case "read":
TextReader tr = new StreamReader(txtSource.Text);
txtPreview.Text = tr.ReadToEnd();
break;
您正尝试在后台线程中设置文本框(txtPreview)的值。
您需要做的是,在后台线程中读取文件内容,然后使用UI线程更新文本框值,使用Dispatcher:
case "read":
TextReader tr = new StreamReader(txtSource.Text);
string content = tr.ReadToEnd();
Dispatcher.Invoke((Action)(() =>
{
txtPreview.Text = content;
}));
txtPreview.Text = tr.ReadToEnd();
break;