1

我想实现一种使用计算密集型方法将消息从对象传递到 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 方法时更新文本框?

4

2 回答 2

2

这里的基本问题是您在 UI 线程上执行计算密集型操作,这会锁定 UI(正如您自己已经弄清楚的那样)。解决方案是启动一个单独的线程,然后从中更新 UI。但是您随后面临的问题是只允许 UI 线程更新 UI。这是通过使用Dispatcher类来解决的,它为你处理所有这些讨厌的东西。

这是一篇很好的、充实的关于 Dispatcher 以及如何使用它的文章:http: //msdn.microsoft.com/en-us/magazine/cc163328.aspx

请注意,还有其他方法可以处理这种带有延迟/缓慢任务的 UI 更新,但我认为这足以解决您的问题。

于 2012-07-23T12:35:04.123 回答
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>

请注意,文本框 Content 属性现在是数据绑定的。

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将自动反映在 UI 中。

于 2012-07-23T12:44:03.890 回答