一、前言

上一篇文章 基于CefSharp开发(七)浏览器收藏夹菜单 简单实现了部分收藏夹功能 如(添加文件夹、添加收藏、删除、右键菜单部分功能)

后续代码中对MTreeViewItem进行了扩展,增加了TextBox用于编辑Item及相应的依赖属性,实现了重命名操作。

浏览器除了有收藏夹菜单,还需要有收藏夹栏用于快捷访问,本章将开发简易的收藏夹栏。

二、收藏夹栏分析

如下面两幅图所示,前者为收藏夹菜单样式,后者为收藏夹栏样式,两者数据结构相同,只是展示形式略有差异。故可采用同一数据结构

观其展示形式与MMenu接近,故此处将采用Menu进行样式扩展开发。

收藏夹栏支持右键功能,故需要添加ContextMenu支持。

收藏夹栏与收藏夹菜单展示的同一数据,属于同一功能,故需增加联动。

下面将逐步完成以上内容的开发,由于样式及结构均与收藏夹菜单接近,故本章不对样式进行叙述,直撸代码

三、创建自定义控件并添加至WebTabControlUc

1、新增定义控件MFavorites、MFavoritesItem

样式可复制MMenu、MMenuItem

MFavorites.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Cys_CustomControls.Controls">
<Style TargetType="{x:Type local:MFavorites}">
<Setter Property="Foreground" Value="{DynamicResource ColorBrush.FontDefaultColor}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Width" Value="Auto"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MFavorites}">
<Border Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

MFavorites.xaml.cs

public class MFavorites : Menu
{
static MFavorites()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MFavorites), new FrameworkPropertyMetadata(typeof(MFavorites)));
}
}

MFavoritesItem.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Cys_CustomControls.Controls"> <ControlTemplate x:Key="MTopLevelHeaderTemplate" TargetType="{x:Type local:MFavoritesItem}">
<Border x:Name="templateRoot" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="{TemplateBinding CornerRadius}" SnapsToDevicePixels="true">
<Grid>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="PART_TextGrid" Opacity="0.8" Margin="10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="Icon" FontSize="20" HorizontalAlignment="Center" Text="{TemplateBinding Icon}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Foreground="{TemplateBinding IconForeground}"/>
<ContentPresenter Margin="10,0,0,0" Grid.Column="1" ContentSource="Header" x:Name="PART_Header" HorizontalAlignment="Center" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
</Grid>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}"
Placement="Bottom" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" PlacementTarget="{Binding ElementName=templateRoot}" Width="{TemplateBinding PopupWidth}">
<Border x:Name="SubMenuBorder" Margin="0 0 5 5" >
<Border.Effect>
<DropShadowEffect Color="{DynamicResource Color.MenuItemDropShadowBrush}" Opacity="0.3" ShadowDepth="3"/>
</Border.Effect>
<Border Background="{DynamicResource WebBrowserBrushes.WebMenuBackground}" BorderThickness="1" CornerRadius="5">
<ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}" Margin="0,5">
<Grid RenderOptions.ClearTypeHint="Enabled" Background="Transparent">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}"
Height="{Binding ActualHeight, ElementName=SubMenuBorder}"
Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle"
Grid.IsSharedSizeScope="true" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
</Grid>
</ScrollViewer>
</Border>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation" Value="true">
<Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
</Trigger>
<!--<Trigger Property="Icon" Value="{x:Null}">
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>-->
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="SubMenuScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
</Trigger>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="templateRoot" Property="Background" Value="{DynamicResource WebBrowserBrushes.WebMenuIsMouseOverBackground}"/>
<Setter TargetName="PART_TextGrid" Property="Opacity" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate> <ControlTemplate x:Key="MTopLevelItemTemplate" TargetType="{x:Type local:MFavoritesItem}">
<Border x:Name="templateRoot" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="{TemplateBinding CornerRadius}" SnapsToDevicePixels="true">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="PART_TextGrid" Opacity="0.8" Margin="10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="Icon" FontSize="20" HorizontalAlignment="Center" Text="{TemplateBinding Icon}" Foreground="{TemplateBinding IconForeground}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
<ContentPresenter Margin="10,0,0,0" ContentSource="Header" HorizontalAlignment="Center" Grid.Column="1" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!--<Trigger Property="Icon" Value="{x:Null}">
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>-->
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="templateRoot" Property="Background" Value="{DynamicResource WebBrowserBrushes.WebMenuIsMouseOverBackground}"/>
<Setter TargetName="PART_TextGrid" Property="Opacity" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate> <ControlTemplate x:Key="MSubmenuHeaderTemplate" TargetType="{x:Type local:MFavoritesItem}">
<Border x:Name="templateRoot" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Height="35" SnapsToDevicePixels="true">
<Grid >
<Grid HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="Icon" Width="20" FontSize="20" HorizontalAlignment="Center" Text="{TemplateBinding Icon}" Foreground="{TemplateBinding IconForeground}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
<ContentPresenter Margin="10,0,0,0" Grid.Column="1" ContentSource="Header" HorizontalAlignment="Center" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
</Grid>
<Path x:Name="RightArrow" Data="M 0,0 L 7,7 L 0,14 Z"
Fill="{TemplateBinding Foreground}"
HorizontalAlignment="Right" Margin="0,0,5,0" VerticalAlignment="Center" Opacity="0.6"/> <Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}"
Placement="Right" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" PlacementTarget="{Binding ElementName=templateRoot}" VerticalOffset="-5" Width="{TemplateBinding PopupWidth}">
<Border x:Name="SubMenuBorder" Margin="0 0 5 5" >
<Border.Effect>
<DropShadowEffect Color="{DynamicResource Color.MenuItemDropShadowBrush}" Opacity="0.3" ShadowDepth="3"/>
</Border.Effect>
<Border Background="{DynamicResource WebBrowserBrushes.WebMenuBackground}" BorderThickness="1" CornerRadius="5">
<ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}" Margin="0,5">
<Grid RenderOptions.ClearTypeHint="Enabled" Background="Transparent">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}"
Height="{Binding ActualHeight, ElementName=SubMenuBorder}"
Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle"
Grid.IsSharedSizeScope="true" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
</Grid>
</ScrollViewer>
</Border>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation" Value="true">
<Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
</Trigger>
<!--<Trigger Property="Icon" Value="{x:Null}">
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>-->
<Trigger Property="IsChecked" Value="True">
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="SubMenuScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
</Trigger>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="templateRoot" Property="Background" Value="{DynamicResource WebBrowserBrushes.WebMenuIsMouseOverBackground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate> <ControlTemplate x:Key="MSubmenuItemTemplate" TargetType="{x:Type local:MFavoritesItem}">
<Border x:Name="templateRoot" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Height="35" SnapsToDevicePixels="true">
<Grid HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="Icon" Width="20" FontSize="20" HorizontalAlignment="Center" Text="{TemplateBinding Icon}" Foreground="{TemplateBinding IconForeground}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
<ContentPresenter Margin="10,0,0,0" Grid.Column="1" x:Name="menuHeaderContainer" HorizontalAlignment="Center" ContentSource="Header" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!--<Trigger Property="Icon" Value="{x:Null}">
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>-->
<Trigger Property="IsChecked" Value="True">
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="templateRoot" Property="Background" Value="{DynamicResource WebBrowserBrushes.WebMenuIsMouseOverBackground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate> <Style TargetType="{x:Type local:MFavoritesItem}">
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="CornerRadius" Value="5"/>
<Setter Property="Background" Value="{DynamicResource WebBrowserBrushes.TabHeaderIsSelectedBackground}"/>
<Setter Property="Foreground" Value="{DynamicResource ColorBrush.FontPrimaryColor}"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontFamily" Value="Microsoft YaHei"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Width" Value="Auto"/>
<Setter Property="MinWidth" Value="40"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template" Value="{StaticResource MSubmenuItemTemplate}"/>
<Style.Triggers>
<Trigger Property="Role" Value="TopLevelHeader">
<Setter Property="BorderThickness" Value="0,0,1,0"/>
<Setter Property="PopupWidth" Value="300"/>
<Setter Property="Template" Value="{StaticResource MTopLevelHeaderTemplate}"/>
</Trigger>
<Trigger Property="Role" Value="TopLevelItem">
<Setter Property="BorderThickness" Value="0,0,1,0"/>
<Setter Property="Template" Value="{StaticResource MTopLevelItemTemplate}"/>
</Trigger>
<Trigger Property="Role" Value="SubmenuHeader">
<Setter Property="PopupWidth" Value="300"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{DynamicResource ColorBrush.FontPrimaryColor}"/>
<Setter Property="Template" Value="{StaticResource MSubmenuHeaderTemplate}"/>
<Setter Property="Padding" Value="3,0,0,0"/>
</Trigger>
<Trigger Property="Role" Value="SubmenuItem">
<Setter Property="Foreground" Value="{DynamicResource ColorBrush.FontPrimaryColor}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template" Value="{StaticResource MSubmenuItemTemplate}"/>
<Setter Property="Padding" Value="3,0,0,0"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

MFavoritesItem.xaml.cs

public class MFavoritesItem : MenuItem
{
static MFavoritesItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MFavoritesItem), new FrameworkPropertyMetadata(typeof(MFavoritesItem)));
} #region == DependencyProperty==
#region == CornerRadius==
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(MFavoritesItem),
new PropertyMetadata(null)); /// <summary>
/// CornerRadius
/// </summary>
public CornerRadius CornerRadius
{
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
#endregion #region == PopupWidth==
public static readonly DependencyProperty PopupWidthProperty = DependencyProperty.Register("PopupWidth", typeof(double), typeof(MFavoritesItem),
new PropertyMetadata(null)); /// <summary>
/// PopupWidth
/// </summary>
public double PopupWidth
{
get => (double)GetValue(PopupWidthProperty);
set => SetValue(PopupWidthProperty, value);
}
#endregion /// <summary>
/// IconForeground 字体图标前景色
/// </summary>
public static readonly DependencyProperty IconForegroundProperty = DependencyProperty.Register("IconForeground", typeof(Brush), typeof(MFavoritesItem));
public Brush IconForeground
{
get => (Brush)GetValue(IconForegroundProperty);
set => SetValue(IconForegroundProperty, value);
} /// <summary>
/// ItemMargin
/// </summary>
public static readonly DependencyProperty ItemMarginProperty = DependencyProperty.Register("ItemMargin", typeof(Thickness), typeof(MFavoritesItem));
public Thickness ItemMargin
{
get => (Thickness)GetValue(ItemMarginProperty);
set => SetValue(ItemMarginProperty, value);
} /// <summary>
/// TextMaxWidth
/// </summary>
public static readonly DependencyProperty TextMaxWidthProperty = DependencyProperty.Register("TextMaxWidth", typeof(double), typeof(MFavoritesItem));
public double TextMaxWidth
{
get => (double)GetValue(TextMaxWidthProperty);
set => SetValue(TextMaxWidthProperty, value);
}
#endregion public int Type { get; set; } public int Level { get; set; }
public int NodeId { get; set; }
}

2、接着新增用户控件FavoritesBarUc

用于承接MFavorites代码如下:

FavoritesBarUc.xaml

<UserControl x:Class="MWebBrowser.View.FavoritesBarUc"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:Cys_CustomControls.Controls;assembly=Cys_CustomControls"
mc:Ignorable="d"
d:DesignHeight="40" d:DesignWidth="800" Height="40" Background="{DynamicResource WebBrowserBrushes.TabHeaderIsSelectedBackground}">
<Grid VerticalAlignment="Center">
<controls:MFavorites x:Name="MenuParent" ContextMenuOpening="FavoritesTree_OnContextMenuOpening" ScrollViewer.HorizontalScrollBarVisibility="Hidden" PreviewMouseLeftButtonUp="FavoritesTree_OnPreviewMouseLeftButtonUp">
<controls:MFavorites.ContextMenu>
<ContextMenu x:Name="FavoritesContextMenu" Style="{DynamicResource WebCustomMenus.DefaultContextMenu}">
<controls:MMenuItem Tag="0" x:Name="OpenAllFolder" Header="全部打开(16个)" Icon=""/>
<controls:MMenuItem Tag="1" x:Name="OpenNewAllFolder" Header="在新建窗口中全部打开(16个)" Icon=""/>
<controls:MMenuItem Tag="2" Header="在新 InPrivate窗口全部打开(16个)" Icon=""/>
<controls:MMenuItem Tag="4" Header="按名称排序" Icon=""/>
<controls:MMenuItem Tag="5" x:Name="ReName" Header="重命名" Icon="" Click="ReName_OnClick"/>
<controls:MMenuItem Tag="6" x:Name="DeleteNode" Header="删除" Icon="" IconFontSize="26" Click="Delete_OnClick"/>
<controls:MMenuItem Tag="7" Header="将当前标签页添加到文件夹" Icon="" Click="AddFavorites_OnClick"/>
<controls:MMenuItem Tag="8" Header="将所有标签页添加到文件夹" Visibility="Collapsed"/>
<controls:MMenuItem Tag="9" Header="添加文件夹" Icon="" Click="AddFolder_OnClick"/>
</ContextMenu>
</controls:MFavorites.ContextMenu>
</controls:MFavorites>
<Popup x:Name="ReNamePop" PopupAnimation="Fade" Placement="Bottom" PlacementTarget="{Binding ElementName=MenuParent}"
StaysOpen="False" AllowsTransparency="True" VerticalOffset="-40">
<Border Background="{DynamicResource WebBrowserBrushes.WebMenuBackground}" CornerRadius="5">
<Border.Effect>
<DropShadowEffect Color="{DynamicResource Color.MenuItemDropShadowBrush}" Opacity="0.3" ShadowDepth="3"/>
</Border.Effect>
<Grid Width="320" Height="140">
<Grid Margin="20,20,20,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="编辑文件夹名称" FontSize="18" Foreground="{DynamicResource ColorBrush.FontPrimaryColor}"/>
<StackPanel Grid.Row="1" Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="名称" Foreground="{DynamicResource ColorBrush.FontPrimaryColor}" VerticalAlignment="Center"/>
<TextBox x:Name="FolderName" Height="30" Width="236" Margin="10,0,0,0" Style="{DynamicResource TextBox.ReName}" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Grid.Row="2" Margin="0,15,0,0" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="保存" Style="{DynamicResource Button.ReSave}" Click="ReSave_OnClick"/>
<Button Content="取消" Style="{DynamicResource Button.ReCancel}" Click="ReCancel_OnClick" Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</Grid>
</Border>
</Popup>
</Grid>
</UserControl>

FavoritesBarUc.xaml.cs

using Cys_Common;
using Cys_Controls.Code;
using Cys_CustomControls.Controls;
using Cys_Model;
using MWebBrowser.Code.Helpers;
using MWebBrowser.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media; namespace MWebBrowser.View
{
/// <summary>
/// Interaction logic for FavoritesBarUc.xaml
/// </summary>
public partial class FavoritesBarUc : UserControl
{
private readonly double _textMaxWidth = 300;
/// <summary>
/// 记录当前右键选中的Item;
/// </summary>
private MFavoritesItem _currentRightItem;
public Func<WebTabControlViewModel> GetWebUrlEvent;
public Action<string> OpenNewTabEvent;
public FavoritesBarUc()
{
InitializeComponent();
this.Loaded += FavoritesBarUc_Loaded;
} private void FavoritesBarUc_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
if (this.IsInDesignMode()) return;
GetFavoritesInfo();
} private void GetFavoritesInfo()
{
List<TreeNode> root = GetNodes(-1, GlobalInfo.FavoritesSetting.FavoritesInfos);
if (root == null || root.Count <= 0 || root[0].ChildNodes.Count <= 0) return;
foreach (var child in root[0].ChildNodes)
{
AddFavoritesItem(null, child, 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;
} /// <summary>
/// 递归添加子集
/// </summary>
/// <param name="parent"></param>
/// <param name="treeNode"></param>
/// <param name="isRoot"></param>
private void AddFavoritesItem(MFavoritesItem parent, TreeNode treeNode, bool isRoot)
{
var item = GetNewFavoritesItem(treeNode);
if (treeNode.ChildNodes.Count > 0)
{
foreach (var child in treeNode.ChildNodes)
{
AddFavoritesItem(item, child, false);
}
} if (!isRoot)
parent.Items.Add(item);
else
MenuParent.Items.Add(item);
} /// <summary>
/// 获取FavoritesItem
/// </summary>
/// <param name="treeNode"></param>
/// <returns></returns>
private MFavoritesItem GetNewFavoritesItem(TreeNode treeNode)
{
return new MFavoritesItem
{
Header = treeNode.NodeName,
Type = treeNode.Type,
NodeId = treeNode.NodeId,
Level = treeNode.Level,
TextMaxWidth = _textMaxWidth,
Icon = treeNode.Type == 0 ? "\ueb1e" : "\ue903",
IconForeground = treeNode.Type == 0 ? new SolidColorBrush(Color.FromRgb(255, 255, 255)) : new SolidColorBrush(Color.FromRgb(255, 205, 44)),
};
} /// <summary>
/// 处理右键菜单打开前的行为
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void FavoritesTree_OnContextMenuOpening(object sender, ContextMenuEventArgs e)
{
_currentRightItem = ControlHelper.FindVisualParent<MFavoritesItem>(e.OriginalSource as DependencyObject);
if (null == _currentRightItem)
{
e.Handled = true;
return;
}
if (_currentRightItem.Type == 0)
{
OpenAllFolder.Visibility = Visibility.Collapsed;
OpenNewAllFolder.Visibility = Visibility.Collapsed;
ReName.Visibility = Visibility.Collapsed;
}
else
{
OpenAllFolder.Visibility = Visibility.Visible;
OpenNewAllFolder.Visibility = Visibility.Visible;
ReName.Visibility = Visibility.Visible;
}
} private void FavoritesTree_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var item = ControlHelper.FindVisualParent<MFavoritesItem>(e.OriginalSource as DependencyObject);
if (item.Type == 1) return;
if (!GlobalInfo.FavoritesSetting.FavoritesInfos.Exists(x => x.NodeId == item.NodeId)) return;
var treeNode = GlobalInfo.FavoritesSetting.FavoritesInfos.First(x => x.NodeId == item.NodeId);
OpenNewTabEvent?.Invoke(treeNode.Url);
} /// <summary>
/// 添加收藏
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AddFavorites_OnClick(object sender, RoutedEventArgs e)
{
var model = GetWebUrlEvent?.Invoke();
if (null == model) return;
if (_currentRightItem?.Type != 1) return;
var newTreeNode = GetNewTreeNodeInfo(false, 0, model.Title, model.CurrentUrl);
_currentRightItem.Items.Add(newTreeNode.Item2);
GlobalInfo.FavoritesSetting.FavoritesInfos.Add(newTreeNode.Item1);
}
/// <summary>
/// 添加文件夹
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AddFolder_OnClick(object sender, RoutedEventArgs e)
{
var newTreeNode = GetNewTreeNodeInfo(false, 1, "新建文件夹", null);
if (_currentRightItem != null && _currentRightItem.Type == 1)
{
_currentRightItem.Items.Add(newTreeNode.Item2);
GlobalInfo.FavoritesSetting.FavoritesInfos.Add(newTreeNode.Item1);
}
} private Tuple<TreeNode, MFavoritesItem> GetNewTreeNodeInfo(bool isRoot, int type, string nodeName, string url)
{
int parentId = 0;
int level = 1;
if (!isRoot)
{
parentId = _currentRightItem.NodeId;
level = parentId == -1 ? +1 : _currentRightItem.Level + 1;
}
int nodeMax = GlobalInfo.FavoritesSetting.FavoritesInfos.Max(x => x.NodeId);
var treeNode = new TreeNode
{
Url = url,
ParentId = parentId,
NodeId = nodeMax + 1,
NodeName = nodeName,
Type = type,
Level = level,
};
var favoritesItem = GetNewFavoritesItem(treeNode);
return new Tuple<TreeNode, MFavoritesItem>(treeNode, favoritesItem);
} #region 右键菜单操作 /// <summary>
/// 删除当前节点
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Delete_OnClick(object sender, RoutedEventArgs e)
{
if (_currentRightItem?.Parent == null) return;
for (int i = _currentRightItem.Items.Count; i > 0; i--)
{
_currentRightItem.Items.Remove(_currentRightItem.Items[^1]);
if (!GlobalInfo.FavoritesSetting.FavoritesInfos.Exists(x => x.NodeId == _currentRightItem.NodeId))
continue;
} if (_currentRightItem.Parent is MFavoritesItem 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);
} if (_currentRightItem.Parent is MFavorites parent)
{
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);
}
parent.Items.Remove(_currentRightItem);
}
} #region 重命名 private void ReName_OnClick(object sender, RoutedEventArgs e)
{
if (null == _currentRightItem) return;
if (_currentRightItem.Type == 0) return; ReNamePop.HorizontalOffset = (this.ActualWidth - 320) / 2;
ReNamePop.IsOpen = true;
} private void ReCancel_OnClick(object sender, RoutedEventArgs e)
{
ReNamePop.IsOpen = false;
} private void ReSave_OnClick(object sender, RoutedEventArgs e)
{
ReNamePop.IsOpen = false;
_currentRightItem.Header = FolderName.Text;
if (!GlobalInfo.FavoritesSetting.FavoritesInfos.Exists(x => x.NodeId == _currentRightItem.NodeId)) return;
var treeNode = GlobalInfo.FavoritesSetting.FavoritesInfos.First(x => x.NodeId == _currentRightItem.NodeId);
treeNode.NodeName = FolderName.Text;
} #endregion #endregion
}
}

该类中的方法用于初始化MFavorites数据

3、更改WebTabControlUc布局

新增一行用于展示FavoritesBarUc

<webBrowser:FavoritesBarUc Grid.Row="2"/>

四、运行效果

五、源码地址

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

项目邀请:如对该项目有兴趣,欢迎联系我共同开发!!!

基于CefSharp开发浏览器(八)浏览器收藏夹栏的更多相关文章

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

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

  2. 基于.net开发chrome核心浏览器【七】

    这是一个系列的文章,前面六篇文章的地址如下: 基于.net开发chrome核心浏览器[六] 基于.net开发chrome核心浏览器[五] 基于.net开发chrome核心浏览器[四] 基于.net开发 ...

  3. 基于.net开发chrome核心浏览器

    本文转载自:http://www.cnblogs.com/liulun/archive/2013/04/20/3031502.html 一: 上一篇的链接: 基于.net开发chrome核心浏览器[一 ...

  4. 基于.net开发chrome核心浏览器【二】

    原文:基于.net开发chrome核心浏览器[二] 一: 上一篇的链接: 基于.net开发chrome核心浏览器[一] 二: 相关资源介绍: chrome Frame: 让IE有一颗chrome的心, ...

  5. 基于.net开发chrome核心浏览器【四】

    原文:基于.net开发chrome核心浏览器[四] 一: 上周去北京出差,给国家电网的项目做架构方案,每天都很晚睡,客户那边的副总也这样拼命工作. 累的不行了,直接导致第四篇文章没有按时发出来. 希望 ...

  6. 基于.net开发chrome核心浏览器【三】

    原文:基于.net开发chrome核心浏览器[三] 本篇我们讲解怎么用CefGlue开发一个最简单的浏览器 一: CefGlue是建立在Cef项目之上的,Cef项目是C/C++的项目:CefGlue只 ...

  7. UC手机浏览器js加入收藏夹

    概述 对于某些网站来说,让用户一键把网页加入收藏夹的设计是非常棒的,它能提醒用户把网页加入收藏夹,从而增加用户的回访率,使网站获得更多的流量. 在PC端,只有ie和ff支持用js把网页加入收藏夹的操作 ...

  8. 基于.net开发chrome核心浏览器【一】

    原文:基于.net开发chrome核心浏览器[一] 说明: 这是本系列的第一篇文章,我会尽快发后续的文章. 源起 1.加快葬送IE6浏览器的进程 世界上使用IE6浏览器最多的地方在中国 中国使用IE6 ...

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

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

随机推荐

  1. POJ 1743 Musical Theme【SAM】

    POJ1743 Musical Theme 要找长度\(\ge 5\)且出现次数\(\ge 2\)并且第一次出现和最后一次出现不重叠的最长子串. 题目条件中,如果对于两个串,在一个串的每个数上都加上相 ...

  2. Codeforces 1355 C. Count Triangles

    传送门:C - Count Triangles  题意:给你四个数A,B,C,D,求有多少个三边为x,y,z (A ≤ x ≤ B ≤ y ≤ C ≤ z ≤ D)的三角形. 题解:枚举 x=A~B, ...

  3. HDU - 3613 Best Reward(manacher或拓展kmp)

    传送门:HDU - 3613 题意:给出26个字母的价值,然后给你一个字符串,把它分成两个字符串,字符串是回文串才算价值,求价值最大是多少. 题解:这个题可以用马拉车,也可以用拓展kmp. ①Mana ...

  4. HDU-6290 奢侈的旅行 (Dijkstra+堆优化)

    高玩小Q不仅喜欢玩寻宝游戏,还喜欢一款升级养成类游戏.在这个游戏的世界地图中一共有nn个城镇,编号依次为11到nn.这些城镇之间有mm条单向道路,第ii 条单项道路包含四个参数ui,vi,ai,biu ...

  5. Java_web-response的outputStream和Write输出数据的问题

    解决方法: 把方法换成这个也可以: 因为浏览器也是一个html解析工具,所以认识html文本 下面这个直接write(1),那么浏览器上就会显示L 这个样子在浏览器上看到的就是1: 字节流输出: 这个 ...

  6. 牛客编程巅峰赛S1第11场 - 黄金&钻石 C.牛牛找子集 (二分)

    题意:有一\(n\)个数,从中找数构成相同的子集,要求子集元素个数为\(k\),求构成子集个数最多的情况,输出子集(字典序最小). 题解:我们可以对子集的个数二分答案,首先用桶记录每个元素的个数,然后 ...

  7. 二、Python基础(input、变量名、条件语句、循环语句、注释)

    一.input用法 input在Python中的含义为永远等待,直到用户输入了值,从而将所输入的值赋值另外的一个东西. n=input('请输入......') 接下来用一个例子学习input的用法 ...

  8. VXLAN学习之路-结合VRF在Linux中实践VXLAN网络

    一.概述 近期在在搞网络安全HCIE.CISP的认证的事,顺便将VXLAN技术再次系统的学习一下,学习过程中看到云原生实验室里的一篇文章,就是关于VXLAN在Linux系统中的实践,感觉文章写得很好, ...

  9. WSL2+Terminal+VScode配置调试

    最近几天一直想找个方法把VMware虚拟机和远程连接工具MobaXterm这一组配合替换掉,因为每次开启虚拟机操作Ubuntu都需要占用很大的内存,而且要等好久好久才能开启!!!后面还要使用MobaX ...

  10. Linux内核实现透视---工作队列

    作为Linux中断低半部的另一种实现机制的基础,工作队列的出现更多的是为了解决软中断和Tasklet对于用户进程的时间片的不良影响问题的.工作队列本身是可以使用内核线程来替代的,但是使用线程来实现复杂 ...