概览
我是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?
答案 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
。您不是在等待任务完成它的初始处理。