基於CefSharp開發(七)瀏覽器收藏夾選單

鹹魚翻身?發表於2021-01-12

一、Edge收藏夾選單分析

如下圖所示為Edge收藏夾選單, 點選收藏夾選單按鈕(紅框部分)彈出收藏夾選單窗體,窗體中包含工具欄(綠框部分)和樹型選單(黃框部分)

工具欄按鈕功能分別為新增當前網頁到根節點、建立新資料夾到根節點、搜尋收藏夾內容、單中單(收藏夾選單中的其他功能)、收藏夾選單固定到右側

在樹節點上右鍵,有如下右鍵選單功能

 要完成此功能,需先仿其形,再謀其神。

二、仿其形

這裡所謂的仿其形,即模仿它的樣式(Style)

1、建立收藏夾選單UserControl

建立FavoritesMenuUc,Grid內容如下:ToggleButton控制選單顯隱,Popup展示彈出選單,

StackPanel 中新增五個按鈕用於實現工具欄中的功能,MTreeView用於實現樹型選單

關於MTreeView的實現請參照 Cys_Control(六) MTreeView

<Grid>
    <ToggleButton x:Name="FavoritesButton" Style="{DynamicResource ToggleButton.FontButton}" Checked="FavoritesButton_OnChecked"
                      Unchecked="FavoritesButton_OnUnchecked" Content="&#xe646;" FontSize="26" Background="Transparent" IsChecked="{Binding ElementName=FavoritesPop,Path=IsOpen}"/>
    <Popup x:Name="FavoritesPop" PopupAnimation="Fade" Placement="Bottom"  PlacementTarget="{Binding ElementName=FavoritesButton}"
               StaysOpen="False" AllowsTransparency="True" HorizontalOffset="-330">
        <Border Background="{DynamicResource WebBrowserBrushes.WebMenuBackground}" CornerRadius="5">
            <Border.Effect>
                <DropShadowEffect Color="{DynamicResource Color.MenuItemDropShadowBrush}" Opacity="0.3" ShadowDepth="3"/>
            </Border.Effect>
            <Grid Width="360" Height="660">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="1"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <Grid Grid.Row="0" Height="40">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" Text="收藏夾欄" VerticalAlignment="Center" FontSize="18" Margin="10,0,0,0" Foreground="{DynamicResource WebBrowserBrushes.DefaultForeground}"/>
                    <StackPanel Grid.Column="2" Orientation="Horizontal" Margin="0,0,10,0">
                        <Button Style="{DynamicResource Button.FontButton}" Content="&#xe659;"/>
                        <Button Style="{DynamicResource Button.FontButton}" Content="&#xe652;"/>
                        <Button Style="{DynamicResource Button.FontButton}" Content="&#xe65c;"/>
                        <Button Style="{DynamicResource Button.FontButton}" Content="..." Padding="0,0,0,12"/>
                        <Button Style="{DynamicResource Button.FontButton}" Content="&#xe6b9;"/>
                    </StackPanel>
                </Grid>
                <Rectangle Grid.Row="1" Height="1" Fill="{DynamicResource WebBrowserBrushes.WebMenuDivideLine}"/>
                <controls:MTreeView Grid.Row="2" x:Name="FavoritesTree"/>
            </Grid>
        </Border>
    </Popup>
</Grid>

新建類TreeNode用於顯示TreeView顯示

public class TreeNode
{
    public int NodeId { get; set; }
    public int ParentId { get; set; }
    public string NodeName { get; set; }
    public string Url { get; set; } = "http://www.baidu.com";
    public List<TreeNode> ChildNodes { get; set; } = new List<TreeNode>();
    public int Type { get; set; } //0-檔案,1-資料夾
}

FavoritesMenuUc.xaml.cs檔案中增加測試資料

private void GetFavoritesInfo()
{
    nodes = new List<TreeNode>()
            {
                new TreeNode(){ParentId=-1, NodeId=0, NodeName = "收藏夾",Type = 1},
                new TreeNode(){ParentId=0, NodeId=1, NodeName = "文字",Type = 1},
                new TreeNode(){ParentId=0, NodeId=2, NodeName = "音訊",Type = 1},
                new TreeNode(){ParentId=0, NodeId=3, NodeName = "視訊",Type = 1},

                new TreeNode(){ParentId=1, NodeId=11, NodeName = "文字1",Type = 0},
                new TreeNode(){ParentId=1, NodeId=12, NodeName = "文字2",Type = 0},
                new TreeNode(){ParentId=1, NodeId=13, NodeName = "文字3",Type = 0},
            };
    List<TreeNode> root = GetNodes(-1, nodes);
    AddTreeViewItems(null, root[0], true);
}

private List<TreeNode> GetNodes(int parentId, List<TreeNode> nodes)
{
    List<TreeNode> mainNodes = nodes.Where(x => x.ParentId == parentId).OrderByDescending(x => x.Type).ToList();
    List<TreeNode> otherNodes = nodes.Where(x => x.ParentId != parentId).OrderByDescending(x => x.Type).ToList();
    foreach (TreeNode node in mainNodes)
        node.ChildNodes = GetNodes(node.NodeId, otherNodes);
    return mainNodes;
}

private void AddTreeViewItems(MTreeViewItem parent, TreeNode treeNode, bool isRoot)
{
    var treeViewItem = new MTreeViewItem();
    if (treeNode.ChildNodes.Count <= 0)
    {
        if (treeNode.Type == 0)
        {
            treeViewItem.Header = treeNode.Url;
            treeViewItem.Icon = "\ueb1e";
            treeViewItem.IsExpandedIcon = "\ueb1e";
            treeViewItem.IconForeground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
        }
        else
        {
            treeViewItem.Header = treeNode.NodeName;
        }
    }
    else
    {
        treeViewItem.Header = treeNode.NodeName;
        foreach (var child in treeNode.ChildNodes)
        {
            AddTreeViewItems(treeViewItem, child, false);
        }
    }
    if (!isRoot)
        parent.Items.Add(treeViewItem);
    else
    {
        FavoritesTree.Items.Add(treeViewItem);
    }
}

執行效果如下:

2、為MTreeView新增右鍵選單

右鍵子選單可使用MMneuItem,xaml樣式如下

<controls:MTreeView Grid.Row="2" x:Name="FavoritesTree" ContextMenuOpening="FavoritesTree_OnContextMenuOpening">
    <controls:MTreeView.ContextMenu>
        <ContextMenu x:Name="FavoritesContextMenu" Style="{DynamicResource WebCustomMenus.DefaultContextMenu}">
            <controls:MMenuItem Tag="0" Header="全部開啟(16個)" Icon="&#xe600;"/>
            <controls:MMenuItem Tag="1" Header="在新建視窗中全部開啟(16個)" Icon="&#xe602;"/>
            <controls:MMenuItem Tag="2" Header="在新 InPrivate視窗全部開啟(16個)" Icon="&#xe68c;"/>
            <controls:MMenuItem Tag="4" Header="按名稱排序" Icon="&#xe606;"/>
            <controls:MMenuItem Tag="5" Header="重新命名" Icon="&#xe712;"/>
            <controls:MMenuItem Tag="5" Header="刪除" Icon="&#xe74e;" IconFontSize="26"/>
            <controls:MMenuItem Tag="5" Header="將當前標籤頁新增到資料夾" Icon="&#xe659;"/>
            <controls:MMenuItem Tag="5" Header="將所有標籤頁新增到資料夾"/>
            <controls:MMenuItem Tag="5" Header="新增資料夾" Icon="&#xe652;"/>
        </ContextMenu>
    </controls:MTreeView.ContextMenu>
</controls:MTreeView>

當右鍵未選中時增加遮蔽右鍵選單

private void FavoritesTree_OnContextMenuOpening(object sender, ContextMenuEventArgs e)
{
    _currentRightItem = ControlHelper.FindVisualParent<MTreeViewItem>(e.OriginalSource as DependencyObject);
    if (null == _currentRightItem)
    {
        e.Handled = true;
    }
}

執行效果如下:

三、謀其神

這裡的謀其神,就是使其可以完成正常的業務處理。

前面仿造了Edge瀏覽器的大致樣式,接下來要對及增加資料儲存、命令處理等

1、增加Favorites資料儲存

這裡使用同資料下載同樣的處理邏輯,將收藏夾儲存為Json檔案

新建FavoritesDataRepository,類中新增 SaveFavoritesSetting、GetFavoritesSetting用於儲存與讀取資料。

public class FavoritesDataRepository
{
    public void SaveFavoritesSetting()
    {
        try
        {
            var setting = GlobalInfo.FavoritesSetting;
            if (setting == null) return;
            var fileName = FileDataPath.GetFilePath(DataFileType.Favorites);
            CommonOperator.SaveDataJson(setting, fileName);
        }
        catch (Exception ex)
        {

        }
    }

    public FavoritesSetting GetFavoritesSetting()
    {
        var fileName = FileDataPath.GetFilePath(DataFileType.Favorites);
        var setting = CommonOperator.GetDataJson<FavoritesSetting>(fileName);
        setting ??= new FavoritesSetting();
        setting.FavoritesInfos ??= new List<TreeNode>();
        if (setting.FavoritesInfos.Count <= 0)
        {
            setting.FavoritesInfos.Add(new TreeNode()
            {
                ParentId = -1,
                NodeId = 0,
                NodeName = "收藏夾",
                Type = 1,
            });
        }
        return setting;
    }
}

 2、增加選單事件

由於事件較多,目前僅處理新增資料夾、新增收藏、刪除功能

新增folder,這裡的if用來判斷是收藏夾工具欄的按鈕還是右鍵選單C

private void AddFolder_OnClick(object sender, RoutedEventArgs e)
{
    if (sender is Button)
    {
        var newTreeNode = GetNewTreeNodeInfo(true, 1, "新建資料夾", null);
        if (null == FavoritesTree || FavoritesTree.Items.Count <= 0) return;
        var treeNodeUc = FavoritesTree.Items[0];
        if (!(treeNodeUc is MTreeViewItem item)) return;
        item.Items.Add(newTreeNode.Item2);
        GlobalInfo.FavoritesSetting.FavoritesInfos.Add(newTreeNode.Item1);
    }
    else if (sender is MMenuItem)
    {
        var newTreeNode = GetNewTreeNodeInfo(false, 1, "新建資料夾", null);
        if (_currentRightItem != null && _currentRightItem.Type == 1)
        {
            _currentRightItem.Items.Add(newTreeNode.Item2);
            GlobalInfo.FavoritesSetting.FavoritesInfos.Add(newTreeNode.Item1);
        }
    }
}

新增收藏,新增收藏增加了GetWebUrlEvent事件 用於獲取當前tab頁的url;

private void AddFavorites_OnClick(object sender, RoutedEventArgs e)
{
    var model = GetWebUrlEvent?.Invoke();
    if (null == model) return;

    if (sender is Button)
    {
        if (!(FavoritesTree.Items[0] is MTreeViewItem item)) return;
        var newTreeNode = GetNewTreeNodeInfo(true, 0, model.Title, model.CurrentUrl);
        GlobalInfo.FavoritesSetting.FavoritesInfos.Add(newTreeNode.Item1);
        item.Items.Add(newTreeNode.Item2);
    }
    else if (sender is MMenuItem)
    {
        if (_currentRightItem != null && _currentRightItem.Type == 1)
        {
            var newTreeNode = GetNewTreeNodeInfo(false, 0, model.Title, model.CurrentUrl);
            _currentRightItem.Items.Add(newTreeNode.Item2);
            GlobalInfo.FavoritesSetting.FavoritesInfos.Add(newTreeNode.Item1);
        }
    }
}

刪除當前節點

private void Delete_OnClick(object sender, RoutedEventArgs e)
{
    if (_currentRightItem?.Parent == null) return;

    foreach (var item in _currentRightItem.Items)
    {
        _currentRightItem.Items.Remove(item);
        if (!GlobalInfo.FavoritesSetting.FavoritesInfos.Exists(x => x.NodeId == _currentRightItem.NodeId))
            continue;
        var currentNode = (GlobalInfo.FavoritesSetting.FavoritesInfos.FirstOrDefault(x => x.NodeId == _currentRightItem.NodeId));
        GlobalInfo.FavoritesSetting.FavoritesInfos.Remove(currentNode);
    }

    if (_currentRightItem.Parent is MTreeViewItem items)
    {
        if (GlobalInfo.FavoritesSetting.FavoritesInfos.Exists(x => x.NodeId == _currentRightItem.NodeId))
        {
            var currentNode = (GlobalInfo.FavoritesSetting.FavoritesInfos.FirstOrDefault(x => x.NodeId == _currentRightItem.NodeId));
            GlobalInfo.FavoritesSetting.FavoritesInfos.Remove(currentNode);
        }
        items.Items.Remove(_currentRightItem);
    }
}

關於建立TreeNode程式碼細節請參考文章末尾原始碼

增加OpenNewTabEvent事件用於點選收藏內容時開啟網頁

四、執行效果

 

五、原始碼地址

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

相關文章