代码之家  ›  专栏  ›  技术社区  ›  Jeffrey Knight

WPF事件:BitmapImage PropertyChanged:“调用线程无法访问”

  •  1
  • Jeffrey Knight  · 技术社区  · 16 年前

    我试图理解下面的代码是什么 非常乐意加载文本文件并显示其内容,但是 不是 很高兴加载BitmapImage并在Timer.Elapsed事件处理程序上显示它。

    我知道这和UI线程有关。

    但是,对于文本文件示例来说,这为什么不是一个问题呢?

    首先,XAML:

    <Window x:Class="WpfApplication7.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    
    <StackPanel Orientation="Vertical">
    
    <TextBlock Text="{Binding Path=Message, UpdateSourceTrigger=PropertyChanged}" FontSize="20" Height="40" Width="300"  Background="AliceBlue"  />
    
    <Image Source="{Binding Path=Image,UpdateSourceTrigger=PropertyChanged}" Height="100" Width="100"/>
    
    </StackPanel>
    </Window>
    

    以及C,它在计时器上引发PropertyChangedEventHandler:

    using System;
    using System.ComponentModel;
    using System.Timers;
    using System.Windows;
    using System.IO;
    using System.Windows.Threading;
    using System.Windows.Media.Imaging;
    

    namespace WpfApplication7
    {
      public partial class Window1 : Window, INotifyPropertyChanged
      {
        public BitmapImage Image { get; private set; }
        public string Message { get; set; }
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    
        private Timer timer;
    
        public Window1()
        {
          InitializeComponent();
          this.DataContext = this;
    
          this.timer = new Timer { Enabled = true, Interval = 100 };
    
          this.timer.Elapsed += (s, e) =>
          {
            //---happy loading from text file. UI updates :)
            this.Message = File.ReadAllText(@"c:\windows\win.ini").Substring(0, 20);
            PropertyChanged(this, new PropertyChangedEventArgs("Message"));
    
            //---not happy loading a BitmapImage. PropertyChanged unhappy :(
            // (Don't make me have to: ! )
            //Application.Current.Dispatcher.Invoke(
            //DispatcherPriority.Send, new Action(delegate
            //{
    
              this.Image = new BitmapImage(new Uri(@"C:\WINDOWS\Web\Wallpaper\Ascent.jpg"));
    
              //Edit --Ah hah, thanks Daniel ! 
              // DependencyObject-> Freezable-> Animatable-> 
              // ImageSource-> BitmapSource-> BitmapImage
              this.Image.Freeze();  //<--- this will fix it, no need for Dispatcher
    
              //Without Dispatcher or Freeze() ... right here:
              //"The calling thread cannot access this object because a different thread owns it."
              PropertyChanged(this, new PropertyChangedEventArgs("Image"));
            //}));
          };
        }
      }
    }
    

    我知道我可以用“application.current.dispatcher.invoke”来修复这个问题。所以解决问题不是问题。不明白我为什么要这样做是个问题。)

    相似的 问题

    3 回复  |  直到 13 年前
        1
  •  2
  •   Daniel Pratt    16 年前

    我认为这两个场景之间的关键区别在于bitmapimage是一个依赖对象,这意味着它有一个“拥有”线程(创建对象的线程)的概念。当您的主UI线程试图访问在另一个线程上创建(并由其拥有)的BitmapImage对象时…boom!

    另一方面,字符串没有“拥有”线程的概念。

        2
  •  2
  •   Community CDub    8 年前

    对于我们中的那些(像我一样)来看如何在线程间传递位图的人,请查看

    https://stackoverflow.com/a/2672929/745913

    答案是 Freeze() 首先是位图。

        3
  •  1
  •   Scott    16 年前

    我认为这是因为BitmapImage是DispatchHobject的;您正在非UI线程上创建DispatcherObject,因此是异常。由于文本不是线程对象,因此您不会看到文本分配错误。