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

C# 文本更新未反映在WPF TextBlock中-我缺少什么?,c#,wpf,user-interface,C#,Wpf,User Interface,概述 我是WPF新手,我有一个带有文本区域、按钮和文本块的简单窗口。点击按钮,我想更新文本块,说“started…”或其他什么,运行一个需要几分钟的例程,最后将其更新为“done” 问题: public partial class MainWindow : Window, INotifyPropertyChanged { private string myValue; public string MyValue {

概述

我是WPF新手,我有一个带有文本区域、按钮和文本块的简单窗口。点击按钮,我想更新文本块,说“started…”或其他什么,运行一个需要几分钟的例程,最后将其更新为“done”

问题:

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();
        }
    }
 <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" />
当前,我的文本块仅更新为“完成”消息:(

代码:

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();
        }
    }
 <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" />
public分部类主窗口:窗口,INotifyPropertyChanged
{
私有字符串myValue;
公共字符串MyValue
{
获取{return myValue;}
设置
{
我的价值=价值;
RaisePropertyChanged(“MyValue”);
}
}
私有void RaisePropertyChanged(字符串propName)
{
if(PropertyChanged!=null)
PropertyChanged(这是新PropertyChangedEventArgs(propName));
}
公共事件属性更改事件处理程序属性更改;
公共主窗口()
{
初始化组件();
this.DataContext=this;
}
私有无效脚本文件按钮\单击(对象发送者,路由目标)
{
Task.Factory.StartNew(()=>
{
对于(int i=0;i<50;i++)
{
系统线程线程睡眠(100);
MyValue=i.ToString();
}
});
MyValue=“正在编写脚本,请稍候…”;
String text=DBObjectsTextArea.text;
字符串[]args=text.Split(“”);
SQLScripter scripter=新建SQLScripter();
scripter.script(args);
MyValue=“完成!”;
}
私有无效窗口\u已关闭(对象发送方,事件参数e)
{
Application.Current.Shutdown();
}
}
XAML:

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();
        }
    }
 <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" />

类似链接:

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();
        }
    }
 <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" />
我的代码基于以下内容:

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

   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的更改会通过dispatcher被封送回来,dispatcher使用单独的调度器,因此不会被阻止

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

如果工作进行得很快,您仍然可能只能看到“完成”,因为以前的消息在您有机会阅读之前已被覆盖。因此,调试这些内容的最佳方法是将调试器锚定在以下语句上:

PropertyChanged(this, new PropertyChangedEventArgs(propName));
这将让您观察通过管道输送到绑定引擎的内容

最后,为了减少事件处理程序的混乱程度,以便关注问题,我使用了一种方便的方法

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

对于工业级应用程序,这将是一种扩展方法或在属性设置程序中完成。

如果希望任务在后台线程上运行,则必须说明在UI线程上更新WPF UI。徐博士对此进行了解释。Dispatcher.InvokeAsync是随.Net Framework 4.5添加的,是首选。类似的功能也可以在早期版本的.Net中使用Dispatcher.BeginInvoke。不要忘记清理任务,并确定如果任务不完整,应用程序应如何关闭或是否关闭

    // 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);
    }
//主窗口。。。
任务t;//保留以进行清理
//UI线程上的更新
私有void SetMyValueAsync(字符串值)
{
Application.Current.Dispatcher.BeginInvoke((操作)(()=>MyValue=value));
}
私有无效脚本文件按钮\单击(对象发送者,路由目标)
{
t=Task.Factory.StartNew(()=>
{
SetMyValueAsync(“脚本,请稍候…”);
//如果以下行将更新UI
//根据徐博士的帖子,他们应该在UI线程上这样做。
//String text=DBObjectsTextArea.text;
//字符串[]args=text.Split(“”);
//SQLScripter scripter=新建SQLScripter();
//scripter.script(args);
对于(int i=0;i<50;i++)
{
系统线程线程睡眠(100);
SetMyValueAsync(i.ToString());
}
SetMyValueAsync(“完成!”);
});
}
关闭时受保护的覆盖无效(CancelEventArgs e)
{
如果(t!=null)
{
请尝试{t.Dispose();}
捕获(系统无效操作异常)
{
e、 取消=真;
MessageBox.Show(“无法关闭。任务未完成”);
}
}
基数(e);
}

我通过以下方法实现了这一点:

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!";
    });
}
private void ScriptToFileButton\单击(对象发送者,路由目标)
{
Task.Factory.StartNew(()=>
{
对于(变量i=0;i<50;i++)
{
睡眠(100);
MyValue=i.ToString(CultureInfo.InvariantCulture);
}
})
.ContinueWith(s=>
{
MyValue=“正在编写脚本,请稍候…”;
String text=DBObjectsTextArea.text;
字符串[]args=text.Split(“”);
SQLScripter scripter=新建SQLScripter();
scripter.script(args);
Thread.Sleep(3000);//此睡眠仅用于模拟脚本
})
.ContinueWith(s=>
{
MyValue=“完成!”;
});
}
您需要创建任务延续。之所以只看到“完成”,是因为您在启动任务后直接设置了
MyValue
。Y