Skip to content

Commit

Permalink
Added Virtualization option for Document and LayoutAnchorable tabbed …
Browse files Browse the repository at this point in the history
…items.
  • Loading branch information
Dirkster99 committed Apr 12, 2020
1 parent 74641ff commit 1a45dbb
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,17 @@ public class LayoutAnchorablePaneControl : TabControlEx, ILayoutControl//, ILogi
#endregion fields

#region Constructors

/// <summary>Static class constructor to register WPF style keys.</summary>
static LayoutAnchorablePaneControl()
{
FocusableProperty.OverrideMetadata(typeof(LayoutAnchorablePaneControl), new FrameworkPropertyMetadata(false));
}

public LayoutAnchorablePaneControl(LayoutAnchorablePane model)
/// <summary>Class constructor from model and virtualization parameter.</summary>
/// <param name="model"></param>
/// <param name="IsVirtualizing">Whether tabbed items are virtualized or not.</param>
internal LayoutAnchorablePaneControl(LayoutAnchorablePane model, bool IsVirtualizing)
: base(IsVirtualizing)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
SetBinding(ItemsSourceProperty, new Binding("Model.Children") { Source = this });
Expand All @@ -49,25 +53,44 @@ public LayoutAnchorablePaneControl(LayoutAnchorablePane model)
#endregion Constructors

#region Properties

/// <summary>Gets the layout model of this control.</summary>
public ILayoutElement Model => _model;

#endregion Properties

#region Overrides

/// <summary>
/// Invoked when an unhandled <see cref="System.Windows.Input.Keyboard.GotKeyboardFocus"/> attached
/// event reaches an element in its route that is derived from this class.
/// Implement this method to add class handling for this event.
/// </summary>
/// <param name="e">The <see cref="System.Windows.Input.KeyboardFocusChangedEventArgs"/> that contains the event data.</param>
protected override void OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
if (_model?.SelectedContent != null) _model.SelectedContent.IsActive = true;
base.OnGotKeyboardFocus(e);
}

/// <summary>
/// Invoked when an unhandled <see cref="System.Windows.UIElement.MouseLeftButtonDown"/> routed
/// event is raised on this element. Implement this method to add class handling
/// for this event.
/// </summary>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> that contains the event data.
/// The event data reports that the left mouse button was pressed.</param>
protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
if (!e.Handled && _model?.SelectedContent != null) _model.SelectedContent.IsActive = true;
}

/// <summary>
/// Invoked when an unhandled <see cref="System.Windows.UIElement.MouseRightButtonDown"/> routed
/// event reaches an element in its route that is derived from this class. Implement
/// this method to add class handling for this event.
/// </summary>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> that contains the event data. The
/// event data reports that the right mouse button was pressed.</param>
protected override void OnMouseRightButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
base.OnMouseRightButtonDown(e);
Expand Down
23 changes: 19 additions & 4 deletions source/Components/AvalonDock/Controls/LayoutDocumentPaneControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ static LayoutDocumentPaneControl()
FocusableProperty.OverrideMetadata(typeof(LayoutDocumentPaneControl), new FrameworkPropertyMetadata(false));
}

/// <summary>Class constructor from model parameter.</summary>
/// <summary>Class constructor from model and virtualization parameter.</summary>
/// <param name="model"></param>
internal LayoutDocumentPaneControl(LayoutDocumentPane model)
/// <param name="isVirtualizing">Whether tabbed items are virtualized or not.</param>
internal LayoutDocumentPaneControl(LayoutDocumentPane model, bool isVirtualizing)
: base(isVirtualizing)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
SetBinding(ItemsSourceProperty, new Binding("Model.Children") { Source = this });
Expand All @@ -51,20 +53,33 @@ internal LayoutDocumentPaneControl(LayoutDocumentPane model)
#endregion Constructors

#region Properties

/// <summary>Gets the layout model of this control.</summary>
public ILayoutElement Model => _model;

#endregion Properties

#region Overrides

/// <summary>
/// Invoked when an unhandled <see cref="System.Windows.UIElement.MouseLeftButtonDown"/> routed
/// event is raised on this element. Implement this method to add class handling
/// for this event.
/// </summary>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> that contains the event data.
/// The event data reports that the left mouse button was pressed.</param>
protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
if (!e.Handled && _model.SelectedContent != null)
_model.SelectedContent.IsActive = true;
}

/// <summary>
/// Invoked when an unhandled <see cref="System.Windows.UIElement.MouseRightButtonDown"/> routed
/// event reaches an element in its route that is derived from this class. Implement
/// this method to add class handling for this event.
/// </summary>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> that contains the event data. The
/// event data reports that the right mouse button was pressed.</param>
protected override void OnMouseRightButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
base.OnMouseRightButtonDown(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,57 @@
using System.Windows.Controls.Primitives;
using System.Windows.Data;

namespace AvalonDock
namespace AvalonDock.Controls
{
/// <summary>
/// This control added to mitigate issue with tab (document) switching speed
/// See this https://stackoverflow.com/questions/2080764/how-to-preserve-control-state-within-tab-items-in-a-tabcontrol
/// and this https://stackoverflow.com/questions/31030293/cefsharp-in-tabcontrol-not-working/37171847#37171847
///
/// by implmenting an option to enable virtualization for tabbed document containers.
/// </summary>
[TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))]
public class TabControlEx : TabControl
{
#region fields
private Panel ItemsHolderPanel = null;
private readonly bool _IsVirtualizing;
#endregion fields

public TabControlEx()
: base()
#region constructors
/// <summary>
/// Class constructor from virtualization parameter.
/// </summary>
/// <param name="isVirtualizing">Whether tabbed items are virtualized or not.</param>
public TabControlEx(bool isVirtualizing)
: this()
{
// This is necessary so that we get the initial databound selected item
ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
_IsVirtualizing = isVirtualizing;
}

/// <summary>
/// If containers are done, generate the selected item
/// Class constructor
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
protected TabControlEx()
: base()
{
if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
UpdateSelectedItem();
}
// This is necessary so that we get the initial databound selected item
ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}
#endregion constructors

#region methods
/// <summary>
/// Get the ItemsHolder and generate any children
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();

// Code below is required only if virtualization is turned ON
if (_IsVirtualizing == false)
return;

ItemsHolderPanel = CreateGrid();
// exchange ContentPresenter for Grid
var topGrid = (Grid)GetVisualChild(0);
Expand All @@ -68,19 +80,6 @@ public override void OnApplyTemplate()
UpdateSelectedItem();
}

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;
}

/// <summary>
/// When the items change we remove any generated panel children and add any new ones as necessary
Expand All @@ -90,6 +89,10 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);

// Code below is required only if virtualization is turned ON
if (_IsVirtualizing == false)
return;

if (ItemsHolderPanel == null)
return;

Expand Down Expand Up @@ -122,12 +125,71 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
}
}

/// <summary>
/// Raises the <see cref="System.Windows.Controls.Primitives.Selector.SelectionChanged"/> routed event.
/// </summary>
/// <param name="e">Provides data for <see cref="SelectionChangedEventArgs"/>.</param>
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);

// Code below is required only if virtualization is turned ON
if (_IsVirtualizing == false)
return;

UpdateSelectedItem();
}

/// <summary>
/// Gets the currently selected item (including its generation if Virtualization is currently switched on).
/// </summary>
/// <returns></returns>
protected TabItem GetSelectedTabItem()
{
object selectedItem = base.SelectedItem;

// Code below is required only if virtualization is turned ON
if (_IsVirtualizing == false)
return selectedItem as TabItem;

if (selectedItem == null)
return null;

TabItem item = selectedItem as TabItem;
if (item == null)
item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem;

return item;
}

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

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;
}

private void UpdateSelectedItem()
{
if (ItemsHolderPanel == null)
Expand Down Expand Up @@ -184,18 +246,6 @@ private ContentPresenter FindChildContentPresenter(object data)

return null;
}

protected TabItem GetSelectedTabItem()
{
object selectedItem = base.SelectedItem;
if (selectedItem == null)
return null;

TabItem item = selectedItem as TabItem;
if (item == null)
item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem;

return item;
}
#endregion methods
}
}
20 changes: 18 additions & 2 deletions source/Components/AvalonDock/DockingManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1319,6 +1319,22 @@ public bool AllowMixedOrientation

#endregion AllowMixedOrientation

#region IsVirtualizingLayoutDocument IsVirtualizingLayoutAnchorable

/// <summary>
/// Gets/sets (a simple non-dependency property) to determine whether the
/// <see cref="LayoutDocumentPaneControl"/> is virtualizing its tabbed item child controls or not.
/// </summary>
public bool IsVirtualizingLayoutDocument { get; set; }

/// <summary>
/// Gets/sets (a simple non-dependency property) to determine whether the
/// <see cref="LayoutAnchorablePaneControl"/> is virtualizing its tabbed item child controls or not.
/// </summary>
public bool IsVirtualizingLayoutAnchorable { get; set; }

#endregion IsVirtualizingLayoutDocument IsVirtualizingLayoutAnchorable

#endregion Public Properties

#region Private Properties
Expand Down Expand Up @@ -1506,13 +1522,13 @@ internal UIElement CreateUIElementForModel(ILayoutElement model)

if (model is LayoutDocumentPane)
{
var templateModelView = new LayoutDocumentPaneControl(model as LayoutDocumentPane);
var templateModelView = new LayoutDocumentPaneControl(model as LayoutDocumentPane, IsVirtualizingLayoutDocument);
templateModelView.SetBinding(StyleProperty, new Binding(DocumentPaneControlStyleProperty.Name) { Source = this });
return templateModelView;
}
if (model is LayoutAnchorablePane)
{
var templateModelView = new LayoutAnchorablePaneControl(model as LayoutAnchorablePane);
var templateModelView = new LayoutAnchorablePaneControl(model as LayoutAnchorablePane, IsVirtualizingLayoutAnchorable);
templateModelView.SetBinding(StyleProperty, new Binding(AnchorablePaneControlStyleProperty.Name) { Source = this });
return templateModelView;
}
Expand Down
4 changes: 3 additions & 1 deletion source/TestApp/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@
x:Name="dockManager"
Grid.Row="1"
AllowMixedOrientation="True"
DocumentClosing="dockManager_DocumentClosing">
DocumentClosing="dockManager_DocumentClosing"
IsVirtualizingLayoutAnchorable="False"
IsVirtualizingLayoutDocument="False">
<!-- avalonDock:DockingManager.Theme>
<avalonDock:Vs2013DarkTheme/>
</avalonDock:DockingManager.Theme-->
Expand Down

0 comments on commit 1a45dbb

Please sign in to comment.