我有一个WPF应用程序,它在相同的代码块中加载了大量内容和UI更新,我想在Label.Content
属性中显示进度或正在执行的任务,目前我正在这样做:
void LoadEverything()
{
UpdateContentLabel("Loading items");
foreach(string i in stringArrayItems)
{
UpdateContentLabel("Loading " + i + " info...");
//LoadTasks
}
UpdateContentLabel("Loading history");
}
void UpdateContentLabel(string Task)
{
myLoadLabel.Content = Task;
}
第一个问题是标签内容没有更新,我知道UI线程和任务线程是相同的,这就是UI冻结的原因,我试图使用BackgroundWorker
将加载任务放在上面和this.Dispatcher.BeginInvoke((Action)delegate { /*UI Updates */ });
用户界面更新(例如创建自定义ListBoxItem
并将其添加到ListBox
)并在TargetInvocationException
中抛出app.ShowDialog();
(app
是不是第一种形式,是mainApp
对话框,在登录窗口中创建。)
BackgroundWorker loadInfo = new BackgroundWorker();
private void app_Loaded(object sender, RoutedEventArgs e)
{
loadInfo.DoWork += loadInfo_DoWork;
loadInfo.RunWorkerCompleted += loadInfo_RunWorkerCompleted;
loadInfo.WorkerReportsProgress = true;
loadInfo.RunWorkerAsync();
}
public void loadInfo_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
UpdateContentLabel("Load complete :)");
}
public void loadInfo_DoWork(object sender, DoWorkEventArgs e)
{
this.Dispatcher.BeginInvoke((Action)delegate { UpdateContentLabel("Loading items"); });
foreach(string i in stringArrayItems)
{
this.Dispatcher.BeginInvoke((Action)delegate { UpdateContentLabel("Loading " + i + " info..."); });
//LoadTasks
this.Dispatcher.BeginInvoke((Action)delegate { /*UI Updates */ });
}
}
所以,问题是如何在上述标签中显示当前任务?
编辑: .NET的项目版本是3.5。
答案 0 :(得分:1)
将您的逻辑放在foreach
循环中,而不是在第二个BeginInvoke
内。
这是一个正在运行的功能示例:
public partial class MainWindow : Window
{
BackgroundWorker loadInfo = new BackgroundWorker();
private List<string> stringArrayItems = new List<string>{
"First file",
"second file",
"third file ",
"fourth file "
};
public MainWindow()
{
InitializeComponent();
loadInfo.DoWork += loadInfo_DoWork;
loadInfo.RunWorkerCompleted += loadInfo_RunWorkerCompleted;
loadInfo.WorkerReportsProgress = true;
loadInfo.RunWorkerAsync();
}
public void loadInfo_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
UpdateContentLabel("Load complete :)");
}
public void loadInfo_DoWork(object sender, DoWorkEventArgs e)
{
this.Dispatcher.BeginInvoke((Action)(() => UpdateContentLabel("Loading items")));
foreach (string i in stringArrayItems)
{
this.Dispatcher.BeginInvoke((Action)(() =>
UpdateContentLabel("Loading " + i + " info...")
));
//LoadTasks
Thread.Sleep(1000);
Console.Out.WriteLine("Loading {0} and sleeping for a second.", i);
}
}
void UpdateContentLabel(string Task)
{
MyLabel.Content = Task;
}
}
答案 1 :(得分:0)
在这里,我创建了与您的代码场景相匹配的示例代码。在这里,想法不是直接从后面的代码访问UI元素(在你的情况下是ListBox)。通过使用INotifyPropertyChanged,您可以利用List集合绑定到ListBox的ItemsSource,并更新代码背后的绑定集合,从而消除您的UI线程附件要求。所以你走了:
<Grid x:Name="MainGrid">
<ListBox ScrollViewer.VerticalScrollBarVisibility="Visible" x:Name="MainListBox" ItemsSource="{Binding AvailableListItems}">
</ListBox>
</Grid>
代码背后:
public partial class MainWindow : INotifyPropertyChanged
{
#region INotifyPropertyChanged section
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
private List<int> availableListItems;
public List<int> AvailableListItems
{
get
{
return availableListItems;
}
set
{
availableListItems = value;
OnPropertyChanged("AvailableListItems");
}
}
public MainWindow()
{
InitializeComponent();
AvailableListItems = new List<int>();
this.DataContext = this;
}
BackgroundWorker loadInfo = new BackgroundWorker();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
loadInfo.DoWork += loadInfo_DoWork;
loadInfo.WorkerReportsProgress = true;
loadInfo.RunWorkerAsync();
}
public void loadInfo_DoWork(object sender, DoWorkEventArgs e)
{
for(int i = 0; i < 2000; i++)
{
AvailableListItems.Add(i);
Thread.Sleep(1);
OnPropertyChanged("OnPropertyChanged");
}
}
}
希望,这会有所帮助。