【问题标题】:Adding images to StackPanel in WPF在 WPF 中将图像添加到 StackPanel
【发布时间】:2011-12-14 10:51:53
【问题描述】:

我有一个 WPF 应用程序,它正在数据库中寻找新图像,如果出现问题,它会将图像添加到列表中。当引发该事件时,我希望它将图像添加到 StackPanel 中。

首先我尝试只插入图像,但收到InvalidOperationException 说“调用线程必须是 STA,因为许多 UI 组件都需要这个。”并想出了:

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

这不会引发任何异常,但实际上什么也没发生......

【问题讨论】:

  • 您不需要创建一个新线程来调用Dispatcher。相反,您应该使用代表致电sp_images.Dispatcher.BeginInvoke(...)。另外,您是否尝试过将图像生成绑定到ItemsControl?您可以使用模板将其转换为 StackPanel,然后使用 ObservableCollection 中包含的 BitmapImage 对象。

标签: c# wpf


【解决方案1】:

参考我的评论,你可以试试这样:

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>

代码背后

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

这应该会绕过您之前可能遇到的任何跨线程异常。它还应该允许您轻松地将新图像添加到ObservableCollection,这将使用图像自动更新 UI。此外,ItemTemplate 的使用意味着您不必每次都自己实际构建 UI; WPF 将为您处理此问题。

有关使用ObservableCollection 的更多信息,请参阅here。另外,请参阅此StackOverflow question 以了解有关容器模板的说明。

【讨论】:

  • 非常感谢您的意见,看起来这是朝着最终解决方案迈出的一大步,但在将 imageObj 添加到 mImages-collection 时出现 NotSupportedException。详细信息说:“这种类型的 CollectionView 不支持从不同于 Dispatcher 线程的线程更改其 SourceCollection。”
  • 啊,是的,确实如此。我已经编辑了我的答案来解决这个问题。
  • 再次感谢您...我现在得到 Markup.XAMLParseException,详细信息是:必须在与 DependencyObject 相同的线程上创建 DependencySource。然后我尝试在添加之前冻结 BitmapImage,它告诉我我无法访问它,因为它来自另一个线程...再次感谢您的帮助...
  • 好吧,让它工作。只需要在我将其添加到第一个列表之前冻结图像,而不是在我将其取出时......非常感谢。您帮了大忙...您的答案应该被标记为正确...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-06
  • 2017-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-24
相关资源
最近更新 更多