C# 文本更新未反映在WPF TextBlock中-我缺少什么?
概述 我是WPF新手,我有一个带有文本区域、按钮和文本块的简单窗口。点击按钮,我想更新文本块,说“started…”或其他什么,运行一个需要几分钟的例程,最后将其更新为“done” 问题: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 {
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