{"id":624,"date":"2014-05-30T23:30:46","date_gmt":"2014-05-30T21:30:46","guid":{"rendered":"http:\/\/dev.bratched.fr\/fr\/?p=624"},"modified":"2014-05-30T23:30:46","modified_gmt":"2014-05-30T21:30:46","slug":"utilisation-des-images-resources-avec-windows-phone","status":"publish","type":"post","link":"https:\/\/bratched.com\/fr\/2014\/05\/30\/utilisation-des-images-resources-avec-windows-phone\/","title":{"rendered":"Utilisation des Images resources avec Windows Phone"},"content":{"rendered":"<p>Je vous propose un petit retour d\u2019exp\u00e9rience sur l\u2019utilisation des ressources dans le d\u00e9veloppement Windows Phone dans un mod\u00e8le M-V-VM.<\/p>\n<h1>Diff\u00e9rentes fa\u00e7on de coder<\/h1>\n<p>L\u2019image que l\u2019on souhaite afficher provient des ressources et devrait pouvoir s\u2019afficher\u00a0\u00e0 travers une propri\u00e9t\u00e9 Image de type BitmapImage dans un ViewModel. A noter que le dans l&rsquo;exemple ci dessous, UIThreadPool permet d\u2019afficher cette image m\u00eame lorsque celle-ci est aliment\u00e9e depuis un Thread qui tourne en t\u00e2che de fond.<\/p>\n<pre class=\"lang:default decode:true\">SynchronizationContext UIThread = SynchronizationContext.Current;\n\npublic BitmapImage Image\n       {\n           get { return _image; }\n           set\n           {\n               UIThread.Post(_ =&gt;\n                   {\n                       _image = value;\n                       RaisePropertyChanged(\"Image\");\n                   }, null);\n           }\n       }<\/pre>\n<h1>Comment afficher une image incluse dans les ressources de l\u2019application\u00a0?<\/h1>\n<p><span style=\"color: #ff0000\">Attention les 2 premi\u00e8res m\u00e9thodes d\u00e9crites sont \u00e0 proscrire et \u00e0 ne surtout pas reproduire<\/span>. Allez jusqu\u2019\u00e0 la fin de l\u2019article pour voir comment impl\u00e9menter correctement l\u2019affichage d&rsquo;une image ressource. <!--more--><\/p>\n<h2>M\u00e9thode Stream<\/h2>\n<p>Lorsque l\u2019on est habitu\u00e9 \u00e0 r\u00e9cup\u00e9rer une image d\u2019un flux http ou m\u00eame de l\u2019isolated Storage on peut \u00eatre tent\u00e9 d\u2019uniformiser le code et d&rsquo;impl\u00e9menter un Stream pour r\u00e9cup\u00e9rer l&rsquo;image stock\u00e9e dans\u00a0une application :<\/p>\n<pre title=\"M\u00e9thode lecture d'une ressource avec un Stream\" class=\"lang:default decode:true\">public static void LoadResourceImage2(string resourceName, Action&lt;BitmapImage&gt; action)\n        {\n            var resource = Application.GetResourceStream(new Uri(resourceName, UriKind.Relative));\n            {\n                if (resource != null)\n                {\n                    Stream stream = resource.Stream;\n                    {\n                        if (stream != null)\n                        {\n                            Deployment.Current.Dispatcher.BeginInvoke(() =&gt;\n                            {\n                                StreamToBitmapAction(action, stream);\n                                stream.Dispose();\n                                stream = null;\n                            });\n                        }\n                        else\n                            throw new NullReferenceException();\n                    }\n                };\n            }\n        }\n<\/pre>\n<p>L\u2019affichage de l\u2019image\u00a0est retourn\u00e9e par une m\u00e9thode g\u00e9n\u00e9rique qui prend un Stream en entr\u00e9e et\u00a0retourne un BitmapImage en asynchrone dans une Action.<\/p>\n<pre title=\"m\u00e9thode StreamToBitmapAction\" class=\"lang:default decode:true\">private static void StreamToBitmapAction(Action&lt;BitmapImage&gt; actionBitmap, Stream stream)\n        {\n            BitmapImage bitmapImage = new BitmapImage();\n            bitmapImage.CreateOptions = BitmapCreateOptions.None; \n            bitmapImage.SetSource(stream);\n            stream.Dispose();\n            if (actionBitmap != null)\n                actionBitmap(bitmapImage);\n<\/pre>\n<p>L\u2019utilisation de ce Stream peut ensuite se faire de la fa\u00e7on suivante\u00a0:<\/p>\n<pre class=\"lang:default decode:true\">CacheImageService.LoadResourceImage2(resourceName, b =&gt; Image = b ) ;\n<\/pre>\n<h2>M\u00e9thode async et await<\/h2>\n<p>Nous pouvons \u00e9galement utiliser async\/Await pour\u00a0rendre le code plus compr\u00e9hensible et \u00e9viter ce meli-m\u00e9lo d\u2019actions imbriqu\u00e9es. A noter que le code\u00a0peut \u00e9galement fonctionner avec Windows Phone 7 si vous ajoutez le paquet nuget Mycrosoft Async et que vous utilisez \u00e0 minima Visual Studio 2012 (<a title=\"Quelles versions de Visual Studio pour vos APPs Windows Phone ?\" href=\"http:\/\/dev.bratched.fr\/fr\/quelles-versions-de-visual-studio-pour-vos-apps-windows-phone\/\">donc avec Windows 8<\/a>). (Remarque : La version Visual Studio 2010 avec WP7 et le paquet Nuget Async\u00a0semble fonctionner. Cependant l&rsquo;application\u00a0se comportement de fa\u00e7on al\u00e9atoire avec le async\/await donc\u00a0je vous recommande d&rsquo;\u00e9viter, sauf \u00e0 vouloir chercher durant des heures pourquoi votre application n\u2019ex\u00e9cute pas \u00e0 chaque fois le code async\/await). La version Visual Studio 2012 et WP7 fonctionne tr\u00e8s bien en revanche. Voici donc\u00a0un exemple\u00a0d&rsquo;impl\u00e9mentation possible avec le async await.<\/p>\n<pre title=\"Async await example\" class=\"lang:default decode:true\">public static async Task&lt;BitmapImage&gt; LoadResourceImage1(string resourceName)\n        {\n            using (var imageStream = await LoadStreamResourceAsync(resourceName))\n            {\n                return await InitBitmapImageAsync(imageStream);\n\n            }\n            return null;\n        }\n\n        public static async Task&lt;Stream&gt; LoadStreamResourceAsync(string resourceName)\n        {\n\n            return await Task.Factory.StartNew&lt;Stream&gt;(() =&gt;\n            {\n                if (resourceName == null)\n                {\n                    throw new ArgumentException(\"Ressource Name is null\");\n                }\n                Stream stream = null;\n                var resource = Application.GetResourceStream(new Uri(resourceName, UriKind.Relative));\n                {\n                    if (resource != null)\n                        stream = resource.Stream;\n                }\n                return stream;\n            }, CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);\n        }\n\n        private static async Task&lt;BitmapImage&gt; InitBitmapImageAsync(Stream imageStream)\n        {\n            BitmapImage image = null;\n            DispatcherSynchronizationContext dsc = new DispatcherSynchronizationContext(Deployment.Current.Dispatcher);\n            await Task.Factory.StartNew(() =&gt;\n            {\n                dsc.Send(_ =&gt;\n                {\n                    if (imageStream != null)\n                    {\n                        image = new BitmapImage();\n                        image.CreateOptions = BitmapCreateOptions.None;\n                        image.SetSource(imageStream);\n                    }\n                }, null);\n            });\n            return image;\n        }<\/pre>\n<h2>M\u00e9thode new BitmapImage<\/h2>\n<p>Enfin, la derni\u00e8re m\u00e9thode est d\u00e9concertante de simplicit\u00e9\u00a0:<\/p>\n<pre title=\"M\u00e9thode new BitmapImage\" class=\"lang:default decode:true\"> public static void LoadResourceImage3(string resourceName, Action&lt;BitmapImage&gt; action)\n        {\n        Uri uri = new Uri(resourceName, UriKind.RelativeOrAbsolute);\n        Deployment.Current.Dispatcher.BeginInvoke(() =&gt;\n            {\n                if (action != null)\n                    action(new BitmapImage(uri));\n            });            \n        }<\/pre>\n<p>Difficile de faire plus simple Non\u00a0? Remarque : le \u00ab\u00a0<strong>Deployment.Current.Dispatcher.BeginInvoke<\/strong>\u00a0\u00bb est tr\u00e8s important ici pour pouvoir utiliser cette m\u00e9thode dans un Thread en arri\u00e8re plan : new BitmapImage n\u00e9cessite en effet le Thread principal de l&rsquo;interface utilisateur (le ThreadUI) pour s&rsquo;ex\u00e9cuter (l&rsquo;instruction &#8230;Dispatcher&#8230;\u00a0est l\u00e0 pour indiquer que l&rsquo;ex\u00e9cution se fait dans le Thread principal).<\/p>\n<h1>Tests de performance<\/h1>\n<p><a href=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0003.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-626\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0003.png\" alt=\"Capture Benchmark affichage ressource Image\" width=\"349\" height=\"581\" srcset=\"https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0003.png 768w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0003-180x300.png 180w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0003-614x1024.png 614w\" sizes=\"(max-width: 349px) 100vw, 349px\" \/><\/a> Pour savoir laquelle de ces 3 m\u00e9thodes est la plus rapide, j\u2019ai r\u00e9alis\u00e9 un petite application\u00a0Benchmark qui charge 100 images avec\u00a0la r\u00e9partition\u00a0suivante :<\/p>\n<ul>\n<li>50 images png petites (300&#215;300 pour 25ko)<\/li>\n<li>50 images png grandes (1000&#215;1000 pour 437ko)<\/li>\n<\/ul>\n<p>Voici les r\u00e9sultats moyens observ\u00e9s sur un Nokia Lumia 920<\/p>\n<ul>\n<li>M\u00e9thode Stream avec async await\u00a0: <span style=\"color: #ff0000\">5 secondes !<\/span><\/li>\n<li>M\u00e9thode Stream avec Action\u00a0: 3,5 secondes<\/li>\n<li>M\u00e9thode new BitmapImage\u00a0: <span style=\"color: #008000\">0,435 secondes\u00a0! (10 x plus rapide)<\/span><\/li>\n<\/ul>\n<p>Cette petite application permet \u00e9galement de tester en r\u00e9el les images dans des listes de 100 \u00e9l\u00e9ments\u00a0et ainsi\u00a0d&rsquo;appr\u00e9cier l\u2019exp\u00e9rience utilisateur. <strong>La derni\u00e8re impl\u00e9mentation: \u00ab\u00a0new BitmapImage(uri)\u00a0\u00bb, la plus simple, est\u00a0\u00e0 privil\u00e9gier pour charger des ressources images car\u00a0elle offre des performances sans \u00e9quivalent.<\/strong><\/p>\n<h1>Content ou resource ?<\/h1>\n<p>On retrouve pas mal d\u2019articles sur le stockage des ressources sous forme content ou resource. Tous les articles que j&rsquo;ai pu rencontrer recommandent de privil\u00e9gier le stockage de type \u00ab\u00a0content\u00a0\u00bb \u00e0 celui de type \u00ab\u00a0resource\u00a0\u00bb dans nos Apps.<\/p>\n<h2>Ressources \u00ab\u00a0Content\u00a0\u00bb<\/h2>\n<p><a href=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/VSResourceContent.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-627\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/VSResourceContent.png\" alt=\"Visual Studio Ressources Content\" width=\"308\" height=\"165\" srcset=\"https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/VSResourceContent.png 549w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/VSResourceContent-300x161.png 300w\" sizes=\"(max-width: 308px) 100vw, 308px\" \/><\/a> Les images stock\u00e9es avec une \u00ab\u00a0Build Action\u00a0\u00bb = content sont simplement ajout\u00e9es au fichier xap de votre application, et de ce fait, ne sont pas stock\u00e9es dans la dll. Ces images\u00a0sont accessibles directement par une syntaxe de cette forme \u00e0 travers\u00a0une Uri : <strong>\u00ab\u00a0Assets\/Testimage.png\u00a0\u00bb<\/strong> (o\u00f9 <strong>Assets<\/strong> repr\u00e9sente le r\u00e9pertoire o\u00f9 elles sont stock\u00e9es).<\/p>\n<h2>Ressources \u00ab\u00a0Resource\u00a0\u00bb<\/h2>\n<p><a href=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/VSResourceResource.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-629\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/VSResourceResource.png\" alt=\"Visual Studio Ressource Resource\" width=\"299\" height=\"160\" srcset=\"https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/VSResourceResource.png 549w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/VSResourceResource-300x161.png 300w\" sizes=\"(max-width: 299px) 100vw, 299px\" \/><\/a> Les images avec un \u00ab\u00a0Build Action\u00a0\u00bb =\u00a0Resource sont stock\u00e9es directement dans la dll. Pour les utiliser, il faudra\u00a0indiquer le chemin complet de cette dll\u00a0de la fa\u00e7on suivante : <strong>\u00ab\u00a0SampleCacheImageApp;component\/Assets\/Testimage2_res.png\u00a0\u00bb<\/strong> (o\u00f9 <strong>SampleCacheImageApp<\/strong> repr\u00e9sente le nom de l\u2019application et\u00a0<strong>\/Assets<\/strong> le r\u00e9pertoire) La resource \u00e9tant contenue dans la dll, il est alors normal que le chargement au d\u00e9marrage de la dll prenne un tout petit peu plus de temps.<\/p>\n<h2>Tests de performance<\/h2>\n<p>Les tests de performances sont assez troublants. Une premi\u00e8re phase de tests a \u00e9t\u00e9 r\u00e9alis\u00e9e avec l&rsquo;\u00e9mulateur qui a nettement favoris\u00e9 le\u00a0stockage \u00ab\u00a0Content\u00a0\u00bb. Mais\u00a0sur un vrai t\u00e9l\u00e9phone,\u00a0les r\u00e9sultats sont totalement diff\u00e9rents :<\/p>\n<ul>\n<li>Rappel : M\u00e9thode new BitmapImage\u00a0avec les images en\u00a0\u00bb content\u00a0\u00bb : 0,435 secondes\u00a0!<\/li>\n<li>M\u00e9thode new BitmapImage avec image en \u00ab\u00a0resource\u00a0\u00bb\u00a0: <span style=\"color: #008000\"><strong><span style=\"text-decoration: underline\">0,049 secondes !!!<\/span><\/strong>,<\/span> soit presque 10x plus rapide.<\/li>\n<\/ul>\n<p><a href=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0002.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-630\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0002.png\" alt=\"Benchmark ressources buildaction content vs resource\" width=\"349\" height=\"581\" srcset=\"https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0002.png 768w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0002-180x300.png 180w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/wp_ss_20140530_0002-614x1024.png 614w\" sizes=\"(max-width: 349px) 100vw, 349px\" \/><\/a> Alors faut-il syst\u00e9matiquement utiliser des ressources en \u00ab\u00a0Build Action =\u00a0content\u00a0\u00a0\u00bb\u00a0? Personnellement, je viens de passer toutes mes ressources en \u00ab\u00a0Action Build = Resource\u00a0\u00bb\u00a0pour toutes les listes n\u00e9cessitant des centaines de chargements&#8230; Si quelqu&rsquo;un peut me donner une explication\u00a0\u00e0 cette diff\u00e9rence entre \u00e9mulateur et\u00a0mat\u00e9riel? Ce chiffre est-il constat\u00e9 sur\u00a0vos t\u00e9l\u00e9phones ? Ai-je commis une erreur dans l&rsquo;\u00e9criture des\u00a0tests ? Est-ce li\u00e9 au nombre d&rsquo;applications install\u00e9es ? Le code source\u00a0de l\u2019application\u00a0des tests benchmark se trouvent \u00e0 cet emplacement : <a title=\"T\u00e9lecharger les sources\" href=\"http:\/\/1drv.ms\/1keBS7U\" target=\"_blank\" rel=\"noopener noreferrer\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-633\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/download-10-icon-256.png\" alt=\"download-10-icon-256\" width=\"256\" height=\"256\" srcset=\"https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/download-10-icon-256.png 256w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/download-10-icon-256-150x150.png 150w\" sizes=\"(max-width: 256px) 100vw, 256px\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Je vous propose un petit retour d\u2019exp\u00e9rience sur l\u2019utilisation des ressources dans le d\u00e9veloppement Windows Phone dans un mod\u00e8le M-V-VM. Diff\u00e9rentes fa\u00e7on de coder L\u2019image que l\u2019on souhaite afficher provient des ressources et devrait pouvoir s\u2019afficher\u00a0\u00e0 travers une propri\u00e9t\u00e9 Image de type BitmapImage dans un ViewModel. A noter que le dans l&rsquo;exemple ci dessous, UIThreadPool [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[63],"tags":[67,68,69,70,4,71,72],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts\/624"}],"collection":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/comments?post=624"}],"version-history":[{"count":0,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts\/624\/revisions"}],"wp:attachment":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/media?parent=624"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/categories?post=624"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/tags?post=624"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}