加载程序时更改WPF标签内容
本文关键字:标签 WPF 程序 加载 | 更新日期: 2023-09-27 18:00:30
我有一个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 */ });
UI更新(比如创建自定义ListBoxItem
并将其添加到ListBox
),并在app.ShowDialog();
中抛出TargetInvocationException
(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的versión项目是3.5。
将逻辑放入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;
}
}
在这里,我创建了与您的代码场景相匹配的示例代码。这里的想法不是直接从代码后面访问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");
}
}
}
希望这会有所帮助。