diff --git a/CefSharp.Wpf.Example/App.xaml b/CefSharp.Wpf.Example/App.xaml
index 38d64b8ff5..184cec1910 100644
--- a/CefSharp.Wpf.Example/App.xaml
+++ b/CefSharp.Wpf.Example/App.xaml
@@ -1,4 +1,87 @@
+ xmlns:controls="clr-namespace:CefSharp.Wpf.Example.Controls"
+ xmlns:views="clr-namespace:CefSharp.Wpf.Example.Views.Main"
+ xmlns:browserTab="clr-namespace:CefSharp.Wpf.Example.Views.BrowserTab"
+ StartupUri="MainWindow.xaml">
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CefSharp.Wpf.Example/App.xaml.cs b/CefSharp.Wpf.Example/App.xaml.cs
index a44df3d687..6d17f67936 100644
--- a/CefSharp.Wpf.Example/App.xaml.cs
+++ b/CefSharp.Wpf.Example/App.xaml.cs
@@ -1,4 +1,8 @@
-using System.Windows;
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System.Windows;
using CefSharp.Example;
namespace CefSharp.Wpf.Example
diff --git a/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj b/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj
index 63fe18a9a8..5f7d61519c 100644
--- a/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj
+++ b/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj
@@ -96,13 +96,14 @@
MSBuild:Compile
Designer
+
-
- MainView.xaml
+
+ BrowserTabView.xaml
-
+
MSBuild:Compile
Designer
@@ -117,9 +118,9 @@
MainWindow.xaml
Code
-
- Designer
+
MSBuild:Compile
+ Designer
diff --git a/CefSharp.Wpf.Example/Controls/TabControlEx.cs b/CefSharp.Wpf.Example/Controls/TabControlEx.cs
new file mode 100644
index 0000000000..51703bd9c0
--- /dev/null
+++ b/CefSharp.Wpf.Example/Controls/TabControlEx.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Specialized;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+
+namespace CefSharp.Wpf.Example.Controls
+{
+ ///
+ /// NOTE: Source directly imported from http://stackoverflow.com/a/9802346
+ /// TabControlEx - Extended TabControl which saves the displayed item so you don't get the performance hit of
+ /// unloading and reloading the VisualTree when switching tabs
+ /// Obtained from http://www.pluralsight-training.net/community/blogs/eburke/archive/2009/04/30/keeping-the-wpf-tab-control-from-destroying-its-children.aspx
+ /// and made a some modifications so it reuses a TabItem's ContentPresenter when doing drag/drop operations
+ ///
+ [TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))]
+ public class TabControlEx : TabControl
+ {
+ private Panel itemsHolderPanel;
+
+ public TabControlEx()
+ {
+ // This is necessary so that we get the initial databound selected item
+ ItemContainerGenerator.StatusChanged += ItemContainerGeneratorStatusChanged;
+ }
+
+ ///
+ /// If containers are done, generate the selected item
+ ///
+ ///
+ ///
+ private void ItemContainerGeneratorStatusChanged(object sender, EventArgs e)
+ {
+ if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
+ {
+ ItemContainerGenerator.StatusChanged -= ItemContainerGeneratorStatusChanged;
+ UpdateSelectedItem();
+ }
+ }
+
+ ///
+ /// Get the ItemsHolder and generate any children
+ ///
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ itemsHolderPanel = GetTemplateChild("PART_ItemsHolder") as Panel;
+ UpdateSelectedItem();
+ }
+
+ ///
+ /// When the items change we remove any generated panel children and add any new ones as necessary
+ ///
+ ///
+ 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;
+
+ // the actual child to be added. cp.Tag is a reference to the TabItem
+ cp = new ContentPresenter();
+ cp.Content = (item is TabItem) ? (item as TabItem).Content : item;
+ cp.ContentTemplate = this.SelectedContentTemplate;
+ cp.ContentTemplateSelector = this.SelectedContentTemplateSelector;
+ cp.ContentStringFormat = this.SelectedContentStringFormat;
+ cp.Visibility = Visibility.Collapsed;
+ cp.Tag = (item is TabItem) ? item : (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;
+ }
+ }
+}
diff --git a/CefSharp.Wpf.Example/MainWindow.xaml b/CefSharp.Wpf.Example/MainWindow.xaml
index 296f017d95..e06c55168b 100644
--- a/CefSharp.Wpf.Example/MainWindow.xaml
+++ b/CefSharp.Wpf.Example/MainWindow.xaml
@@ -1,27 +1,41 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CefSharp.Wpf.Example/MainWindow.xaml.cs b/CefSharp.Wpf.Example/MainWindow.xaml.cs
index 69f3b1f48c..c99d600520 100644
--- a/CefSharp.Wpf.Example/MainWindow.xaml.cs
+++ b/CefSharp.Wpf.Example/MainWindow.xaml.cs
@@ -1,65 +1,68 @@
-using System.Windows.Data;
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System.Collections.ObjectModel;
+using System.Windows;
using System.Windows.Input;
+using CefSharp.Example;
using CefSharp.Wpf.Example.Views.Main;
-using System.Windows;
-using System.Windows.Controls;
namespace CefSharp.Wpf.Example
{
- public partial class MainWindow : Window
- {
- public FrameworkElement Tab1Content { get; set; }
- public FrameworkElement Tab2Content { get; set; }
+ public partial class MainWindow : Window
+ {
+ private const string DefaultUrl = "https://www.google.com.au";
+
+ public ObservableCollection BrowserTabs { get; set; }
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ DataContext = this;
+
+ BrowserTabs = new ObservableCollection();
- public MainWindow()
- {
- InitializeComponent();
- DataContext = this;
+ CommandBindings.Add(new CommandBinding(ApplicationCommands.New, OpenNewTab));
+ CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, CloseTab));
- Tab1Content = new MainView
- {
- DataContext = new MainViewModel { ShowSidebar = true }
- };
+ Loaded += MainWindowLoaded;
+ }
- Tab2Content = CreateNewTab();
- }
+ private void CloseTab(object sender, ExecutedRoutedEventArgs e)
+ {
+ if (BrowserTabs.Count > 0)
+ {
+ //Obtain the original source element for this event
+ var originalSource = (FrameworkElement)e.OriginalSource;
- private void OnTabControlSelectionChanged(object sender, SelectionChangedEventArgs e)
- {
- if (e.AddedItems.Count != 1) return;
+ if (originalSource is MainWindow)
+ {
+ BrowserTabs.RemoveAt(TabControl.SelectedIndex);
+ }
+ else
+ {
+ //Remove the matching DataContext from the BrowserTabs collection
+ BrowserTabs.Remove((BrowserTabViewModel)originalSource.DataContext);
+ }
+ }
+ }
- var selectedtab = (TabItem)e.AddedItems[0];
- if ((string)selectedtab.Header != "+")
- {
- return;
- }
+ private void OpenNewTab(object sender, ExecutedRoutedEventArgs e)
+ {
+ CreateNewTab();
- var tabItem = CreateTabItem();
- TabControl.Items.Insert(TabControl.Items.Count - 1, tabItem);
- }
+ TabControl.SelectedIndex = TabControl.Items.Count - 1;
+ }
- private static TabItem CreateTabItem()
- {
- var tabItem = new TabItem
- {
- Width = 150,
- Height = 20,
- IsSelected = true,
- Content = CreateNewTab()
- };
- tabItem.SetBinding(HeaderedContentControl.HeaderProperty, new Binding("Content.DataContext.Title")
- {
- RelativeSource = RelativeSource.Self
- });
- return tabItem;
- }
+ private void MainWindowLoaded(object sender, RoutedEventArgs e)
+ {
+ CreateNewTab(ExamplePresenter.DefaultUrl, true);
+ }
- private static MainView CreateNewTab()
- {
- return new MainView
- {
- DataContext = new MainViewModel("http://www.google.com")
- };
- }
- }
+ private void CreateNewTab(string url = DefaultUrl, bool showSideBar = false)
+ {
+ BrowserTabs.Add(new BrowserTabViewModel(url) { ShowSidebar = showSideBar });
+ }
+ }
}
diff --git a/CefSharp.Wpf.Example/Mvvm/DelegateCommand.cs b/CefSharp.Wpf.Example/Mvvm/DelegateCommand.cs
index 23d9fc6caa..af3fda400c 100644
--- a/CefSharp.Wpf.Example/Mvvm/DelegateCommand.cs
+++ b/CefSharp.Wpf.Example/Mvvm/DelegateCommand.cs
@@ -1,4 +1,8 @@
-using System;
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System;
using System.Windows.Input;
namespace CefSharp.Wpf.Example.Mvvm
diff --git a/CefSharp.Wpf.Example/Mvvm/DelegateCommandT.cs b/CefSharp.Wpf.Example/Mvvm/DelegateCommandT.cs
index f76ad483e8..a34cdab9d6 100644
--- a/CefSharp.Wpf.Example/Mvvm/DelegateCommandT.cs
+++ b/CefSharp.Wpf.Example/Mvvm/DelegateCommandT.cs
@@ -1,4 +1,8 @@
-using System;
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System;
using System.Windows.Input;
namespace CefSharp.Wpf.Example.Mvvm
diff --git a/CefSharp.Wpf.Example/Mvvm/ViewModelBase.cs b/CefSharp.Wpf.Example/Mvvm/ViewModelBase.cs
index 528139f2a1..43bab9267e 100644
--- a/CefSharp.Wpf.Example/Mvvm/ViewModelBase.cs
+++ b/CefSharp.Wpf.Example/Mvvm/ViewModelBase.cs
@@ -1,4 +1,8 @@
-using System;
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
diff --git a/CefSharp.Wpf.Example/Views/Main/MainView.xaml b/CefSharp.Wpf.Example/Views/BrowserTab/BrowserTabView.xaml
similarity index 97%
rename from CefSharp.Wpf.Example/Views/Main/MainView.xaml
rename to CefSharp.Wpf.Example/Views/BrowserTab/BrowserTabView.xaml
index c74f388c34..d065f5cfe3 100644
--- a/CefSharp.Wpf.Example/Views/Main/MainView.xaml
+++ b/CefSharp.Wpf.Example/Views/BrowserTab/BrowserTabView.xaml
@@ -1,4 +1,4 @@
-
+ d:DataContext="{d:DesignInstance local:BrowserTabViewModel}">
@@ -199,7 +199,7 @@
Command="{Binding WebBrowser.ZoomResetCommand}" />
+ Command="{Binding WebBrowser.ViewSourceCommand}" />
diff --git a/CefSharp.Wpf.Example/Views/BrowserTab/BrowserTabView.xaml.cs b/CefSharp.Wpf.Example/Views/BrowserTab/BrowserTabView.xaml.cs
new file mode 100644
index 0000000000..e01004a687
--- /dev/null
+++ b/CefSharp.Wpf.Example/Views/BrowserTab/BrowserTabView.xaml.cs
@@ -0,0 +1,29 @@
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace CefSharp.Wpf.Example.Views.BrowserTab
+{
+ public partial class BrowserTabView : UserControl
+ {
+ public BrowserTabView()
+ {
+ InitializeComponent();
+ }
+
+ private void OnTextBoxGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
+ {
+ var textBox = (TextBox)sender;
+ textBox.SelectAll();
+ }
+
+ private void OnTextBoxGotMouseCapture(object sender, MouseEventArgs e)
+ {
+ var textBox = (TextBox)sender;
+ textBox.SelectAll();
+ }
+ }
+}
diff --git a/CefSharp.Wpf.Example/Views/Main/MainViewModel.cs b/CefSharp.Wpf.Example/Views/BrowserTab/BrowserTabViewModel.cs
similarity index 94%
rename from CefSharp.Wpf.Example/Views/Main/MainViewModel.cs
rename to CefSharp.Wpf.Example/Views/BrowserTab/BrowserTabViewModel.cs
index aecf0b65af..0d3599b9cb 100644
--- a/CefSharp.Wpf.Example/Views/Main/MainViewModel.cs
+++ b/CefSharp.Wpf.Example/Views/BrowserTab/BrowserTabViewModel.cs
@@ -1,13 +1,17 @@
-using CefSharp.Example;
-using CefSharp.Wpf.Example.Mvvm;
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using CefSharp.Example;
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
+using CefSharp.Wpf.Example.Mvvm;
namespace CefSharp.Wpf.Example.Views.Main
{
- public class MainViewModel : INotifyPropertyChanged
+ public class BrowserTabViewModel : INotifyPropertyChanged
{
private string address;
public string Address
@@ -67,7 +71,7 @@ public bool ShowSidebar
public event PropertyChangedEventHandler PropertyChanged;
- public MainViewModel(string address = null)
+ public BrowserTabViewModel(string address = null)
{
Address = address ?? ExamplePresenter.DefaultUrl;
AddressEditable = Address;
diff --git a/CefSharp.Wpf.Example/Views/Main/MainView.xaml.cs b/CefSharp.Wpf.Example/Views/Main/MainView.xaml.cs
deleted file mode 100644
index 5fc68dcecb..0000000000
--- a/CefSharp.Wpf.Example/Views/Main/MainView.xaml.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Windows.Controls;
-using System.Windows.Input;
-
-namespace CefSharp.Wpf.Example.Views.Main
-{
- public partial class MainView : UserControl
- {
- public MainView()
- {
- InitializeComponent();
- }
-
- private void OnTextBoxGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
- {
- var textBox = (TextBox) sender;
- textBox.SelectAll();
- }
-
- private void OnTextBoxGotMouseCapture(object sender, MouseEventArgs e)
- {
- var textBox = (TextBox) sender;
- textBox.SelectAll();
- }
- }
-}
diff --git a/CefSharp.Wpf/DelegateCommand.cs b/CefSharp.Wpf/DelegateCommand.cs
index 208d81f39a..dd5335ff76 100644
--- a/CefSharp.Wpf/DelegateCommand.cs
+++ b/CefSharp.Wpf/DelegateCommand.cs
@@ -1,4 +1,8 @@
-using System;
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
diff --git a/CefSharp.Wpf/IWpfWebBrowser.cs b/CefSharp.Wpf/IWpfWebBrowser.cs
index db755498f1..6d4feb5fa9 100644
--- a/CefSharp.Wpf/IWpfWebBrowser.cs
+++ b/CefSharp.Wpf/IWpfWebBrowser.cs
@@ -1,4 +1,8 @@
-using System.Windows.Input;
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System.Windows.Input;
namespace CefSharp.Wpf
{
@@ -57,6 +61,11 @@ public interface IWpfWebBrowser : IWebBrowser
///
ICommand ZoomResetCommand { get; }
+ ///
+ /// Views the source of current page
+ ///
+ ICommand ViewSourceCommand { get; }
+
///
/// Opens up a new program window (using the default text editor) where the source code of the currently displayed web
/// page is shown.
diff --git a/CefSharp.Wpf/WM.cs b/CefSharp.Wpf/WM.cs
index 4300bff592..4a96191dee 100644
--- a/CefSharp.Wpf/WM.cs
+++ b/CefSharp.Wpf/WM.cs
@@ -1,4 +1,8 @@
-namespace CefSharp.Wpf
+// Copyright © 2010-2014 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+namespace CefSharp.Wpf
{
// Gratiously based on http://www.pinvoke.net/default.aspx/Enums/WindowsMessages.html
public enum WM : uint
diff --git a/CefSharp.Wpf/WebView.cs b/CefSharp.Wpf/WebView.cs
index 7d0c4c6461..86ab73b03f 100644
--- a/CefSharp.Wpf/WebView.cs
+++ b/CefSharp.Wpf/WebView.cs
@@ -57,6 +57,7 @@ public class WebView : ContentControl, IRenderWebBrowser, IWpfWebBrowser
public ICommand ZoomInCommand { get; private set; }
public ICommand ZoomOutCommand { get; private set; }
public ICommand ZoomResetCommand { get; private set; }
+ public ICommand ViewSourceCommand { get; private set; }
public bool CanGoBack { get; private set; }
public bool CanGoForward { get; private set; }
@@ -338,6 +339,7 @@ public WebView()
ZoomInCommand = new DelegateCommand(ZoomIn);
ZoomOutCommand = new DelegateCommand(ZoomOut);
ZoomResetCommand = new DelegateCommand(ZoomReset);
+ ViewSourceCommand = new DelegateCommand(ViewSource);
managedCefBrowserAdapter = new ManagedCefBrowserAdapter(this);
managedCefBrowserAdapter.CreateOffscreenBrowser(BrowserSettings ?? new BrowserSettings());