一、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