在加载程序时更改WPF标签内容

时间:2014-04-27 22:41:27

标签: c# wpf multithreading backgroundworker

我有一个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。

2 个答案:

答案 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");
        }
    }
}

希望,这会有所帮助。