From 1a45dbbe66c931e6c87ad769a9b269da4cb290ae Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Sun, 12 Apr 2020 20:12:09 +0200 Subject: [PATCH] Added Virtualization option for Document and LayoutAnchorable tabbed items. --- .../Controls/LayoutAnchorablePaneControl.cs | 31 ++++- .../Controls/LayoutDocumentPaneControl.cs | 23 +++- .../AvalonDock/{ => Controls}/TabControlEx.cs | 130 ++++++++++++------ .../Components/AvalonDock/DockingManager.cs | 20 ++- source/TestApp/MainWindow.xaml | 4 +- 5 files changed, 157 insertions(+), 51 deletions(-) rename source/Components/AvalonDock/{ => Controls}/TabControlEx.cs (79%) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorablePaneControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorablePaneControl.cs index 8c426ba1..5f44d6ed 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorablePaneControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorablePaneControl.cs @@ -30,13 +30,17 @@ public class LayoutAnchorablePaneControl : TabControlEx, ILayoutControl//, ILogi #endregion fields #region Constructors - + /// Static class constructor to register WPF style keys. static LayoutAnchorablePaneControl() { FocusableProperty.OverrideMetadata(typeof(LayoutAnchorablePaneControl), new FrameworkPropertyMetadata(false)); } - public LayoutAnchorablePaneControl(LayoutAnchorablePane model) + /// Class constructor from model and virtualization parameter. + /// + /// Whether tabbed items are virtualized or not. + internal LayoutAnchorablePaneControl(LayoutAnchorablePane model, bool IsVirtualizing) + : base(IsVirtualizing) { _model = model ?? throw new ArgumentNullException(nameof(model)); SetBinding(ItemsSourceProperty, new Binding("Model.Children") { Source = this }); @@ -49,25 +53,44 @@ public LayoutAnchorablePaneControl(LayoutAnchorablePane model) #endregion Constructors #region Properties - + /// Gets the layout model of this control. public ILayoutElement Model => _model; #endregion Properties #region Overrides - + /// + /// Invoked when an unhandled attached + /// event reaches an element in its route that is derived from this class. + /// Implement this method to add class handling for this event. + /// + /// The that contains the event data. protected override void OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e) { if (_model?.SelectedContent != null) _model.SelectedContent.IsActive = true; base.OnGotKeyboardFocus(e); } + /// + /// Invoked when an unhandled routed + /// event is raised on this element. Implement this method to add class handling + /// for this event. + /// + /// The that contains the event data. + /// The event data reports that the left mouse button was pressed. protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); if (!e.Handled && _model?.SelectedContent != null) _model.SelectedContent.IsActive = true; } + /// + /// Invoked when an unhandled routed + /// event reaches an element in its route that is derived from this class. Implement + /// this method to add class handling for this event. + /// + /// The that contains the event data. The + /// event data reports that the right mouse button was pressed. protected override void OnMouseRightButtonDown(System.Windows.Input.MouseButtonEventArgs e) { base.OnMouseRightButtonDown(e); diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentPaneControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentPaneControl.cs index dbd3e132..31e18401 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentPaneControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentPaneControl.cs @@ -37,9 +37,11 @@ static LayoutDocumentPaneControl() FocusableProperty.OverrideMetadata(typeof(LayoutDocumentPaneControl), new FrameworkPropertyMetadata(false)); } - /// Class constructor from model parameter. + /// Class constructor from model and virtualization parameter. /// - internal LayoutDocumentPaneControl(LayoutDocumentPane model) + /// Whether tabbed items are virtualized or not. + internal LayoutDocumentPaneControl(LayoutDocumentPane model, bool isVirtualizing) + : base(isVirtualizing) { _model = model ?? throw new ArgumentNullException(nameof(model)); SetBinding(ItemsSourceProperty, new Binding("Model.Children") { Source = this }); @@ -51,13 +53,19 @@ internal LayoutDocumentPaneControl(LayoutDocumentPane model) #endregion Constructors #region Properties - + /// Gets the layout model of this control. public ILayoutElement Model => _model; #endregion Properties #region Overrides - + /// + /// Invoked when an unhandled routed + /// event is raised on this element. Implement this method to add class handling + /// for this event. + /// + /// The that contains the event data. + /// The event data reports that the left mouse button was pressed. protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); @@ -65,6 +73,13 @@ protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEv _model.SelectedContent.IsActive = true; } + /// + /// Invoked when an unhandled routed + /// event reaches an element in its route that is derived from this class. Implement + /// this method to add class handling for this event. + /// + /// The that contains the event data. The + /// event data reports that the right mouse button was pressed. protected override void OnMouseRightButtonDown(System.Windows.Input.MouseButtonEventArgs e) { base.OnMouseRightButtonDown(e); diff --git a/source/Components/AvalonDock/TabControlEx.cs b/source/Components/AvalonDock/Controls/TabControlEx.cs similarity index 79% rename from source/Components/AvalonDock/TabControlEx.cs rename to source/Components/AvalonDock/Controls/TabControlEx.cs index 721939b5..5d2993bb 100644 --- a/source/Components/AvalonDock/TabControlEx.cs +++ b/source/Components/AvalonDock/Controls/TabControlEx.cs @@ -5,45 +5,57 @@ using System.Windows.Controls.Primitives; using System.Windows.Data; -namespace AvalonDock +namespace AvalonDock.Controls { /// /// 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. /// [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 + /// + /// Class constructor from virtualization parameter. + /// + /// Whether tabbed items are virtualized or not. + public TabControlEx(bool isVirtualizing) + : this() { - // This is necessary so that we get the initial databound selected item - ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; + _IsVirtualizing = isVirtualizing; } /// - /// If containers are done, generate the selected item + /// Class constructor /// - /// - /// - 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 /// /// Get the ItemsHolder and generate any children /// 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); @@ -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; - } /// /// When the items change we remove any generated panel children and add any new ones as necessary @@ -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; @@ -122,12 +125,71 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) } } + /// + /// Raises the routed event. + /// + /// Provides data for . protected override void OnSelectionChanged(SelectionChangedEventArgs e) { base.OnSelectionChanged(e); + + // Code below is required only if virtualization is turned ON + if (_IsVirtualizing == false) + return; + UpdateSelectedItem(); } + /// + /// Gets the currently selected item (including its generation if Virtualization is currently switched on). + /// + /// + 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; + } + + /// + /// If containers are done, generate the selected item + /// + /// + /// + 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) @@ -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 } } diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index 28ffb076..5382fc4e 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1319,6 +1319,22 @@ public bool AllowMixedOrientation #endregion AllowMixedOrientation + #region IsVirtualizingLayoutDocument IsVirtualizingLayoutAnchorable + + /// + /// Gets/sets (a simple non-dependency property) to determine whether the + /// is virtualizing its tabbed item child controls or not. + /// + public bool IsVirtualizingLayoutDocument { get; set; } + + /// + /// Gets/sets (a simple non-dependency property) to determine whether the + /// is virtualizing its tabbed item child controls or not. + /// + public bool IsVirtualizingLayoutAnchorable { get; set; } + + #endregion IsVirtualizingLayoutDocument IsVirtualizingLayoutAnchorable + #endregion Public Properties #region Private Properties @@ -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; } diff --git a/source/TestApp/MainWindow.xaml b/source/TestApp/MainWindow.xaml index 425d2e69..ba2a23d6 100644 --- a/source/TestApp/MainWindow.xaml +++ b/source/TestApp/MainWindow.xaml @@ -64,7 +64,9 @@ x:Name="dockManager" Grid.Row="1" AllowMixedOrientation="True" - DocumentClosing="dockManager_DocumentClosing"> + DocumentClosing="dockManager_DocumentClosing" + IsVirtualizingLayoutAnchorable="False" + IsVirtualizingLayoutDocument="False">