2

So I'm building a WPF control that needs to load (and show) between 400 and 600 images (each one with size of 200Kb aprox.) from disk. The next is the codebehind for the control.

private List<BitmapImage> pages = new List<BitmapImage>();

        private BackgroundWorker worker;

        public PagesListBox()
        {
            InitializeComponent();
            worker = new BackgroundWorker();
            worker.WorkerSupportsCancellation = false;
            worker.WorkerReportsProgress = false;
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            List<BitmapImage> pagesList = new List<BitmapImage>();
            var files = DirectoryServices.GetFiles(Properties.Settings.Default.PagesScanDirectory, new string[] { "*.tiff", "*.jpg", "*.png", "*.bmp" });
            foreach (var file in files)
            {
                Uri uri = new Uri(file);
                pagesList.Add(new BitmapImage(uri));
            }
            e.Result = pagesList;
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Pages.ItemsSource = (List<BitmapImage>)e.Result;
        }

        internal void LoadPages()
        {
            worker.RunWorkerAsync();
        }

        internal List<BitmapImage> AttachPages()
        {
            List<BitmapImage> attachedPages = new List<BitmapImage>();

            foreach (BitmapImage eachItem in Pages.SelectedItems)
            {
                attachedPages.Add(eachItem);
                pages.Remove(eachItem);
            }
            Pages.ItemsSource = null;
            Pages.ItemsSource = pages;

            return attachedPages;
        }

I try to assign the pages list to the view (wich I can not using the background worker), but It fails.

Is ther any other way to load the images asynchronously (maybe updating the UI) or this approach of Background Worker I'm trying is fine. And, if it's fine, how can I solve the exception (within completed event):

Must create DependencySource on same Thread as the DependencyObject.

Thanks

H.B.
  • 166,899
  • 29
  • 327
  • 400
Erre Efe
  • 15,387
  • 10
  • 45
  • 77
  • 1
    This may fix the exception: http://stackoverflow.com/questions/4705726/problem-with-binding-property-typeof-bitmapimage – Steve Wong Apr 13 '12 at 01:59
  • @SteveWong Thanks. Actually it did. Now, do you now if this approach is good for this task? – Erre Efe Apr 13 '12 at 02:07
  • I find myself always recommending using BackgroundWorker to others and from what I see, you are using it perfectly. – Rhyous Apr 13 '12 at 04:02

1 Answers1

2

To simplify your code, use ObservableCollection<> rather than List<> and just assign the ItemsSource property once.

The threading error is because you are creating the bitmap and adding it to the list on the background worker thread not the main UI thread. To get around this use DispatcherHelper (taken from Laurent Bugnion's MVVMLight Framwwork).

I'd suggest that you consider MVVM as an application design methodology if you're going to be doing much WPF development, rather than just doing things the "Winforms way", although it is a completely different way of thinking.

...
DispatcherHelper.Initialise()
...


private ObservableCollection<BitmapImage> _Pages = new ObservableCollection<BitmapImage>();

public PagesListBox()
{
    InitializeComponent();
    BackgroundWorker worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = false;
    worker.WorkerReportsProgress = false;
    worker.DoWork += worker_DoWork;
    this.ItemSource = _Pages;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
     var files = DirectoryServices.GetFiles(Properties.Settings.Default.PagesScanDirectory, new string[] { "*.tiff", "*.jpg", "*.png", "*.bmp" });
    foreach (var file in files)
    {
        DispatcherHelper.CheckBeginInvokeOnUI(()=>
        {
            Uri uri = new Uri(file);
            _Pages.Add(new BitmapImage(uri));
        });
    }
}


public static class DispatcherHelper
{
    public static Dispatcher UIDispatcher { get; private set; }

    public static void CheckBeginInvokeOnUI(Action action)
    {
        if (UIDispatcher.CheckAccess())
            action();
        else
            UIDispatcher.BeginInvoke(action);
    }

    public static void Initialize()
    {
        if (UIDispatcher != null)
            return;

        UIDispatcher = Dispatcher.CurrentDispatcher;
    }
}
Peregrine
  • 4,287
  • 3
  • 17
  • 34