RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 866956
Accepted
Rootware
Rootware
Asked:2020-08-10 17:24:28 +0000 UTC2020-08-10 17:24:28 +0000 UTC 2020-08-10 17:24:28 +0000 UTC

WPF 无法更新窗口元素

  • 772

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 是这样。

c#
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    MSDN.WhiteKnight
    2020-08-13T19:26:47Z2020-08-13T19:26:47Z

    ImageSource 对象通常只能在创建它的线程中使用。如果您想在其他线程上使用它,则必须image.Freeze()在图像完成加载(即调用image.EndInit())后调用它(然后它将变得不可变)。请参阅可冻结对象概述


    此外,如果 ImageSource 指向 HTTP URL,它会被异步加载。因此,在调用 Freeze 之前,需要等待它加载完毕,同时在线程上进行 WPF 事件处理。为此,您需要一个辅助方法:

    using System.Windows.Threading;
    
    public static void DoWpfEvents()
    {
          DispatcherFrame frame = new DispatcherFrame();
          Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
               new DispatcherOperationCallback((f) =>
               {
                   ((DispatcherFrame)f).Continue = false; return null;
               }), frame);
          Dispatcher.PushFrame(frame);
     } 
    

    等待可以这样完成:

    BitmapImage image = new BitmapImage();
    image.BeginInit();
    image.UriSource = new Uri("http://example.com/image_" + slideId.ToString() + ".png");
    image.CacheOption = BitmapCacheOption.OnLoad;
    image.EndInit();
    
    while (image.IsDownloading) { DoWpfEvents(); Thread.Sleep(100); }                     
    
    image.Freeze();
    

    但最好从相邻答案中获取建议并在主线程上创建图像(如果操作正确,它实际上可以工作):

    BitmapImage image=null;
    
    Application.Current.Dispatcher.Invoke(() => {
        image = new BitmapImage();
        image.BeginInit();
        image.UriSource = new Uri("http://example.com/image_" + slideId.ToString() + ".png");        
        image.EndInit();
    
    });
    
    • 1
  2. LiptonDev
    2020-08-10T20:59:03Z2020-08-10T20:59:03Z

    从 UI 线程以外的线程与 UI 的任何交互都必须在 UI 线程上调用。检查与 UI 控件有任何交互的所有代码,例如分配图像,并使用 Dispatcher。

    App.Current.Dispatcher.Invoke(() => imageSlide1.Source = CreateImage());
    
    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5