简体   繁体   中英

CefSharp in TabControl not Showing Anything

I am developing a multi-tab browser using CefSharp. The code I have now is similar to the wpf example in CefSharp's github repo. However, the browser control is not working properly. It doesn't show anything. Please help. You can download the source here .

App.xaml

<Application x:Class="WpfApplication1.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:view="clr-namespace:WpfApplication1.Views"
    xmlns:viewModel="clr-namespace:WpfApplication1.ViewModels"
    StartupUri="MainWindow.xaml">
    <Application.Resources>
        <DataTemplate DataType="{x:Type viewModel:BrowserTabViewModel}">
            <view:BrowserTabView />
        </DataTemplate>
    </Application.Resources>
</Application>

App.xaml.cs

using System.Windows;
namespace WpfApplication1
{
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            CefSharp.Cef.Initialize(new CefSharp.CefSettings());
            base.OnStartup(e);
        }
    }
}

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:controls="clr-namespace:WpfApplication1.Controls"
        xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="Google" Command="New" CommandParameter="http://google.com"></MenuItem>
        </Menu>
        <controls:NonReloadingTabControl
            x:Name="TabControl"
            Margin="0,5,0,0"
            ItemsSource="{Binding BrowserTabs, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
            SelectedIndex="0">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Title}"/>
                        <Button Content="X"
                                Height="20"
                                Width="20"
                                FontFamily="Courier"
                                FontWeight="Bold"
                                Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
                                Command="Close"
                                FontSize="10"
                                Padding="0"
                                Margin="10,0,0,0"
                                ToolTip="Close"/>
                    </StackPanel>
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <ContentControl Content="{Binding}" />
                </DataTemplate>
            </TabControl.ContentTemplate>
        </controls:NonReloadingTabControl>
    </DockPanel>
</Window>

MainWindow.xaml.cs

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using WpfApplication1.ViewModels;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public ObservableCollection<BrowserTabViewModel> BrowserTabs { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            BrowserTabs = new ObservableCollection<BrowserTabViewModel>();
            CommandBindings.Add(new CommandBinding(ApplicationCommands.New, OpenNewTab));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, CloseTab));
        }

        private void CloseTab(object sender, ExecutedRoutedEventArgs e)
        {
            if (BrowserTabs.Count > 0)
            {
                //Obtain the original source element for this event
                var originalSource = (FrameworkElement)e.OriginalSource;

                BrowserTabViewModel browserViewModel = null;

                if (originalSource is MainWindow)
                {
                    browserViewModel = BrowserTabs[TabControl.SelectedIndex];
                    BrowserTabs.RemoveAt(TabControl.SelectedIndex);
                }
                else
                {
                    //Remove the matching DataContext from the BrowserTabs collection
                    browserViewModel = (BrowserTabViewModel)originalSource.DataContext;
                    BrowserTabs.Remove(browserViewModel);
                }

                browserViewModel.WebBrowser.Dispose();
            }
        }

        private void OpenNewTab(object sender, ExecutedRoutedEventArgs e)
        {
            CreateNewTab((string)e.Parameter);
            TabControl.SelectedIndex = TabControl.Items.Count - 1;
        }

        private void CreateNewTab(string url = "about:blank")
        {
            BrowserTabs.Add(new BrowserTabViewModel(url));
        }
    }
}

BroswerTabView.xaml

<UserControl x:Class="WpfApplication1.Views.BrowserTabView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <DockPanel>
        <StatusBar DockPanel.Dock="Bottom">
            <ProgressBar
                HorizontalAlignment="Right"
                IsIndeterminate="{Binding WebBrowser.IsLoading}"
                Width="100"
                Height="16"
                Margin="3" />
        </StatusBar>
        <cefSharp:ChromiumWebBrowser
            Address="{Binding Address, Mode=TwoWay}"
            Title="{Binding Title, Mode=OneWayToSource}"
            WebBrowser="{Binding WebBrowser, Mode=OneWayToSource}" />
    </DockPanel>
</UserControl>

BrowserTabView.xaml.cs

using System.Windows.Controls;

namespace WpfApplication1.Views
{
    /// <summary>
    /// Interaction logic for BrowserTabView.xaml
    /// </summary>
    public partial class BrowserTabView : UserControl
    {
        public BrowserTabView()
        {
            InitializeComponent();
        }
    }
}

BrowserTabViewModel.cs

using CefSharp.Wpf;
using GalaSoft.MvvmLight;

namespace WpfApplication1.ViewModels
{
    public class BrowserTabViewModel : ViewModelBase
    {
        private string address;
        public string Address
        {
            get { return address; }
            set { Set(ref address, value); }
        }

        private string title;
        public string Title
        {
            get { return title; }
            set { Set(ref title, value); }
        }

        private IWpfWebBrowser webBrowser;
        public IWpfWebBrowser WebBrowser
        {
            get { return webBrowser; }
            set { Set(ref webBrowser, value); }
        }

        public BrowserTabViewModel(string address)
        {
            Address = address;
        }
    }
}

NonReloadingTabControl.cs

using System;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace WpfApplication1.Controls
{
    [TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))]
    public class NonReloadingTabControl : TabControl
    {
        private Panel itemsHolderPanel;

        public NonReloadingTabControl()
        {
            // This is necessary so that we get the initial databound selected item
            ItemContainerGenerator.StatusChanged += ItemContainerGeneratorStatusChanged;
        }

        /// <summary>
        /// If containers are done, generate the selected item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ItemContainerGeneratorStatusChanged(object sender, EventArgs e)
        {
            if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                ItemContainerGenerator.StatusChanged -= ItemContainerGeneratorStatusChanged;
                UpdateSelectedItem();
            }
        }

        /// <summary>
        /// Get the ItemsHolder and generate any children
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            itemsHolderPanel = GetTemplateChild("PART_ItemsHolder") as Panel;
            UpdateSelectedItem();
        }

        /// <summary>
        /// When the items change we remove any generated panel children and add any new ones as necessary
        /// </summary>
        /// <param name="e"></param>
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);

            if (itemsHolderPanel == null)
                return;

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Reset:
                    itemsHolderPanel.Children.Clear();
                    break;

                case NotifyCollectionChangedAction.Add:
                case NotifyCollectionChangedAction.Remove:
                    if (e.OldItems != null)
                    {
                        foreach (var item in e.OldItems)
                        {
                            var cp = FindChildContentPresenter(item);
                            if (cp != null)
                                itemsHolderPanel.Children.Remove(cp);
                        }
                    }

                    // Don't do anything with new items because we don't want to
                    // create visuals that aren't being shown

                    UpdateSelectedItem();
                    break;

                case NotifyCollectionChangedAction.Replace:
                    throw new NotImplementedException("Replace not implemented yet");
            }
        }

        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.OnSelectionChanged(e);
            UpdateSelectedItem();
        }

        private void UpdateSelectedItem()
        {
            if (itemsHolderPanel == null)
                return;

            // Generate a ContentPresenter if necessary
            var item = GetSelectedTabItem();
            if (item != null)
                CreateChildContentPresenter(item);

            // show the right child
            foreach (ContentPresenter child in itemsHolderPanel.Children)
                child.Visibility = ((child.Tag as TabItem).IsSelected) ? Visibility.Visible : Visibility.Collapsed;
        }

        private ContentPresenter CreateChildContentPresenter(object item)
        {
            if (item == null)
                return null;

            var cp = FindChildContentPresenter(item);

            if (cp != null)
                return cp;

            var tabItem = item as TabItem;
            cp = new ContentPresenter
            {
                Content = (tabItem != null) ? tabItem.Content : item,
                ContentTemplate = this.SelectedContentTemplate,
                ContentTemplateSelector = this.SelectedContentTemplateSelector,
                ContentStringFormat = this.SelectedContentStringFormat,
                Visibility = Visibility.Collapsed,
                Tag = tabItem ?? (this.ItemContainerGenerator.ContainerFromItem(item))
            };
            itemsHolderPanel.Children.Add(cp);
            return cp;
        }

        private ContentPresenter FindChildContentPresenter(object data)
        {
            if (data is TabItem)
                data = (data as TabItem).Content;

            if (data == null)
                return null;

            if (itemsHolderPanel == null)
                return null;

            foreach (ContentPresenter cp in itemsHolderPanel.Children)
            {
                if (cp.Content == data)
                    return cp;
            }

            return null;
        }

        protected TabItem GetSelectedTabItem()
        {
            var selectedItem = SelectedItem;
            if (selectedItem == null)
                return null;

            var item = selectedItem as TabItem ?? ItemContainerGenerator.ContainerFromIndex(SelectedIndex) as TabItem;

            return item;
        }
    }
}

NonReloadingTabControl needs a custom style, see https://github.com/cefsharp/CefSharp/blob/master/CefSharp.Wpf.Example/App.xaml Or you may try hack NonReloadingTabControl.OnApplyTemplate() if you do not want to redefine the style

/// <summary>
/// Get the ItemsHolder and generate any children
/// </summary>
public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    itemsHolderPanel = new Grid();
    // exchange ContentPresenter for Grid
    var topGrid = (Grid)GetVisualChild(0);
    var border = (Border)topGrid.Children[1];
    border.Child = itemsHolderPanel;
    UpdateSelectedItem();
}

/// <summary>
/// Create Grid in code behind instead of defining own style.
/// </summary>
private Grid CreateGrid()
{
    var grid = new Grid();
    Binding binding = new Binding(PaddingProperty.Name);
    binding.Source = this;  // view model?
    grid.SetBinding(Grid.MarginProperty, binding);

    binding = new Binding(SnapsToDevicePixelsProperty.Name);
    binding.Source = this;  // view model?
    grid.SetBinding(Grid.SnapsToDevicePixelsProperty, binding);

    return grid;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM