【问题标题】:wpf BackgroundWorker - Regarding updating UIwpf BackgroundWorker - 关于更新 UI
【发布时间】:2011-03-30 15:26:26
【问题描述】:

我使用浏览文件对话框来允许用户选择多个图像。如果选择了很多图像,正如预期的那样,它需要一些时间。下面是我如何处理所选图像的示例。我遍历图像的文件路径并创建用户控件的实例,用户控件有一个图像控件和一些其他控件。我创建此控件的实例,然后将其添加到在关联窗口 xaml 文件中创建的现有 stackPanel。下面的示例工作正常,但我试图更好地理解 BackGroundWorker,我了解了如何设置它的基础知识,它的事件,并传回一个可以更新进度条的值,但是因为我的循环需要下面的正常运行时间将用户控件实例添加到现有的 stackPanel,它不会工作,在不同的线程中。 BackGroundWorker 是否适用于这样的示例?如果是这样,那么更新线程外的 ui(我的堆栈面板)的最佳方法是什么。我对 wpf 还很陌生,除了测试它只是用一个 int 值更新进度之外,从未使用过 BackGroundWorker,所以我希望这个问题是有道理的,如果我偏离目标,请告诉我。感谢您的任何想法。

我现在的做法示例,效果很好。

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();  
    }  
}    

在这个类下面我有这个,所以我可以刷新进度条:

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

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

【问题讨论】:

    标签: wpf progress-bar backgroundworker invoke


    【解决方案1】:

    查看这篇文章 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";
        }
    }
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多