宝妈的需求“一仔播放器” WPF 的开源项目(三 视频列表,涉及遍历、异步Task、接收RelayCommand、委托Invoke、interactivity事件)

目录

文件分析

数据实体

遍历加载

        添加数据组对象

        遍历文件夹存入对象

        引用资源的方式       

设计视图

       界面UI数据绑定 

        控件事件Command

        RelayCommand命令

异步Task

委托 Dispatcher.Invoke

界面加载事件绑定命令

运行效果


文件分析

        上篇构建好了项目,紧接着就要把视频列表展示出来,这不是什么难事,不过我们先对宝妈视频文件夹的进行分析,了解宝妈的存放方式。

        

        从图中可以看出宝妈的视频文件夹存放还是比较复杂,有单层文件夹存放也有多层文件夹,而且会有空的文件夹,另外视频文件还不一样有 mp4 、mkv、avi ,唯独没有图片文件,毕竟宝妈说过视频列表最好有图片,......(项目协商).....最终和宝妈商量让她辛苦一点,自己截一张图放到和视频文件一起就可以了 格式 jpg、png 都行,接下来要看怎么读取了。

数据实体

        创建数据实体 CardModel.cs 

namespace YiZiPlayer.Model
{
    /// <summary>
    /// 数据模型
    /// </summary>
    public class CardModel
    {
        // <summary>
        /// 标题
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// 文件夹路径
        /// </summary>
        public string FolderPath { get; set; }

        /// <summary>
        /// 图片路径
        /// </summary>
        public string PicturePath { get; set; }
    }
}
遍历加载
        添加数据组对象

        加载到的数据需要展示在界面上的,需要到 MainViewModel.cs 加入数组属性   ObservableCollection<CardModel> VideoList ,存放数据,你会问为什么不是 List<CardModel> VideoList ,理由就是 数组 List  没办法动态响应到界面

        ObservableCollection<CardModel> videoList;
        /// <summary>
        /// 文件列表
        /// </summary>
        public ObservableCollection<CardModel> VideoList
        {
            get { return videoList; }
            set { videoList = value; RaisePropertyChanged(); }
        }
        遍历文件夹存入对象

        通过对文件夹的分析,我打算使用文件夹的名称做为标题,而没有视频文件的文件夹跳过,如果有图片文件取第一张作为封面,没有则显示默认图片,如果有子文件夹继续递归

        /// <summary>
        /// 遍历文件夹,装载数据
        /// </summary>
        void GetVideoList(string path)
        {

            if (Directory.Exists(path))
            {

                string[] _folders = Directory.GetDirectories(path); //查询子目录

                string[] _fileVideos = Utils.GitFileVideos(path); //查询视频文件 

                string[] _filePictures = Utils.GitFilePictures(path);  //查询图片文件


                //是否存在视频文件
                if (_fileVideos.Length != 0)
                {
                    CardModel cardModel = new CardModel();

                    //存在图片文件
                    if (_filePictures.Length != 0)
                    {
                        cardModel.PicturePath = _filePictures[0]; //第一张图片为封面
                    }
                    else
                    { 
                        cardModel.PicturePath = @"pack://application:,,,/YiZiPlayer;component/Resource/null.png"; //无图片时应用项目里图片素材
                    }

                    cardModel.FolderPath = path; //保存路径

                    cardModel.Title = System.IO.Path.GetFileName(path); //文件夹名称作为标题

                    VideoList.Add(cardModel);
                }
                else if (_folders.Length != 0)
                {
                    //子文件夹递归
                    foreach (string str in _folders)
                    {     
                        GetVideoList(str);
                    }
                }
            }
        }
        引用资源的方式       

        这里边有个知识点,如何引用资源文件  pack://application:,,,/YiZiPlayer;........ 上面的代码段中有写到,没有图片就显示 “默认图片”, pack://application:,,,/(项目名);component/(路径)

        

设计视图

        既然使用了 HandyControl 控件库,那就优先在里边找找,这能省不少事,经过和宝妈的框图比对卡片控件比较符合如图,不过还需要改进一下,下边的小字 改成我们的两个按钮 播放、打开目录

       界面UI数据绑定 

       MainWindow.xaml 加入代码

       ListBox 中指定数据源  ItemsSource="{Binding VideoList} 

       每一项是绑定 都使用  Binding 进行,具体看代码

<Grid >  
        
        <ListBox Margin="32" Padding="0,15" hc:ScrollViewer.IsInertiaEnabled="True" BorderThickness="0" Style="{StaticResource WrapPanelHorizontalListBox}"  ItemsSource="{Binding VideoList}">
            <ListBox.ItemTemplate>
                <DataTemplate DataType="data:CardModel">
                    <hc:Card MaxWidth="240" BorderThickness="0" Effect="{StaticResource EffectShadow2}" Margin="8" Footer="{Binding Title}">
                        
                        <Border CornerRadius="4,4,0,0" Style="{StaticResource BorderClip}">

                            <Image Width="240" Height="240" Source="{Binding PicturePath}" Stretch="Uniform" >
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="MouseDown" >
                                        <i:InvokeCommandAction Command="{Binding Source={StaticResource Locator},Path=Main.PlayFileCommand}" CommandParameter="{Binding DataContext.FolderPath,RelativeSource={RelativeSource AncestorType=hc:Card}}" />
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </Image>

                        </Border>
                        <hc:Card.FooterTemplate>
                            <DataTemplate>
                                <StackPanel Margin="10">
                                    <TextBlock TextWrapping="WrapWithOverflow"  TextTrimming="CharacterEllipsis" Height="60"  Style="{StaticResource TextBlockLarge}" FontSize="20" Text="{Binding DataContext.Title,RelativeSource={RelativeSource AncestorType=hc:Card}}" HorizontalAlignment="Left"/>

                                    <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                                        <Button Content="播放" Margin="5,5,60,5" Style="{StaticResource ButtonSuccess}" Command="{Binding Source={StaticResource Locator},Path=Main.PlayFileCommand}" CommandParameter="{Binding DataContext.FolderPath,RelativeSource={RelativeSource AncestorType=hc:Card}}"/>

                                        <Button Content="打开文件夹" Margin="5" Style="{StaticResource ButtonDashed}" Command="{Binding Source={StaticResource Locator},Path=Main.OpenFolderCommand}" CommandParameter="{Binding DataContext.FolderPath,RelativeSource={RelativeSource AncestorType=hc:Card}}"  />
                                    </StackPanel>
                                    
                                </StackPanel>
                            </DataTemplate>
                        </hc:Card.FooterTemplate>
                    </hc:Card>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
        控件事件Command

         单独整一小段来说明什么是控件事件(毕竟要照顾一下初学者),简单的说就是控件的加载、点击、鼠标滑过等等都是事件,事件通常结合命令使用,比如 按钮点击后 要执行什么命令,但不是所有的控件都提供事件,所以WPF提供了 System.Windows.Interactivity.dll 组件,让我们可以给控件添加事件,但前提是先要引用
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 

/*例如图片的点击事件用到组件*/
 <Image Width="240" Height="240" Source="{Binding PicturePath}" Stretch="Uniform" >
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="MouseDown" >
                                        <i:InvokeCommandAction Command="{Binding Source={StaticResource Locator},Path=Main.PlayFileCommand}" CommandParameter="{Binding DataContext.FolderPath,RelativeSource={RelativeSource AncestorType=hc:Card}}" />
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </Image>

/*按钮的自带点击事件*/
<Button Content="打开文件夹" Margin="5" Style="{StaticResource ButtonDashed}" Command="{Binding Source={StaticResource Locator},Path=Main.OpenFolderCommand}" CommandParameter="{Binding DataContext.FolderPath,RelativeSource={RelativeSource AncestorType=hc:Card}}"  />
                                   
        RelayCommand命令

        前端控件事件所触发的后台命令必须由 RelayCommand 去定义,事件可以传参数也可以不传

例如在 MainViewModel.cs 定义的命令

        /// <summary>
        /// 加载数据 无参数
        /// </summary>
        public RelayCommand LoadDataCommand
        {
            get
            {
                var command = new RelayCommand(() =>
                {
                    //代码段......
                });

                return command;
            }
        }
        /// <summary>
        /// 点击打开文件夹,路径参数
        /// </summary>
        public RelayCommand<string> OpenFolderCommand
        {
            get
            {
                var command = new RelayCommand<string>((string path) =>
                {
                    System.Diagnostics.Process.Start(path);
                });

                return command;
            }
        }

        /// <summary>
        /// 点击播放按钮,路径参数
        /// </summary>
        public RelayCommand<string> PlayFileCommand
        {
            get
            {
                var command = new RelayCommand<string>((string path) =>
                {
                    //代码段......
                });

                return command;
            }
        }

异步Task

        用到异步因为宝妈的视频文件夹数据太多了,一开始计划是遍历文件夹,同时显示界面,不成想数据量大,界面直接卡住了,故而用到 Task 异步线程

委托 Dispatcher.Invoke

        异步Task 是解决了读数据界面卡顿的问题,但异步线程中的数据变更是没办法响应到界面,会报异常错误,所以需要用到了 委托

具体代码

        /// <summary>
        /// 加载数据
        /// </summary>
        public RelayCommand LoadDataCommand
        {
            get
            {
                var command = new RelayCommand(() =>
                {
                    //异步线程,加载数据防止卡顿
                    Task task = new Task(() =>
                    {
                        try
                        {
                            Thread.Sleep(1500);                            
                            Application.Current.Dispatcher.Invoke(new Action(() => {
                                LoadData();
                            }));

                        }
                        catch (Exception ex)
                        {
                            Growl.Error(ex.Message);
                        }
                    });

                    task.Start();
                });

                return command;
            }
        }
界面加载事件绑定命令

        视频列表相关代码写到这里也快收尾了,这一步是指界面打开事件时去执行后台加载数据的命令,Command="{Binding LoadDataCommand}"

<hc:Window x:Class="YiZiPlayer.View.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:hc="https://handyorg.github.io/handycontrol" 
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"           
        DataContext="{Binding Source={StaticResource Locator},Path=Main}"           
        WindowState="Maximized" 
        WindowStartupLocation="CenterScreen"
        Icon="/Resource/favicon32.ico"        
        mc:Ignorable="d"
        Title="一仔播放器" Height="768" Width="1366">    
    
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadDataCommand}"  />
         </i:EventTrigger>
    </i:Interaction.Triggers>
运行效果

效果不错吧!在看看框图基本一致

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值