Jeremie
Jeremie

Reputation: 41

How to update a collection of images bound to a tab control from background worker?

This is my first post to stackoverflow so if I've done anything wrong I apologize. I have a WPF application that utilizes Prism and the MVVM pattern. On my view I have a TabControl that has a custom data template and is bound to an ObservableCollection of a class called ShippingLabel.

Xaml for TabControl:

<TabControl Grid.Row="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=ShippingLabels}">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <Label Content="{Binding Path=Name}"/>
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <StackPanel>
                        <Image Source="{Binding Path=Image}"/>
                    </StackPanel>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>

Helper class that I created to bind to the TabControl

public class ShippingLabel
{
/// <summary>
/// Name of the image, this is the text that will be displayed in the Tab
/// </summary>
public string Name { get; set; }
/// <summary>
/// BitmapImage to display for TabContent
/// </summary>
public BitmapImage Image { get; set; }
}

Initially I was doing all the work that needed done in the Constructor of my ViewModel, the ViewModel calls a WCF service and gets back a response that contains shipping labels in a base64 encoded string format. This worked fine and the labels were displayed correctly based on the number of packages they indicated they were shipping. To improve the usability I decided to add a BackgroundWorker to handle calling the service and to process the shipping labels once the worker had completed.

BackgroundWorker events

private void serviceWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  //Present the shipping labels
  ShippingLabels = new ObservableCollection<ShippingLabel>();
  int packageNumber = 0;
  foreach (var result in adapter.CurrentResponse.PackageResults)
  {
    packageNumber++;

    Byte[] labelBytes = new Byte[result.ShippingLabel.Length];
    labelBytes = Convert.FromBase64String(result.ShippingLabel.Replace("\r\n", "").Replace(" ", ""));

    var tempLabel = new BitmapImage();
    tempLabel.BeginInit();
    tempLabel.StreamSource = new MemoryStream(labelBytes);
    tempLabel.Rotation = Rotation.Rotate90;
    tempLabel.EndInit();

    ShippingLabels.Add(new ShippingLabel()
    {
      Name = "Package " + packageNumber.ToString(),
      Image = tempLabel
    });
  }

  ShowBusyIndicator = false;
  RaisePropertyChanged(() => ShippingLabels);
}

private void serviceWorker_DoWork(object sender, DoWorkEventArgs e)
{
  adapter.ProcessShipment();
}

However when I tried this the UI will only display the Tab with the name, but no Image in the tab content. I attempted to make my class support INotifyPropertyChanged thinking it was something there but that made no difference. So the question is what needs done in order for the UI to be correctly notified that the Image property of a collection has changed so that it will update?

Upvotes: 0

Views: 386

Answers (1)

brunnerh
brunnerh

Reputation: 185290

Since you noted that the name of the labels is shown this is probably something which is quite specific to the image itself. The bindings look fine.

If your images are created on a background worker you have to be quite careful, BitmapImage has thread affinity and it is comparatively complicated to load one in the background, it needs to be frozen to be transferred back.

Are you sure there are no exceptions that are being swallowed by a background worker somewhere?

Upvotes: 1

Related Questions