【WPF】请注意一个 异步绑定 的陷阱!

WPF的异步绑定在很大程度上简化了延迟较大的绑定操作,大大提高了生产力。但在这个糖果里存在一个天大的陷阱。

如果你不能发现这个陷阱,轻则导致延迟严重,重则导致程序崩溃。

 

请看第一个案例: 

<Image Source="http://images.cnblogs.com/adminlogo.gif" />

这段代码贴入VS2010以后,第一反应就是VS会假死一段时间。然后在Design窗口才会显示出园子的logo。运行段代码,程序也要假死一段时间才能正常工作。原因就是通过网络加载这个图片的时间过长。在我这需要10+秒。

 

看到这里,你一定会说,使用异步操作就可以了。

请看第二个案例: 

前台代码: 

<Image Source="{Binding IsAsync=True}"/>

后台代码: 

        public MainWindow()

        {

            InitializeComponent();

            this.DataContext = "http://images.cnblogs.com/adminlogo.gif";

        }

看上去这段代码能够完美的工作,可实际上效果和案例一完全一样。除了不会让VS假死一段时间以外。

原因是因为你给UI Thread传递的只是一个stringUI Thread调用Async Thread把它转化为Uri以后,继续用UI Thread去下载图片,自然还是会导致阻塞。

 

那么自然就会想起第三种方案。

请看第三个案例: 

前台代码: 

<Image Source="{Binding Image, IsAsync=True}"/>

后台代码: 

       public MainWindow()

        {

            InitializeComponent();

            this.DataContext = new VM();

        }

 

        public class VM

        {

            public ImageSource Image

            {

                get

                {

                    return new BitmapImage(new Uri("http://images.cnblogs.com/adminlogo.gif"));

                }

            }

        }

这样的代码看上去绝对是完美了。

运行,并没有发现阻塞了UI Thread,正等待图片异步下载完毕的时候,突然弹出异常:

 The calling thread cannot access this object because a different thread owns it.

WPFUI Thread是不能使用非UI Thread创建的Image的。

 

下面正式推出我们的终极解决方案: 

前台代码不变,后台代码修改为

        public MainWindow()

        {

            InitializeComponent();

            this.DataContext = new VM(this.Dispatcher);

        }

 

        public class VM

        {

            Dispatcher _dispatcher = null;

 

            public VM(Dispatcher dispatcher)

            {

                _dispatcher = dispatcher;

            }

 

            public ImageSource Image

            {

                get

                {

                    if (_dispatcher == null)

                        return null;

                    BitmapImage bi = null;

                    try

                    {

                        WebClient wc = new WebClient();

                        MemoryStream ms = new MemoryStream(wc.DownloadData("http://images.cnblogs.com/adminlogo.gif"));

                        _dispatcher.Invoke(new Action(() =>

                        {

                            bi = new BitmapImage();

                            bi.BeginInit();

                            bi.StreamSource = ms;

                            bi.EndInit();

                        }), null);

                    }

                    catch { }

                    return bi;

                }

            }

        }

现在你还会发现任何问题吗? 

posted @ 2011-02-24 01:00  Aimeast  阅读(2434)  评论(2编辑  收藏  举报