天天看点

WPF自定义控件ListView异步加载图片列表

通过改变ListView控件的ItemsControl.ItemContainerStyle样式可以实现各式各样的列表展示效果。如果后台读取文件夹图片资源后最后给ListView的ItemsSource赋值,可以在前台显示加载的图片列表,但是由于加载图片需要时间,会有一段空白停顿,并且要加载的图片越大停顿的时间越长。如下图所示:

WPF自定义控件ListView异步加载图片列表

这里的解决方案是首先异步加载图片地址,再异步读取图片资源的方式来加载本地图片列表。效果如下:

WPF自定义控件ListView异步加载图片列表

具体代码:

1、自定义控件ucListPicView.xaml

<ListView x:Class="ListViewDemo.ucListPicView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ListViewDemo"
             mc:Ignorable="d" SelectionMode="Single" Background="#f0f0f0"
             ItemContainerStyle="{DynamicResource ListViewItemStyle}" 
             ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto">
    <ListView.Resources>
        <Style x:Key="ListViewItemStyle" TargetType="{x:Type ListViewItem}">
            <Setter Property="AllowDrop" Value="True"/>
            <Setter Property="Cursor" Value ="Hand"/>
            <Setter Property="Margin" Value="5,5,5,5"/>
            <Setter Property="Foreground" Value="Black"/>
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListViewItem}">
                        <Grid x:Name="gridContent" HorizontalAlignment="Center" VerticalAlignment="Center">
                            <Grid.RowDefinitions>
                                <RowDefinition ></RowDefinition>
                                <RowDefinition ></RowDefinition>
                            </Grid.RowDefinitions>
                            <Border Name="picBorder" Background="White" BorderBrush="DarkGray" BorderThickness="1">
                                <Image Name="ItemPic" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Source="{Binding ItemPic}" Stretch="{Binding StrethMethod}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                            </Border>
                            <ContentControl Name="ItemName" Content="{Binding ItemName}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Margin="0"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="picBorder" Property="BorderBrush" Value="DarkBlue"></Setter>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="picBorder" Property="BorderBrush" Value="Blue"></Setter>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListView.Resources>
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel>
            </WrapPanel>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
</ListView>
           

2、自定义控件后台代码ucListPicView.xaml.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace ListViewDemo
{
    /// <summary>
    /// ucListPicView.xaml 的交互逻辑
    /// </summary>
    public partial class ucListPicView : ListView
    {
        private BackgroundWorker loadLocalBackgroudworker = null;//后台处理线程

        private ObservableCollection<ListBindData> bindItems = null;// 绑定的资源元素集合

        private int nameLargeLength;//可展示的图片名称字数
        public int NameLargeLength
        {
            get
            {
                return nameLargeLength;
            }

            set
            {
                nameLargeLength = value;
            }
        }

        private string itemSourcePath;//图片路径
        public string ItemSourcePath
        {
            get
            {
                return itemSourcePath;
            }

            set
            {
                itemSourcePath = value;
                LoadLocalImageData();
            }
        }

        public ucListPicView()
        {
            InitializeComponent();
            bindItems = new ObservableCollection<ListBindData>();
        }

        #region 后台线程处理图片数据
        private void LoadLocalImageData()
        {
            this.ItemsSource = null;
            bindItems.Clear();
            if (loadLocalBackgroudworker != null)
            {
                loadLocalBackgroudworker.CancelAsync();
                loadLocalBackgroudworker.DoWork -= loadLocalBackgroudworker_DoWork;
                loadLocalBackgroudworker.RunWorkerCompleted -= loadLocalBackgroudworker_RunWorkerCompleted;
            }
            loadLocalBackgroudworker = new BackgroundWorker();
            loadLocalBackgroudworker.WorkerSupportsCancellation = true;
            loadLocalBackgroudworker.DoWork += loadLocalBackgroudworker_DoWork;
            loadLocalBackgroudworker.RunWorkerCompleted += loadLocalBackgroudworker_RunWorkerCompleted;
            loadLocalBackgroudworker.RunWorkerAsync();
        }
        /// <summary>
        /// 逐个加载图片资源
        /// </summary>
        private void loadLocalBackgroudworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.ItemsSource = bindItems;
            int tempCount = bindItems.Count;
            for (int i = 0; i < bindItems.Count; i++)
            {
                if (tempCount != bindItems.Count)
                {
                    break;
                }
                LoadImageDataByBackground(bindItems[i]);
            }
        }
        /// <summary>
        /// 加载资源路径
        /// </summary>
        private void loadLocalBackgroudworker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                DirectoryInfo theFolder = new DirectoryInfo(ItemSourcePath);
                if (!theFolder.Exists)
                    return;

                string[] files = Directory.GetFiles(ItemSourcePath);
                if (files.Length > 0)
                    Array.Sort(files);

                for (int i = 0; i < files.Length; i++)
                {
                    this.Dispatcher.Invoke(new Action(() =>
                    {
                        if (IsSupport(files[i]))
                        {
                            var tmpBindData = new ListBindData();
                            tmpBindData.ItemName = SetStringLength(GetFileNameEx(files[i]), nameLargeLength);
                            tmpBindData.ItemPath = files[i];
                            tmpBindData.ItemWidth = 160;
                            tmpBindData.ItemHeight = 80;
                            bindItems.Add(tmpBindData);
                        }
                    }));
                }
            }
            catch (Exception ex)
            {

            }
        }
        /// <summary>
        /// 加载资源图片线程
        /// </summary>
        private void LoadImageDataByBackground(ListBindData bindData)
        {
            BackgroundWorker loadImgWorker = new BackgroundWorker();
            loadImgWorker.DoWork += loadImgWorker_DoWork;
            loadImgWorker.RunWorkerCompleted += loadImgWorker_RunWorkerCompleted;
            loadImgWorker.WorkerSupportsCancellation = true;
            loadImgWorker.RunWorkerAsync(bindData);
        }
        /// <summary>
        /// 后台线程完成后释放所占资源
        /// </summary>
        private void loadImgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            var tempWorker = sender as BackgroundWorker;
            if (tempWorker != null)
            {
                tempWorker.Dispose();
                tempWorker = null;
            }
        }
        /// <summary>
        /// 更新资源图片
        /// </summary>
        private void loadImgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var bindData = e.Argument as ListBindData;
            if (bindData != null)
            {
                var temp = this.Dispatcher.BeginInvoke(new Action<ListBindData>((tempData) =>
                {
                    if (tempData != null && !string.IsNullOrEmpty(tempData.ItemPath))
                    {
                        BitmapImage bi = LoadBitmapSourceByPath(tempData.ItemPath);
                        if (bi != null)
                        {
                            tempData.ItemPic = bi;
                            if (bi.PixelWidth < tempData.ItemWidth && bi.PixelHeight < tempData.ItemHeight)
                            {
                                tempData.StrethMethod = Stretch.None;
                            }
                            else
                            {
                                tempData.StrethMethod = Stretch.UniformToFill;
                            }
                        }
                        bi = null;
                    }
                }), DispatcherPriority.Background, bindData);
            }
        }

        #endregion

        #region 辅助方法
        /// <summary>
        /// 设置显示的图片名称
        /// </summary>
        private string SetStringLength(string text, int leng)
        {
            if (text.Length > leng)
            {
                text = text.Substring(0, leng) + "...";
                return text;
            }
            else
            {
                return text;
            }
        }
        /// <summary>
        /// 从文件路径中获取文件名
        /// </summary>
        private static String GetFileName(String fileName)
        {
            if (fileName == null || fileName == "")
                return "";
            return fileName.Substring(fileName.LastIndexOf("\\") + 1);
        }
        /// <summary>
        /// 获得文件后缀名
        /// </summary>
        private static String GetEndFile(String fileName)
        {
            return fileName.Substring(fileName.LastIndexOf(".") + 1);
        }
        /// <summary>
        /// 获得没有后缀的文件名
        /// </summary>
        private static string GetFileNameEx(String fileName)
        {
            try
            {
                return GetFileName(fileName).Substring(0, GetFileName(fileName).LastIndexOf(GetEndFile(fileName)) - 1);
            }
            catch { return ""; }
        }
        /// <summary>
        /// 文件头转换
        /// </summary>
        private enum FileExtension
        {
            JPG = 255216,
            GIF = 7173,
            PNG = 13780,
            SWF = 6787,
            RAR = 8297,
            ZIP = 8075,
            _7Z = 55122,
            VALIDFILE = 9999999
        }
        /// <summary>
        /// 根据图源属性获取扩展名
        /// </summary>
        private string GetBitmapExtensions(string path)
        {
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
                {
                    string fileType = string.Empty;
                    FileExtension extension;
                    using (BinaryReader br = new System.IO.BinaryReader(fs))
                    {
                        byte data = br.ReadByte();
                        fileType += data.ToString();
                        data = br.ReadByte();
                        fileType += data.ToString();
                        try
                        {
                            extension = (FileExtension)Enum.Parse(typeof(FileExtension), fileType);
                        }
                        catch
                        {
                            extension = FileExtension.VALIDFILE;
                        }
                    }
                    return extension.ToString();
                }
            }
            catch (Exception)
            {
                return FileExtension.VALIDFILE.ToString();
            }
        }
        /// <summary>
        /// 受支持的图片格式
        /// </summary>
        private List<string> supportedPicType = new List<string>()
        {
            "jpg",
            "png",
            "bmp",
        };
        /// <summary>
        /// 判断该扩展名是否是受支持的图片类型
        /// </summary>
        private bool IsBmpSupport(string ext)
        {
            return supportedPicType.FindAll((c) => c.Contains(ext.ToLower())).Count > 0;
        }
        /// <summary>
        /// 判断该图片是否受支持
        /// </summary>
        private bool IsSupport(string path)
        {
            return IsBmpSupport(GetBitmapExtensions(path));
        }
        /// <summary>
        /// 从指定路径读取图片源
        /// </summary>
        private BitmapImage LoadBitmapSourceByPath(string path)
        {
            try
            {
                //文件不存在,返回空
                if (!File.Exists(path))
                {
                    return null;
                }

                BitmapImage bi = new BitmapImage();
                using (FileStream strream = new FileStream(path, FileMode.Open, FileAccess.Read))
                {
                    using (BinaryReader br = new BinaryReader(strream))
                    {
                        byte[] bytes = br.ReadBytes((int)strream.Length);
                        bi.BeginInit();
                        bi.StreamSource = new MemoryStream(bytes);
                        bi.EndInit();
                    }
                }
                return bi;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        #endregion
    }

    public class ListBindData : INotifyPropertyChanged
    {
        public ListBindData()
        {
            
        }

        private ImageSource itemPic;
        private Stretch strethmethod = Stretch.Uniform;

        /// <summary>
        /// 素材展示预览图
        /// </summary>
        public ImageSource ItemPic
        {
            get
            {
                return itemPic;
            }
            set
            {
                if (itemPic != value)
                {
                    itemPic = value;
                    OnPropertyChanged("ItemPic");
                }
            }
        }

        public Stretch StrethMethod
        {
            get
            {
                return strethmethod;
            }
            set
            {
                if (strethmethod != value)
                {
                    strethmethod = value;
                    OnPropertyChanged("StrethMethod");
                }
            }
        }

        /// <summary>
        /// 图片名称
        /// </summary>
        public string ItemName
        {
            get;
            set;
        }

        /// <summary>
        /// 图片的本地路径
        /// </summary>
        public string ItemPath
        {
            get;
            set;
        }

        /// <summary>
        /// 图片的宽
        /// </summary>
        public int ItemWidth
        {
            get;
            set;
        }

        /// <summary>
        /// 图片的高
        /// </summary>
        public int ItemHeight
        {
            get;
            set;
        }

        private void OnPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}
           

3、主窗体MainWindow.xaml

<Window x:Class="ListViewDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ListViewDemo"
        mc:Ignorable="d" WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="450" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition Height="40"></RowDefinition>
        </Grid.RowDefinitions>
        <local:ucListPicView x:Name="picListView" Width="180" Margin="10"></local:ucListPicView>
        <Button x:Name="btnOpenFile" Content="打开" Grid.Row="1" Width="100" Margin="10,0,10,10" Click="btnOpenFile_Click"></Button>
    </Grid>
</Window>
           

4、主窗体后台代码

using System;
using System.Windows;
using System.Windows.Forms;

namespace ListViewDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnOpenFile_Click(object sender, RoutedEventArgs e)
        {
            FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
            folderBrowserDialog.Description = "选择图片所在的文件夹";
            folderBrowserDialog.ShowNewFolderButton = false;
            folderBrowserDialog.RootFolder = Environment.SpecialFolder.MyComputer;//设置目录,可注掉
            folderBrowserDialog.ShowDialog();
            if (folderBrowserDialog.SelectedPath == string.Empty)
            {
                return;
            }
            string selectFolder = folderBrowserDialog.SelectedPath;
            this.picListView.NameLargeLength = 20;
            this.picListView.ItemSourcePath = selectFolder;
        }
    }
}
           

继续阅读