DispatcherTimer
在窗口代码内部使用了一个滑块。但是在转移到控制图片缓存和更改幻灯片的单独的单例线程类之后,会弹出一个错误。
帮助克服将图像传输到表单时发生的此错误。我不明白我做错了什么。就像通过委托发布对窗口的访问一样。和类似的结构一样,可以正常使用表单的文本字段,但不存在图像。
MainWindow.xaml.cs 代码:
public partial class MainWindow : Window
{
private delegate void slideCacheUpdateDelegate(int slide, BitmapImage image);
public MainWindow()
{
SlideCache.getInstance.init();
InitializeComponent();
// Initial slide cache updater delegate.
SlideCache.OnRefresh += (s, i) =>
{
if (Dispatcher.CheckAccess())
changeToNextSlide(s, i);
else
Dispatcher.BeginInvoke(new slideCacheUpdateDelegate(changeToNextSlide), new object[] { s, i });
};
SlideCache.getInstance.startSlideShow();
}
private void changeToNextSlide(int slide, BitmapImage image)
{
switch (slide)
{
case 1:
this.imageSlide2.Source = image;
DoubleAnimation animationSlide1 = new DoubleAnimation();
animationSlide1.From = 0;
animationSlide1.To = 1;
animationSlide1.Duration = TimeSpan.FromMilliseconds(500);
this.imageSlide2.BeginAnimation(Canvas.OpacityProperty, animationSlide1);
break;
case 2:
this.imageSlide1.Source = image;
DoubleAnimation animationSlide2 = new DoubleAnimation();
animationSlide2.From = 1;
animationSlide2.To = 0;
animationSlide2.Duration = TimeSpan.FromMilliseconds(500);
this.imageSlide2.BeginAnimation(Canvas.OpacityProperty, animationSlide2);
break;
}
}
}
MainWindow.xaml 代码:
<Window x:Class="SlideShow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Width="200" Height="100" Background="Black">
<Image Name="imageSlide1" Stretch="Fill"/>
<Image Name="imageSlide2" Stretch="Fill"/>
</StackPanel>
</Grid>
</Window>
SlideCache.cs 代码:
public class SlideCache
{
public delegate void Slider(int slide, BitmapImage holder);
public static event Slider OnRefresh;
private static volatile SlideCache INSTANCE;
private static object syncRoot = new Object();
private Thread _threadCache;
private Thread _threadSlide;
private Dictionary<int, BitmapHolder> _images;
private int _currentSlide;
private Boolean _isFirstImage;
private SlideCache()
{
// Do nothing.
}
public void init()
{
initDictionary();
_currentSlide = 0;
_isFirstImage = true;
_threadCache = new Thread(slideCache);
_threadCache.IsBackground = true;
_threadCache.SetApartmentState(ApartmentState.MTA);
_threadCache.Start();
}
private void slideShow()
{
while (true)
{
++_currentSlide;
if (_images[_currentSlide] != null)
{
slideUpdate(_isFirstImage ? 1 : 2, _images[_currentSlide].getImage());
_isFirstImage = !_isFirstImage;
}
Thread.Sleep(3000);
}
}
private void slideCache()
{
while (true)
{
DateTime timestamp = DateTime.Now;
for (int i = 0; i < 10; i++ )
{
int slideId = i + 1;
BitmapHolder holder = _images[slideId];
if (holder == null || holder.getTimestamp() < timestamp)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri("http://example.com/slide_" + slideId);
image.EndInit();
if (holder != null && holder.getImage() != null && image.PixelWidth == 1)
continue;
_images[slideId] = new BitmapHolder(timestamp.AddSeconds(new Random().Next(180, 600)), image);
}
}
Thread.Sleep(1000);
}
}
private void initDictionary()
{
_images = new Dictionary<int, BitmapHolder>(10);
for (int i = 0; i < 10; i++)
_images.Add((i + 1), null);
}
private void slideUpdate(int slide, BitmapImage image)
{
OnRefresh.Invoke(slide, image);
}
public void startSlideShow()
{
_threadSlide = new Thread(slideShow);
_threadSlide.IsBackground = true;
_threadSlide.SetApartmentState(ApartmentState.MTA);
_threadSlide.Start();
}
public void stopSlideShow()
{
_threadSlide.Abort();
}
public void changeToNextSlide(int slide)
{
_currentSlide = slide;
slideUpdate(_isFirstImage ? 1 : 2, _images[_currentSlide].getImage());
_isFirstImage = !_isFirstImage;
}
public Boolean slideShowEnabled()
{
return _threadSlide.ThreadState == ThreadState.Running;
}
public static SlideCache getInstance
{
get
{
if (INSTANCE == null)
{
lock (syncRoot)
{
if (INSTANCE == null)
INSTANCE = new SlideCache();
}
}
return INSTANCE;
}
}
}
BitmapHolder.cs 代码:
public class BitmapHolder
{
private DateTime _timestamp;
private BitmapImage _image;
public BitmapHolder(DateTime timestamp, BitmapImage image)
{
_timestamp = timestamp;
_image = image;
}
public DateTime getTimestamp()
{
return _timestamp;
}
public BitmapImage getImage()
{
return _image;
}
}
启动应用程序时卡住并在VS中弹出以下错误:
用户代码未处理 System.InvalidOperationException调用线程无法访问此对象,因为另一个线程拥有此对象。
解决方案:
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = new MemoryStream(new WebClient().DownloadData("http://example.com/Slide_" + slideId + ".png"));
image.EndInit();
image.Freeze();
感谢MSDN.WhiteKnight帮助我查看本地资源。您的回答最接近解决问题。
PS 问题本身不是从另一个线程访问 UI 元素,而是来自Singleton
类的线程试图转移到 UI 的阻塞资源。那些。有必要将图片作为流并基于它创建资源。进一步如 MSDN 中的Freeze()
共享描述。我希望它对某人有用,tk。问题不是孤立的,至少在 EN SO 是这样。
ImageSource 对象通常只能在创建它的线程中使用。如果您想在其他线程上使用它,则必须
image.Freeze()
在图像完成加载(即调用image.EndInit()
)后调用它(然后它将变得不可变)。请参阅可冻结对象概述此外,如果 ImageSource 指向 HTTP URL,它会被异步加载。因此,在调用 Freeze 之前,需要等待它加载完毕,同时在线程上进行 WPF 事件处理。为此,您需要一个辅助方法:
等待可以这样完成:
但最好从相邻答案中获取建议并在主线程上创建图像(如果操作正确,它实际上可以工作):
从 UI 线程以外的线程与 UI 的任何交互都必须在 UI 线程上调用。检查与 UI 控件有任何交互的所有代码,例如分配图像,并使用 Dispatcher。