一、CefSharp文件下载分析

查看ChromiumWebBrowser类发现cef数据下载处理在IDownloadHandler中进行,但并未找到相应的实现类,故我们需要自己实现DownloadHandler

创建CustomDownloadHandler类并实现IDownloadHandler接口

public class CustomDownloadHandler : IDownloadHandler
{
public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
IBeforeDownloadCallback callback)
{
throw new System.NotImplementedException();
} public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
IDownloadItemCallback callback)
{
throw new System.NotImplementedException();
}
}

IDownloadHandler中声明了两个方法,从方法命名来看 OnBeforeDownload和OnDownloadUpdated一个是在下载之前处理,一个在下载中处理

1、首先看第一个方法OnBeforeDownload

DownloadItem 类如下图这里给我们提供了下载文件的相关信息(文件建议名称、源地址、文件大小、当前大小等)

查看IBeforeDownloadCallback接口定义

IsDisposed 判断当前下载回调对象是否已释放

Continue 继续下载 第一个参数为下载路径、第二个数是否弹出保存对话框

2、接下来我们看 OnDownloadUpdated

方法中 DownloadItem 与 OnBeforeDownload中相同

再看回调接口 IDownloadItemCallback ,提供了取消下载、暂停下载、继续下载等方法

3、综上所述

CefSharp下载前的设置可以在 OnBeforeDownload中进行处理

下载的实时状态可以在 OnDownloadUpdated进行处理

二、通知外部正在下载文件

当文件下载时我们需要通知浏览器显示下载工具栏并更新下载状态信息

在 CustomDownloadHandler中新增_downloadCallBackEvent事件

private readonly Action<bool, DownloadItem> _downloadCallBackEvent;//第一个参数为true为update

第一个参数判断在OnBeforeDownload还是在OnDownloadUpdated中执行

public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
IBeforeDownloadCallback callback)
{
if (callback.IsDisposed) return;
_downloadCallBackEvent?.Invoke(false, downloadItem);
downloadItem.IsInProgress = true;
var path = GetDownloadFullPath(downloadItem.SuggestedFileName);
callback.Continue(path, false);
} public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
IDownloadItemCallback callback)
{
_downloadCallBackEvent?.Invoke(true, downloadItem);
}

三、设计下载任务栏

上图为Edge下载任务栏

据图可以分为三个部分,红框部分存在一个或多个,两个绿框部分为固定部分

创建 UserControl 【DownloadToolUc】 暂根据上图设计UI如下

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" x:Name="ItemsParent" Orientation="Horizontal" HorizontalAlignment="Left"> </StackPanel>
<Button Grid.Column="1" Style="{DynamicResource Button.DownloadLookAllButton}" Content="全部显示" Click="ShowAll_OnClick"/>
<Button Grid.Column="2" Style="{DynamicResource Button.DownloadCloseButton}" Margin="5,0" Click="CloseDownloadTool_OnClick"/>
</Grid>

红框部分单独创建UserControl 【DownloadToolItemUc】

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Margin="10,0" Source="{DynamicResource DrawingImage.File}" Width="25" Height="25"/>
<StackPanel Grid.Column="1" HorizontalAlignment="Left">
<TextBlock Text="{Binding FileName}" FontSize="14" Margin="0,5,0,0" TextTrimming="CharacterEllipsis"/>
<Grid>
<TextBlock Margin="0,5,0,0" Visibility="Collapsed">
<Hyperlink FontSize="14" Foreground="{DynamicResource WebBrowserBrushes.OpenFileForeground}" Focusable="False" Click="OpenFile_OnClick">打开文件</Hyperlink>
</TextBlock>
<StackPanel Margin="0,5,0,0">
<ProgressBar Height="6"/>
<TextBlock FontSize="12" Margin="0,5,0,0" TextTrimming="CharacterEllipsis">
<Run Text="{Binding CurrentSizeStr}"/>
<Run Text="/"/>
<Run Text="{Binding TotalSizeStr}"/>
</TextBlock>
</StackPanel>
</Grid>
</StackPanel>
<Button Grid.Column="2" Style="{DynamicResource Button.DownloadLookAllButton}" Width="35" Height="35" Content=". . ." Padding="0,0,0,9" Margin="10,0,5,0"/>
</Grid>

四、下载绑定处理

1、为DownloadToolItemUc添加ViewModel

 public class DownloadToolItemViewModel : BaseViewModel
{
private string _currentSizeStr;
public string CurrentSizeStr { get => _currentSizeStr; set { _currentSizeStr = value; OnPropertyChanged("CurrentSizeStr"); } } private string _totalSizeStr;
public string TotalSizeStr { get => _totalSizeStr; set { _totalSizeStr = value; OnPropertyChanged("TotalSizeStr"); } } private string _fileName;
public string FileName { get => _fileName; set { _fileName = value; OnPropertyChanged("FileName"); } } public string ConvertFileSize(long size)
{
if (size > 1024 * 1024 * 1024)
{
return $"{size / (1024 * 1024 * 1024)}G";
}
if (size > 1024 * 1024)
{
return $"{size / (1024 * 1024)}M";
}
return size > 1024 ? $"{size / 1024}K" : $"{size}B";
}
}

2、在DownloadToolUc中增加 DownloadFile方法

public void DownloadFile(bool isUpdate, DownloadItem downloadItem)
{
if (!isUpdate)
{
if (_downloadDict.ContainsKey(downloadItem.Id)) return;
var viewModel = new DownloadToolItemViewModel
{
FileName = downloadItem.SuggestedFileName,
};
_downloadDict.Add(downloadItem.Id, viewModel); this.Dispatcher.Invoke(new Action(() =>
{
this.Visibility = Visibility.Visible;
var item = new DownloadToolItemUc { DataContext = viewModel };
ItemsParent.Children.Insert(0, item);
})); }
else
{
if (!_downloadDict.ContainsKey(downloadItem.Id)) return;
var item = _downloadDict[downloadItem.Id];
item.CurrentSizeStr = item.ConvertFileSize(downloadItem.ReceivedBytes);
item.TotalSizeStr = downloadItem.TotalBytes <= 0 ? "未知" : item.ConvertFileSize(downloadItem.TotalBytes);
}
}

3、在创建新TabItem时建立回调事件与DownloadFile绑定

var uc = new WebTabItemUc { ViewModel = { CurrentUrl = obj?.ToString() } };
uc.CefWebBrowser.DownloadCallBackEvent += DownloadTool.DownloadFile;

五、下载进度绑定

在DownloadToolItemViewModel 中增加如下两个属性

private double _currentSize;
public double CurrentSize { get => _currentSize; set { _currentSize = value; OnPropertyChanged("CurrentSize"); } } private double _totalSize;
public double TotalSize { get => _totalSize; set { _totalSize = value; OnPropertyChanged("TotalSize"); } }

在DownloadToolItemUc控件中绑定

<ProgressBar Height="6" Maximum="{Binding TotalSize}" Value="{Binding CurrentSize}"/>

注意在给ViewModel赋值时downloadItem.TotalBytes可能为0,故增加TotalSize增加如下处理

item.TotalSize = downloadItem.TotalBytes > downloadItem.ReceivedBytes? downloadItem.TotalBytes : downloadItem.ReceivedBytes;
item.CurrentSize = downloadItem.ReceivedBytes;

六、下载栏增加显隐动画

任务栏的显示和隐藏有些突兀,为了显示丝滑一些,为其增加简单关键帧动画

为 DownloadToolUc增加 RenderTransform特效 并定义Storyboard资源如下

    <UserControl.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform Y="50"/>
</TransformGroup>
</UserControl.RenderTransform>
<Grid Background="{DynamicResource WebBrowserBrushes.DownloadToolBackground}" x:Name="ParentGrid">
<Grid.Resources>
<!-- 显示下载工具栏 -->
<Storyboard x:Key="DisplayTool" RepeatBehavior="1x">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="DownloadUc">
<EasingDoubleKeyFrame Value="0" KeyTime="00:00:01">
<EasingDoubleKeyFrame.EasingFunction>
<QuarticEase EasingMode="EaseInOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard> <Storyboard x:Key="HideTool" RepeatBehavior="1x">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="DownloadUc">
<EasingDoubleKeyFrame Value="50" KeyTime="00:00:01">
<EasingDoubleKeyFrame.EasingFunction>
<QuarticEase EasingMode="EaseInOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>

此处我们使用TranslateTransform(平移动画) <TranslateTransform Y="50"/> 意味着初始时距离0,0点向下50

显示下载工具栏时使纵坐标回到0点,隐藏时使纵坐标到达50

在cs文件中增加动画启动方法

private void InitStoryboard()
{
_displayToolStoryboard = (Storyboard)ParentGrid.FindResource("DisplayTool");
_hideToolStoryboard = (Storyboard)ParentGrid.FindResource("HideTool");
} private void DisplayTool()
{
_displayToolStoryboard.Begin();
} private void HideTool()
{
_hideToolStoryboard.Begin();
}

在下载时执行DisplayTool 在关闭时执行HideTool

七、源码地址

gitee地址:https://gitee.com/sirius_machao/mweb-browser

基于CefSharp开发(四)浏览器文件下载的更多相关文章

  1. 基于CefSharp开发浏览器(八)浏览器收藏夹栏

    一.前言 上一篇文章 基于CefSharp开发(七)浏览器收藏夹菜单 简单实现了部分收藏夹功能 如(添加文件夹.添加收藏.删除.右键菜单部分功能) 后续代码中对MTreeViewItem进行了扩展,增 ...

  2. mac 下基于firebreath 开发多浏览器支持的浏览器插件

    mac 下基于firebreath 开发多浏览器支持的浏览器插件 首先要区分什么是浏览器扩展和浏览器插件;插件可以像本地程序一样做的更多 一. 关于 firebreath http://www.fir ...

  3. 基于CefSharp开发(二)自定义浏览器窗体

    上一篇 https://www.cnblogs.com/mchao/p/13914726.html 简单了解了CefSharp引用配置但页面光秃秃的,这一篇着手开发简单浏览器窗体 一.Edge浏览器窗 ...

  4. 基于CefSharp开发(五)浏览器菜单样式

    一.菜单分析 上图为Edge浏览器现有的菜单内容,菜单中即有子菜单也有组合菜单. 本章节将开发浏览器菜单样式,菜单部分功能将后期进行处理. 二.创建菜单用户控件 新建用户控件命名为WebMenuUc, ...

  5. 基于CefSharp开发(三)浏览器头部优化

    一.上文回顾 上编实现了简单的网页加载功能包括URL输入.打开空标签页.网页链接中新页面处理等 本编将对网页的Title绑定.前进.后退.刷新等事件处理 二.Title绑定处理 当打开网页时Title ...

  6. 基于CefSharp开发(七)浏览器收藏夹菜单

    一.Edge收藏夹菜单分析 如下图所示为Edge收藏夹菜单, 点击收藏夹菜单按钮(红框部分)弹出收藏夹菜单窗体,窗体中包含工具栏(绿框部分)和树型菜单(黄框部分) 工具栏按钮功能分别为添加当前网页到根 ...

  7. 基于CefSharp开发浏览器(九)浏览器历史记录弹窗面板

    一.前言 前两篇文章写的是关于浏览器收藏夹的内容,因为收藏夹的内容不会太多,故采用json格式的文本文件作为收藏夹的存储方式. 关于浏览器历史记录,我个人每天大概会打开百来次网页甚至更多,时间越长历史 ...

  8. 基于CefSharp开发(六)浏览器网页缩放

    一.网页缩放分析 缩放入口 1.Ctrl + 鼠标滑轮缩放 2.菜单中缩放子菜单缩放 3.搜索框中网页缩放按钮缩放 缩放属性及命令 ChromiumWebBrowser 提供了缩放量值.缩放级别.放大 ...

  9. 基于cefsharp的用户浏览器

    技术:vc++2015   概述 用于需要制作一个浏览器 winfrom 中浏览器的插件有很多种 如:WebBrowser , Web.kit等 但用于比较稳定 功能齐全的还是cefsharp 详细 ...

随机推荐

  1. 较详细的gdb入门教程

    本文主要介绍gdb的基础使用.若需了解一些技巧,请访问此篇博客:点这里 本篇教程适用于Windows,macOS及Linux,但由于Windows的自带终端很难用,所以体验可能不太好.Windows ...

  2. 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖

    本次博客的目标 1. 手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有 ...

  3. .net 之json 一般处理程序

    <%@ WebHandler Language="C#" Class="read" %> using System; using System.We ...

  4. python数据分析01准备工作

    第1章 准备工作 1.1 本书的内容 本书讲的是利用Python进行数据控制.处理.整理.分析等方面的具体细节和基本要点.我的目标是介绍Python编程和用于数据处理的库和工具环境,掌握这些,可以让你 ...

  5. Flink的sink实战之四:自定义

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  6. 五分钟看懂抓包神技:DPDK

    我是一个网络监控软件,我被开发出来的使命就是监控网络中进进出出的所有通信流量. 一直以来,我的工作都非常的出色,但是随着我监控的网络越来越庞大,网络中的通信流量也变得越来越多,我开始有些忙不过来了,逐 ...

  7. IDEA与Eclipse创建struts项目

    1.IDEA创建struts项目 这里再构建struts项目是选择jar包出问题了,可以重新配置 创建页面和action配置struts.xml 启动tomcat,浏览器中运行 具体参考: https ...

  8. 字符串匹配算法之Sunday算法(转)

    字符串匹配算法之Sunday算法 背景 我们第一次接触字符串匹配,想到的肯定是直接用2个循环来遍历,这样代码虽然简单,但时间复杂度却是Ω(m*n),也就是达到了字符串匹配效率的下限.于是后来人经过研究 ...

  9. maven pom.xml 报错

    首先介绍背景,在eclipse中导入一个maven的项目,在我之前的电脑上导入好用,在自己的电脑上导入居然pom报错了Missing artifact junit:junit:jar:4.11,还会有 ...

  10. python 之路 《三》列表与元组

    我也试着把我写的东西给我的一些同学看,其实这只是我的经验还是比较建议先看书,或者在网上找相关的教学视频有了一定的基础之后再来看我写的文章,将我的经验与自己所学的知识相结合这样才会有所提高.有的同学建议 ...