从对象更新UI文本框

时间:2012-07-23 12:31:46

标签: c# wpf

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

2 个答案:

答案 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属性的任何更改都会自动反映在用户界面中。