Recently, you are working on a WPF project. A requirement in the project is to display the project picture as a list. Some of these images exist on the internet and some exist on the local disk. Files that exist on the local disk say, mostly pictures that exist in the network. Because images that exist in the network are time-consuming to load, it can cause the interface to stutter if the image control is bound directly to the URI attribute. In order to provide a better experience, there is a need for special effects like images loaded in Web pages.
After two days of research, I looked over the love wallpaper HD for Windows source code (you know). Finally finished this function. The effect of the implementation is as shown in the illustration on the right:
Displays a list of pictures, which is definitely a listbox. By customizing the Itemspanel and ItemTemplate of the ListBox, you can implement the sub-horizontal of the listbox and set the child as a picture.
When you do a WPF project, we usually assign values to the properties of the control by binding, so we first construct a data source and make a viewmodel.
The data source is set to a simple text file (list.txt). Each line, a picture address.
The sample code is as follows:
Http://img11.360buyimg.com//n3/g2/m00/06/1d/rbegevakffuiaaaaaab54f55qh8aabwrqlxlr0aahn4106.jpgc:\users\soar\ Pictures\lovewallpaper\18451,106.jpghttp://img12.360buyimg.com//n3/g1/m00/06/1d/ rbegdvakffqiaaaaaab0mdavaccaabwrqmcudwaahsw197.jpgc:\users\soar\pictures\lovewallpaper\367448,106.jpghttp:// Img13.360buyimg.com//n3/g2/m00/06/1d/rbegelakffiiaaaaaadvr1yd_x0aabwrqklu2maanvf537.jpgc:\users\soar\pictures\ Lovewallpaper\359090,106.jpghttp://img10.360buyimg.com//n3/g5/m02/1c/00/ Rbeic1akfe8iaaaaaabdtsbt3bqaafecqah13kaaepo445.jpghttp://img11.360buyimg.com//n3/g3/m00/06/1d/ Rbege1akfgiiaaaaaacfm_mhwryaabwrqmmk8kaaj-z240.jpghttp://img12.360buyimg.com//n3/g3/m00/06/1d/ Rbegffakfhqiaaaaaabhekje6jqaabwrqogieuaaees965.jpghttp://img13.360buyimg.com//n3/g2/m00/06/1d/ Rbegelakfegiaaaaaaclvhjsnqoaabwrqj0ktiaakxw818.jpghttp://img14.360buyimg.com//n3/g1/m00/06/1d/ Rbegdlakfe4iaaaaaabqsm9egeoaabwrqj4wiwaafdi883.jpghttp://img10.360buyimg.com//n3/g3/m00/06/1d/ Rbege1akfgqiaaaaaacbzc_hevaaabwrqm293saaif9407.jpghttp://img11.360buyimg.com//n3/g3/m00/06/1d/rbege1akfgkiaaaaaac_6a3anhwaabwrqofht8aamaa406.jpghttp:// img12.360buyimg.com//n3/g5/m02/1c/00/rbedilakfeaiaaaaaacdjbyljh0aafecqauismaaj08326.jpghttp:// img13.360buyimg.com//n3/g1/m00/06/1d/rbegdvakfe4iaaaaaacxzwgdqfoaabwrqkpcmeaajfn685.jpghttp:// img12.360buyimg.com//n3/g3/m00/06/1d/rbege1akfgciaaaaaac5nk25heqaabwrqoca3saalm0258.jpghttp:// img14.360buyimg.com//n3/g2/m00/06/1d/rbegefakfduiaaaaaaczblnax_kaabwrqj0zwgaajmg566.jpghttp:// img14.360buyimg.com//n3/g2/m00/06/1d/rbegefakfewiaaaaaacfqqvjlnoaabwrqoirgwaaj_b820.jpghttp:// Img11.360buyimg.com//n3/g2/m01/06/1d/rbegefakffmiaaaaaacgy4epzwyaabwrgafhyiaakb7880.jpg
Here is the code for ViewModel (MainViewModel.cs):
Using system;using system.collections.generic;using system.linq;using system.text;using System.IO;namespace webimagelist{public class Mainviewmodel {public Mainviewmodel () { using (var sr = new StreamReader ("List.txt")) { this._images = new list<string> (); while (!SR. Endofstream) { this._images.add (Sr.) ReadLine ()); }}} Private list<string> _images; Public list<string> Images { get {return _images;} set {_images = value;}}}}
In the picture, we can see that there is a loading effect, our next task is to make this effect. (This, I copy of the. )
The original picture is as follows:
WPF native does not support GIF format pictures, and GIF format picture color is also very limited, so this loading effect is a PNG image plus a rotation animation completed. First, we want to add a user control. There is only one image child control in this user control. In a XAML file, the URI of the image control is set to this picture, and the animation starts after the image loading is complete. The XAML (WAITINGPROGRESS.XAML) code is as follows:
<usercontrol x:class= "webimagelist.waitingprogress" xmlns= "Http://schemas.microsoft.com/winfx/2006/xaml/pre Sentation "xmlns:x=" Http://schemas.microsoft.com/winfx/2006/xaml "xmlns:mc=" Http://schemas.openxml formats.org/markup-compatibility/2006 "xmlns:d=" http://schemas.microsoft.com/expression/blend/2008 " mc:ignorable= "D" d:designheight= "d:designwidth=" > <UserControl.Resources> < ; Storyboard x:key= "Waiting" name= "Waiting" > <doubleanimation storyboard.targetname= "spinnerrotate" Storybo Ard. Targetproperty= "(rotatetransform.angle)" from= "0" to= "359" duration= "0:0:02" repeatbehavior= "Forever"/> </St oryboard> </UserControl.Resources> <image name= "Image" source= "loading.png" rendertransformorigin= "0.5, 0.5 "stretch=" None "loaded=" Image_loaded_1 "> <Image.RenderTransform> <rotatetransform x:name = "Spinnerrotate" Angle= "0"/> </Image.RenderTransform> </Image></UserControl>
The corresponding CS code (WaitingProgress.xaml.cs) is as follows:
Using system;using system.collections.generic;using system.linq;using system.text;using System.Windows;using System.windows.controls;using system.windows.data;using system.windows.documents;using System.Windows.Input;using System.windows.media;using system.windows.media.animation;using system.windows.media.imaging;using System.windows.navigation;using system.windows.shapes;namespace webimagelist{//<summary>//WaitingProgres S.xaml Interactive logic///</summary> public partial class Waitingprogress:usercontrol {private Storyboard Story Public waitingprogress () {InitializeComponent (); This.story = (base. resources["Waiting"] as Storyboard); } private void Image_loaded_1 (object sender, RoutedEventArgs e) {this.story.Begin (this.image, tr UE); } public void Stop () {base. Dispatcher.begininvoke (new Action () = {This.story.Pause (this.image); Base. Visibility = System.Windows.Visibility.Collapsed; })); } }}
Next, we should analyze how to get the picture. Because a picture may exist on a local disk or there may be a network, you need to use different methods to get the picture based on different storage locations (PS: If you use BitmapImage as the image source, and the image is loaded through a URI, when the source for this image is set asynchronously , an error occurs that the object does not belong to this thread {This is probably the error. }。 This error is bitmapimage errors, not the image control error. Specific reasons, not quite understand. )
In, we can see that these pictures are not displayed all at once, but one by one. Here, it is necessary to use a generic first-in, first-out set queue<t>. When you bind a source to a list, the data is loaded into the queue collection. Then, create a background thread, from which the background thread constantly pulls data from the collection and translates to BitmapImage. Also, the image control will be notified when the conversion is complete, and I'll download it for you, and you can do it yourself. When all the pictures are downloaded, the daemon does not stop, but waits, and as soon as a new binding comes in, the thread activates and continues to do what it needs to do. Here we are going to use a type: AutoResetEvent. Principle is such a principle, and see the Code (ImageQueue.cs) as follows:
Using system;using system.collections.generic;using system.linq;using system.text;using System.Net;using System.windows.controls;using system.windows.media.imaging;using system.threading;using System.IO;namespace webimagelist{///<summary>///Picture Download queue///</summary> public static class Imagequeue {# Region auxiliary class private class Imagequeueinfo {public Image image {get; set;} Public String URL {get; set;} } #endregion public delegate void Complatedelegate (Image I, String u, BitmapImage b); public static event Complatedelegate Oncomplate; private static AutoResetEvent autoevent; private static queue<imagequeueinfo> Stacks; Static Imagequeue () {imagequeue.stacks = new queue<imagequeueinfo> (); Autoevent = new AutoResetEvent (true); Thread t = new Thread (new ThreadStart (imagequeue.downloadimage)); t.name = "Download diagramFilm "; T.isbackground = true; T.start (); } private static void Downloadimage () {while (true) {Imagequeueinfo T = NULL; Lock (Imagequeue.stacks) {if (ImageQueue.Stacks.Count > 0) { t = ImageQueue.Stacks.Dequeue (); }} if (t! = null) {uri uri = new Uri (T.url); BitmapImage image = null; try {if ("http"). Equals (URI. Scheme, Stringcomparison.currentcultureignorecase)) {//If it is an HTTP download file WebClient WC = new WebClient (); using (var ms = new MemoryStream (WC). Downloaddata (URI))) {image = new BitmapImage (); Image. BeginInit (); Image. cacheoption = Bitmapcacheoption.onload; Image. Streamsource = ms; Image. EndInit (); }} else if ("file"). Equals (URI. Scheme, Stringcomparison.currentcultureignorecase)) {using (var fs = NE W FileStream (T.url, FileMode.Open)) {image = new BitmapImage (); Image. BeginInit (); Image. cacheoption = Bitmapcacheoption.onload; Image. Streamsource = FS; Image. EndInit (); }} if (image! = null) {i F (image. Canfreeze) image. Freeze (); T.image.dispatcher.begininvoke (New Action<imagequeueinfo, bitmapimage> ((i, BMP) = {if (imagequeue.oncomplate! = null) { Imagequeue.oncomplate (I.image, I.url, BMP); }}), new object[] {t, image}); }} catch (Exception e) {System.Windows.Me Ssagebox.show (E.message); Continue }} if (ImageQueue.Stacks.Count > 0) continue; Autoevent.waitone (); }} public static void Queue (Image img, String URL) {if (String.IsNullOrEmpty (URL)) retur N Lock (Imagequeue.stacks) {ImageQueue.Stacks.Enqueue (new imagequeueinfo {urL = URL, image = img}); ImageQueue.autoEvent.Set (); } } }}
In the code, we define a delegate and an event. Notify the image control of the heavy responsibility, will be handed over to the two-dollar generals.
Next, let's do the effect of the picture Display list (ListBox). We know that in a grid control, if a child control does not set any position or size property, the child control will fill the grid. So we're going to customize the ItemTemplate of the listbox. Let it contain a grid control. This grid control has two child controls, one is the progress bar control that you just made, no positioning, size properties. The other is the image control, which sets the Size property.
<window x:class= "Webimagelist.mainwindow" xmlns= "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x= "Http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local= "clr-namespace:webimagelist" title= "Ma Inwindow "height=" "width=" "windowstartuplocation=" Centerscreen "> <StackPanel> <button conten t= "Loading pictures" click= "Button_click_1" ></Button> <listbox itemssource= "{Binding Images}" Scrollviewer.horizon talscrollbarvisibility= "Disabled" > <ListBox.ItemTemplate> <DataTemplate> <grid > <local:WaitingProgress/> <image stretch= "U Niformtofill "width=" "height=" "local:imagedecoder.source=" {Binding} "></Image> </gri d> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemsPanel> <itemspaneltemplate> <wrappanel name= "WrapPanel" horizontalalignment= "Stretch"/> < ;/itemspaneltemplate> </ListBox.ItemsPanel> </ListBox> </stackpanel></window& Gt
First, let's show the progress bar control, then bind the data source to the ListBox, and press the downloaded data into the download queue, and register the downloaded event to hide the progress bar when the event executes. Set the Source property to the value taken for the image control and add a gradient animation.
To implement the above functions, we need to add a dependency property.
Using system;using system.collections.generic;using system.linq;using system.text;using System.Windows;using System.windows.controls;using system.windows.media.animation;using System.windows.media.imaging;namespace webimagelist{public static class Imagedecoder {public static readonly DependencyProperty sourceproperty; public static string GetSource (image image) {if (image = = null) {throw New ArgumentNullException ("Image"); } return (string) image. GetValue (Imagedecoder.sourceproperty); public static void SetSource (image image, String value) {if (image = = null) { throw new ArgumentNullException ("Image"); } image. SetValue (imagedecoder.sourceproperty, value); } static Imagedecoder () {imagedecoder.sourceproperty = dependencyproperty.registerattached ("Sour Ce ", typeof (String), typeof (ImagedeCoder), New PropertyMetadata (New Propertychangedcallback (imagedecoder.onsourcewithsourcechanged))); Imagequeue.oncomplate + = new Imagequeue.complatedelegate (imagedecoder.imagequeue_oncomplate); The private static void Imagequeue_oncomplate (Image I, String u, BitmapImage b) {//system.windows. MessageBox.Show (U); String Source = Imagedecoder.getsource (i); if (Source = = U.tostring ()) {i.source = b; Storyboard Storyboard = new Storyboard (); DoubleAnimation doubleanimation = new DoubleAnimation (0.0, 1.0, New Duration (Timespan.frommilliseconds (500.0))); Storyboard.settarget (DoubleAnimation, i); Storyboard.settargetproperty (DoubleAnimation, New PropertyPath ("Opacity", New Object[0])); Storyboard. Children.add (DoubleAnimation); Storyboard. Begin (); if (I.parent is Grid) { Grid Grid = I.parent as grid; foreach (var c in grid. Children) {if (c is waitingprogress && c! = null) {(c as waitingprogress). Stop (); Break }}}}} private static void Onsourcewithsourcechanged (Depende Ncyobject o, DependencyPropertyChangedEventArgs e) {imagequeue.queue (Image) o, (string) e.newvalue); } }}
At this point, the demo is done. The time I contacted WPF was not short, but the real use was not much. This article is used in a lot of things, are doing this demo when learning, welcome everyone to communicate with me.
Also attached:http://files.cnblogs.com/Soar1991/WebImageList.rar
WPF loading pictures asynchronously with load in animation