一、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. 限制页面只能由微信内置浏览器打开,在其他浏览器打开跳转到Oauth2页面

    在需要限制的页面加上  appid必填,可以获取也可以自己随意 <script> var ua = navigator.userAgent.toLowerCase(); var isWei ...

  2. BIM+GIS它们各有什么优缺点

    BIM+GIS它们各有什么优缺点?应用有哪些优势?BIM模型精细程度高,语义信息丰富,侧重整合和管理建筑物自身所有阶段信息,包括建筑物所有微观图形化和非图形化信息,三维GIS侧重宏观.大范围地理环境与 ...

  3. jquery自定义弹层显示大图(兼容多层iframe)

    1.介绍 a:可用于多层iframe中,显示在最外层 b:动画效果为从点击的图片位置开始放大至全屏显示 2.效果图 3.js代码 1 function ShowMaxImg(src, y, x, w, ...

  4. php判断用户设备类型

    最近做的一个需求里面希望能判断用户访问页面的设备类型,根据不同的类型去加载不同的数据和页面样式.由于技术栈是使用的php,于是考虑在php层面去做这个判断. 假设主要判断的设备有平板和手机为主,分两个 ...

  5. JS多物体宽度运动案例

    任务 对于每一个Div区块,鼠标移入,宽度逐渐变宽,最宽值为400px,当鼠标移除时,宽度逐渐减小,最小值为100px. 任务提示: (1)多物体运动的定时器需要需要每个物体上同时最多只能开一个定时器 ...

  6. c++的基本点

    C++的要点:    一个思想:抽象(和分类)的思想:    三个概念:类.对象.消息:    三个特征:封装.继承.多态:    一个观念:函数服务于数据.      程序 = 对象 + 消息   ...

  7. Rest语法,传入多个参数

    Rest语法,传入多个参数 js调用函数时可以传入任意数量的参数,而不报错.如果传入的参数没有用到,那么传入多余的参数没有任何用处,那不是瞎子点灯白费蜡嘛.为了充分利用传入的每一个参数,我们可以采用R ...

  8. 一个List按照某个size分割为多个小的List

    针对于List的size比较大,使用多线程处理任务时,可以将List分割为一个一个比较小的任务单元进行处理. 例如集合大小:645,按照100分割,会将集合分割为6个size为100的集合和一个siz ...

  9. 实在是秒啊,我还从来没见过把Spring之AOP讲的这么通俗易懂的,安排!

    Spring之AOP 什么是AOP? AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. ...

  10. 微软开源的 AI 工具,让旧照片焕发新生

    原文地址:Bringing Old Photos Back to Life 原文作者:Ziyu Wan 译者 & 校正:HelloGitHub-小鱼干 & 鸭鸭 写在前面 在 GitH ...