我想实现一种方法,使用计算密集型方法从对象传递消息到UI,以便通知用户计算的状态和进度。在执行此操作时,UI应保持响应,即计算在另一个线程上执行。我已经阅读了有关代表,后台工作者等的内容,但我发现它们非常令人困惑,并且无法在我的应用程序中实现它们。这是一个简化的应用程序,与我的应用程序具有相同的一般概念在计算密集型方法完成后,UI中的文本框将在此处更新:
<Window x:Class="UpdateTxtBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UpdateTxtBox" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="94,112,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" Click="btnStart_Click" />
<TextBox Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="txtBox" VerticalAlignment="Stretch" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" />
</Grid>
namespace UpdateTxtBox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
MyTextProducer txtProducer = new MyTextProducer();
txtProducer.ProduceText();
txtBox.Text = txtProducer.myText;
}
}
}
计算密集型课程:
namespace UpdateTxtBox
{
public class MyTextProducer
{
public string myText { get; private set; }
public MyTextProducer()
{
myText = string.Empty;
}
public void ProduceText()
{
string txt;
for (int i = 0; i < 10; i++)
{
txt = string.Format("This is line number {0}", i.ToString());
AddText(txt);
Thread.Sleep(1000);
}
}
private void AddText(string txt)
{
myText += txt + Environment.NewLine;
}
}
}
如何修改此代码,以便每次调用AddText方法时都更新文本框?
答案 0 :(得分:2)
这里的基本问题是你在UI线程上进行计算密集型操作,这会锁定UI(正如你自己已经想到的那样)。解决方案是启动一个单独的线程,然后从中更新UI。但是您面临的问题是只允许UI线程更新UI。这是通过使用Dispatcher
类来解决的,它为您处理所有这些蠢事。
这是一篇很好的,我们在Dispatcher上的文章,以及如何使用它:http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
请注意,还有其他方法可以使用延迟/慢速任务处理此类UI更新,但我认为这是解决问题的充分方法。
答案 1 :(得分:1)
当您使用WPF时,我建议您使用数据绑定,这是您的代码的示例实现:
<Window x:Class="UpdateTxtBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UpdateTxtBox" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="94,112,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" Click="btnStart_Click" />
<TextBox Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="txtBox" VerticalAlignment="Stretch" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" Text="{Binding Path=myText}" />
</Grid>
</Window>
请注意,文本框内容属性现在是数据绑定。
public partial class MainWindow : Window
{
MyTextProducer txtProducer;
public MainWindow()
{
InitializeComponent();
txtProducer = new MyTextProducer();
this.DataContext = txtProducer;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(txtProducer.ProduceText);
txtBox.Text = txtProducer.myText;
}
}
注意this.DataContext = txtProducer
行,这就是告诉绑定在哪里寻找值
public class MyTextProducer : INotifyPropertyChanged
{
private string _myText;
public string myText { get { return _myText; } set { _myText = value; RaisePropertyChanged("myText"); } }
public MyTextProducer()
{
myText = string.Empty;
}
public void ProduceText()
{
string txt;
for (int i = 0; i < 10; i++)
{
txt = string.Format("This is line number {0}", i.ToString());
AddText(txt);
Thread.Sleep(1000);
}
}
private void AddText(string txt)
{
myText += txt + Environment.NewLine;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
MyTextProducer
现在实现了INotifyPropertyChanged
,因此对myText
属性的任何更改都会自动反映在用户界面中。