文本更新未反映在WPF TextBlock中 - 我缺少什么?

时间:2014-05-22 14:47:55

标签: c# wpf user-interface

概览

我是WPF的新手,我有一个带有textarea的简单窗口,一个按钮和一个文本块。单击按钮我想更新文本块以说“开始......”或其他东西,运行一个需要几分钟的例程,最后将其更新为“完成”。

问题:

目前,我的文本块仅使用“完成”消息进行更新。 :(

CODE:

public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string myValue;
        public string MyValue
        {
            get { return myValue; }
            set
            {
                myValue = value;
                RaisePropertyChanged("MyValue");
            }
        }

        private void RaisePropertyChanged(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        private void ScriptToFileButton_Click(object sender, RoutedEventArgs e)
        {
            Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < 50; i++)
                {
                    System.Threading.Thread.Sleep(100);
                    MyValue = i.ToString();
                }
            });

            MyValue = "Scripting, please wait..";
            String text = DBObjectsTextArea.Text;
            String[] args = text.Split(' ');
            SQLScripter scripter = new SQLScripter();
            scripter.script(args);
            MyValue = "Done!";

        }
        private void Window_Closed(object sender, EventArgs e)
        {
            Application.Current.Shutdown();
        }
    }

XAML:

 <TextBox Height="109" HorizontalAlignment="Left" Margin="45,12,0,0" Name="DBObjectsTextArea" VerticalAlignment="Top" Width="418" />
        <Button Content="Script To File" Height="23" HorizontalAlignment="Left" Margin="173,145,0,0" Name="ScriptToFileButton" VerticalAlignment="Top" Width="168" Click="ScriptToFileButton_Click" />
        <TextBlock Height="56" HorizontalAlignment="Left" Margin="45,197,0,0" Name="StatusTextBlock" Text="{Binding Path=MyValue}" VerticalAlignment="Top" Width="409" />

SIMILAR LINK:

我的代码基于此: How do I refresh visual control properties (TextBlock.text) set inside a loop?

3 个答案:

答案 0 :(得分:1)

您可以将事件处理程序更改为此...

   private void ScriptToFileButton_Click(object sender, RoutedEventArgs e)
        {
            String text = DBObjectsTextArea.Text;
            Task t = new Task(() => PerformOnDispatcher(() => { MyValue = "Scripting, please wait.."; }));
            ManualResetEvent mre = new ManualResetEvent(false);
            t.ContinueWith((x) =>
            {
                // scripting code goes here...
                mre.Set();
            }, TaskContinuationOptions.LongRunning);

            t.ContinueWith((y) => 
            { 
                mre.WaitOne();
                PerformOnDispatcher(() => { MyValue = "Scripting, please wait.."; });
            });
            t.Start();
        }

这将工作分为三个阶段:告诉用户脚本已经开始,然后进行工作,然后告诉用户工作已完成。同步由“ContinueWith”方法控制。此方法在启动之前等待前一个方法完成。

因为它在任务中,所以对UI的更改通过调度程序进行编组,调度程序使用单独的调度程序,因此不会被阻止。

最后一个语句t.Start()启动事件序列,因此单击按钮的用户体验不会受到阻止的UI的阻碍。事件处理程序几乎立即返回。

如果工作很快发生,您仍然可能只看到“完成”,因为之前的消息在您有机会阅读之前被覆盖了。所以调试这些东西的最好方法是将调试器锚定在这个语句上:

PropertyChanged(this, new PropertyChangedEventArgs(propName));

这将让您观察绑定引擎的输出内容。

最后,为了使事件处理程序更加混乱以便专注于问题,我使用了一种方便的方法...

    private void PerformOnDispatcher(Action a)
    {
        Dispatcher.InvokeAsync(a);
    }

对于工业强度应用,这可以是扩展方法,也可以在属性设置器中完成。

答案 1 :(得分:0)

如果您希望任务在后台线程上运行,则必须考虑更新UI线程上的WPF UI。 Xu博士的post解释了这一点。 Dispatcher.InvokeAsync与.Net Framework 4.5一起添加,是首选。在具有Dispatcher.BeginInvoke的早期版本的.Net中也可以使用类似的功能。不要忘记清理任务,并确定如果任务不完整,应用程序应该关闭的方式。

    // MainWindow...
    Task t; // keep for cleanup
    // Update on the UI thread
    private void SetMyValueAsync(string value)
    {
        Application.Current.Dispatcher.BeginInvoke((Action)(() => MyValue = value));
    }
    private void ScriptToFileButton_Click(object sender, RoutedEventArgs e)
    {
        t = Task.Factory.StartNew(() =>
        {
            SetMyValueAsync("Scripting, please wait..");
            // If the following lines will update the UI
            // they should do so on the UI thread per Dr. Xu's post.
            // String text = DBObjectsTextArea.Text;
            // String[] args = text.Split(' ');
            // SQLScripter scripter = new SQLScripter();
            //scripter.script(args);
            for (int i = 0; i < 50; i++)
            {
                System.Threading.Thread.Sleep(100);
                SetMyValueAsync(i.ToString());
            }
            SetMyValueAsync("Done!");
        });
    }

    protected override void OnClosing(CancelEventArgs e)
    {
        if (t != null)
        {
            try { t.Dispose(); }
            catch (System.InvalidOperationException)
            {
                e.Cancel = true;
                MessageBox.Show("Cannot close. Task is not complete.");
            }

        }
        base.OnClosing(e);
    }

答案 2 :(得分:0)

我使用以下方法实现了这项工作:

private void ScriptToFileButton_Click(object sender, RoutedEventArgs e)
{
    Task.Factory.StartNew(() =>
    {
        for (var i = 0; i < 50; i++)
        {
            Thread.Sleep(100);
            MyValue = i.ToString(CultureInfo.InvariantCulture);
        }
    })
    .ContinueWith(s =>
    {
        MyValue = "Scripting, please wait..";

        String text = DBObjectsTextArea.Text;
        String[] args = text.Split(' ');
        SQLScripter scripter = new SQLScripter();
        scripter.script(args);

        Thread.Sleep(3000); // This sleep is only used to simulate scripting
    })
    .ContinueWith(s =>
    {
        MyValue = "Done!";
    });
}

您需要创建任务延续。你之所以只看到&#34;完成&#34;是因为您在启动任务后直接设置MyValue。您不是在等待任务完成它的初始处理。