wpf BackgroundWorker - 关于更新UI

时间:2011-03-30 15:26:27

标签: wpf progress-bar backgroundworker invoke

我使用浏览文件对话框允许用户选择多个图像。如果选择了大量图像,则需要一点点。以下是我对所选图像的处理示例。我遍历文件路径到图像并创建用户控件的实例,用户控件有一个Image控件和一些其他控件。我创建此控件的实例,然后将其添加到关联窗口xaml文件中创建的现有stackPanel。下面的示例工作正常,但我正在尝试更好地理解BackGroundWorker,我得到了如何设置它的基础知识,它的事件,并传回一个可以更新进度条的值,但因为我的循环需要下面的up time将usercontrol实例添加到现有的stackPanel,它将无法工作,位于不同的线程中。 BackGroundWorker是否可以用于这样的示例?如果是这样,更新线程外部的ui(我的堆栈面板)的最佳方法是什么。我对wpf相当新,并且从未使用BackGroundWorker,除了测试它只是用int值更新进度,所以我希望这个问题有意义,如果我离开目标只是让我知道。谢谢你的任何想法。

我现在如何做的示例,它可以正常工作。

protected void myMethod(string[] fileNames) {  
    MyUserControl uc;  

    foreach (String imagePath in fileNames) {  
        uc = new MyUserControl();  
        uc.setImage(imagePath);  
        stackPanel.Children.Add(uc);  
        progressBar.Value = ++counter;  
        progressBar.Refresh();  
    }  
}    

在这个类下面我有这个所以我可以进行progressBar刷新:

public static class extensionRefresh {  
    private static Action EmptyDelegate = delegate() { };  

    public static void Refresh(this UIElement uiElement) {  
        uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);  
    }  
}

2 个答案:

答案 0 :(得分:2)

查看这篇文章 Building more responsive apps with the Dispatcher

  

现在您已经了解了Dispatcher的工作原理,您可能会惊讶地发现在大多数情况下您将无法使用它。在Windows Forms 2.0中,Microsoft引入了一个非UI线程处理类,以简化用户界面开发人员的开发模型。该类称为BackgroundWorker

     

在WPF中,此模型使用DispatcherSynchronizationContext类进行扩展。通过使用BackgroundWorker,Dispatcher将自动用于调用跨线程方法调用。好消息是,由于您可能已经熟悉这种常见模式,因此可以在新的WPF项目中继续使用BackgroundWorker

基本上方法是

BackgroundWorker _backgroundWorker = new BackgroundWorker();

// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;

// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);

// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // Do something
}

// Completed Method
void _backgroundWorker_RunWorkerCompleted(object sender,  RunWorkerCompletedEventArgs e)
{
    // Doing UI stuff
    if (e.Cancelled)
    {
        statusText.Text = "Cancelled";
    }
    else if (e.Error != null) 
    {
        statusText.Text = "Exception Thrown";
    }
    else 
    {
        statusText.Text = "Completed";
    }
}

答案 1 :(得分:1)

单独使用BackgroundWorker无法解决您的问题,因为在DoWork部分期间创建的元素仍然来自非UI线程。您必须在要在另一个线程上使用的任何对象上调用Freeze。但是,只有某些UI对象可以冻结。您可能必须在后台线程上将图像作为BitmapImage加载,然后在UI线程上创建其余的用户控件。这仍然可以实现您的目标,因为在图像中加载可能是最重量级的操作。

请记住将BitmapImage.CacheOption设置为OnLoad,因此它实际上会在您创建对象时加载图像,而不是等到需要显示它。