1

I have a WPF-application that is looking for new images in a database and if something comes up, it adds the image into a list. When that event is raised I want it to add the image into a StackPanel.

First I tried just to insert the image, but got an InvalidOperationException saying "The calling thread must be STA, because many UI components require this." and came up with:

public void Instance_GraphicChanged(object sender, PropertyChangedEventArgs e)
{
    foreach (Model.Graphic item in Model.IncomingCall.Instance.Graphics)
    {
        if(!_strings.Contains(item.ImageId.ToString()))
        {
            Thread thread = new Thread( new ThreadStart(
                delegate()
                {
                    //sp_images StackPanel for Images
                    sp_images.Dispatcher.Invoke(
                        DispatcherPriority.Normal, new Action(
                            delegate()
                            {
                                Image img = new Image();
                                img.Source = item.ImageObj; //ImageObj returns a BitmapImage
                                sp_images.Children.Add(img);
                            }
                    ));
                }
            ));
            _strings.Add(item.ImageId.ToString());
        }
    }
}

This does not throw any kind of exception, but actually nothing happens...

1
  • You shouldn't need to create a new thread to invoke on the Dispatcher. Instead, you should call sp_images.Dispatcher.BeginInvoke(...) with a delegate. Also, have you tried binding the image generation to an ItemsControl? You can use a template to turn that into a StackPanel and just work with BitmapImage objects contained within an ObservableCollection. Commented Dec 14, 2011 at 10:57

1 Answer 1

2

In reference to my comment, you could try something like this:

XAML

<!-- ... Other XAML Code ... -->
<ItemsControl x:Name="sp_images">
    <ItemsControl.ItemsPanel>
        <StackPanel Orientation="Horizontal" />
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Code Behind

private readonly HashSet<string> mImageIds = new HashSet<string>();
private readonly ObservableCollection<BitmapImage> mImages = new ObservableCollection<BitmapImage>();

// ... Inside the constructor
{
    InitializeComponent();

    sp_images.ItemsSource = mImages;
}

public void Instance_GraphicChanged(object sender, PropertyChangedEventArgs e)
{
    foreach (Model.Graphic item in Model.IncomingCall.Instance.Graphics)
    {
        // Have we already seen the image
        if (mImageIds.Add(item.ImageId.ToString()))
        {
            // We've not seen the image yet, so add it to the collection
            // Note: We must invoke this on the Dispatcher thread.
            this.Dispatcher.BeginInvoke((Action)delegate()
            {
                mImages.Add(item.ImageObj);
            });
        }
    }
}

This should bypass any cross-thread exceptions you might have had before. It should also allow you to easily add new images to the ObservableCollection, which will automatically update the UI with images. Also, the use of the ItemTemplate means you don't have to actually build the UI every time yourself; WPF will handle this for you.

See here for more information on using the ObservableCollection. Also, refer to this StackOverflow question for an explanation on the container templating.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you very much for your input, and it looks like it is a large step on the way to a final solution, but I get an NotSupportedException where I add the imageObj to the mImages-collection. Details says: "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread."
Ah yes, so it does. I have edited my answer to fix this issue.
And thank you again... I now get Markup.XAMLParseException, and detail is: Must create DependencySource on same Thread as the DependencyObject. I then tried to freeze the BitmapImage before adding it, and it told me that I couldn't access it because it was from another thread... Again, thank your for your help...
Well, got it to work. Just needed to Freeze the image before I added it to the first list, and not when I took it out... Thank you very much. You have been a great help... Your answer should have been marked as correct...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.