diff --git a/Dragablz/TabablzControl.cs b/Dragablz/TabablzControl.cs
index c161770..8ff95ab 100644
--- a/Dragablz/TabablzControl.cs
+++ b/Dragablz/TabablzControl.cs
@@ -1,1538 +1,1564 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Linq;
-using System.Windows;
-using System.Windows.Automation.Peers;
-using System.Windows.Controls;
-using System.Windows.Controls.Primitives;
-using System.Windows.Data;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Threading;
-using Dragablz.Core;
-using Dragablz.Dockablz;
-using Dragablz.Referenceless;
-
-namespace Dragablz
-{
- //original code specific to keeping visual tree "alive" sourced from http://stackoverflow.com/questions/12432062/binding-to-itemssource-of-tabcontrol-in-wpf
-
- ///
- /// Extended tab control which supports tab repositioning, and drag and drop. Also
- /// uses the common WPF technique for pesisting the visual tree across tabs.
- ///
- [TemplatePart(Name = HeaderItemsControlPartName, Type = typeof(DragablzItemsControl))]
- [TemplatePart(Name = ItemsHolderPartName, Type = typeof(Panel))]
- public class TabablzControl : TabControl
- {
- ///
- /// Template part.
- ///
- public const string HeaderItemsControlPartName = "PART_HeaderItemsControl";
- ///
- /// Template part.
- ///
- public const string ItemsHolderPartName = "PART_ItemsHolder";
-
- ///
- /// Routed command which can be used to close a tab.
- ///
- public static RoutedCommand CloseItemCommand = new RoutedUICommand("Close", "Close", typeof(TabablzControl));
-
- ///
- /// Routed command which can be used to add a new tab. See .
- ///
- public static RoutedCommand AddItemCommand = new RoutedUICommand("Add", "Add", typeof(TabablzControl));
-
- private static readonly HashSet LoadedInstances = new HashSet();
- private static readonly HashSet VisibleInstances = new HashSet();
-
- private Panel _itemsHolder;
- private TabHeaderDragStartInformation _tabHeaderDragStartInformation;
- private WeakReference _previousSelection;
- private DragablzItemsControl _dragablzItemsControl;
- private IDisposable _templateSubscription;
- private readonly SerialDisposable _windowSubscription = new SerialDisposable();
-
- private InterTabTransfer _interTabTransfer;
-
- static TabablzControl()
- {
- DefaultStyleKeyProperty.OverrideMetadata(typeof(TabablzControl), new FrameworkPropertyMetadata(typeof(TabablzControl)));
- CommandManager.RegisterClassCommandBinding(typeof(FrameworkElement), new CommandBinding(CloseItemCommand, CloseItemClassHandler, CloseItemCanExecuteClassHandler));
- }
-
- ///
- /// Default constructor.
- ///
- public TabablzControl()
- {
- AddHandler(DragablzItem.DragStarted, new DragablzDragStartedEventHandler(ItemDragStarted), true);
- AddHandler(DragablzItem.PreviewDragDelta, new DragablzDragDeltaEventHandler(PreviewItemDragDelta), true);
- AddHandler(DragablzItem.DragDelta, new DragablzDragDeltaEventHandler(ItemDragDelta), true);
- AddHandler(DragablzItem.DragCompleted, new DragablzDragCompletedEventHandler(ItemDragCompleted), true);
- CommandBindings.Add(new CommandBinding(AddItemCommand, AddItemHandler));
-
- Loaded += OnLoaded;
- Unloaded += OnUnloaded;
- IsVisibleChanged += OnIsVisibleChanged;
- }
-
- public static readonly DependencyProperty CustomHeaderItemStyleProperty = DependencyProperty.Register(
- "CustomHeaderItemStyle", typeof (Style), typeof (TabablzControl), new PropertyMetadata(default(Style)));
-
- ///
- /// Helper method which returns all the currently loaded instances.
- ///
- ///
- public static IEnumerable GetLoadedInstances()
- {
- return LoadedInstances.Union(VisibleInstances).Distinct().ToList();
- }
-
- ///
- /// Helper method to close all tabs where the item is the tab's content (helpful with MVVM scenarios)
- ///
- ///
- /// In MVVM scenarios where you don't want to bind the routed command to your ViewModel,
- /// with this helper method and embedding the TabablzControl in a UserControl, you can keep
- /// the View-specific dependencies out of the ViewModel.
- ///
- /// An existing Tab item content (a ViewModel in MVVM scenarios) which is backing a tab control
- public static void CloseItem(object tabContentItem)
- {
- if (tabContentItem == null) return; //Do nothing.
-
- //Find all loaded TabablzControl instances with tabs backed by this item and close them
- foreach(var tabWithItemContent in
- GetLoadedInstances().SelectMany(tc =>
- tc._dragablzItemsControl.DragablzItems().Where(di => di.Content.Equals(tabContentItem)).Select(di => new { tc, di })))
- {
- TabablzControl.CloseItem(tabWithItemContent.di, tabWithItemContent.tc);
- }
- }
-
- ///
- /// Helper method to add an item next to an existing item.
- ///
- ///
- /// Due to the organisable nature of the control, the order of items may not reflect the order in the source collection. This method
- /// will add items to the source collection, managing their initial appearance on screen at the same time.
- /// If you are using a this will be used to add the item into the source collection.
- ///
- /// New item to add.
- /// Existing object/tab item content which defines which tab control should be used to add the object.
- /// Location, relative to the object
- public static void AddItem(object item, object nearItem, AddLocationHint addLocationHint)
- {
- if (nearItem == null) throw new ArgumentNullException("nearItem");
-
- var existingLocation = GetLoadedInstances().SelectMany(tabControl =>
- (tabControl.ItemsSource ?? tabControl.Items).OfType()
- .Select(existingObject => new {tabControl, existingObject}))
- .SingleOrDefault(a => nearItem.Equals(a.existingObject));
-
- if (existingLocation == null)
- throw new ArgumentException("Did not find precisely one instance of adjacentTo", "nearItem");
-
- existingLocation.tabControl.AddToSource(item);
- if (existingLocation.tabControl._dragablzItemsControl != null)
- existingLocation.tabControl._dragablzItemsControl.MoveItem(new MoveItemRequest(item, nearItem, addLocationHint));
- }
-
- ///
- /// Finds and selects an item.
- ///
- ///
- public static void SelectItem(object item)
- {
- var existingLocation = GetLoadedInstances().SelectMany(tabControl =>
- (tabControl.ItemsSource ?? tabControl.Items).OfType()
- .Select(existingObject => new {tabControl, existingObject}))
- .FirstOrDefault(a => item.Equals(a.existingObject));
-
- if (existingLocation == null) return;
-
- existingLocation.tabControl.SelectedItem = item;
- }
-
- ///
- /// Style to apply to header items which are not their own item container ( ). Typically items bound via the will use this style.
- ///
- [Obsolete]
- public Style CustomHeaderItemStyle
- {
- get { return (Style) GetValue(CustomHeaderItemStyleProperty); }
- set { SetValue(CustomHeaderItemStyleProperty, value); }
- }
-
- public static readonly DependencyProperty CustomHeaderItemTemplateProperty = DependencyProperty.Register(
- "CustomHeaderItemTemplate", typeof (DataTemplate), typeof (TabablzControl), new PropertyMetadata(default(DataTemplate)));
-
- [Obsolete("Prefer HeaderItemTemplate")]
- public DataTemplate CustomHeaderItemTemplate
- {
- get { return (DataTemplate) GetValue(CustomHeaderItemTemplateProperty); }
- set { SetValue(CustomHeaderItemTemplateProperty, value); }
- }
-
- public static readonly DependencyProperty DefaultHeaderItemStyleProperty = DependencyProperty.Register(
- "DefaultHeaderItemStyle", typeof (Style), typeof (TabablzControl), new PropertyMetadata(default(Style)));
-
- [Obsolete]
- public Style DefaultHeaderItemStyle
- {
- get { return (Style) GetValue(DefaultHeaderItemStyleProperty); }
- set { SetValue(DefaultHeaderItemStyleProperty, value); }
- }
-
- public static readonly DependencyProperty AdjacentHeaderItemOffsetProperty = DependencyProperty.Register(
- "AdjacentHeaderItemOffset", typeof (double), typeof (TabablzControl), new PropertyMetadata(default(double), AdjacentHeaderItemOffsetPropertyChangedCallback));
-
- private static void AdjacentHeaderItemOffsetPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
- {
- dependencyObject.SetValue(HeaderItemsOrganiserProperty, new HorizontalOrganiser((double)dependencyPropertyChangedEventArgs.NewValue));
- }
-
- public double AdjacentHeaderItemOffset
- {
- get { return (double) GetValue(AdjacentHeaderItemOffsetProperty); }
- set { SetValue(AdjacentHeaderItemOffsetProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderItemsOrganiserProperty = DependencyProperty.Register(
- "HeaderItemsOrganiser", typeof (IItemsOrganiser), typeof (TabablzControl), new PropertyMetadata(new HorizontalOrganiser()));
-
- public IItemsOrganiser HeaderItemsOrganiser
- {
- get { return (IItemsOrganiser) GetValue(HeaderItemsOrganiserProperty); }
- set { SetValue(HeaderItemsOrganiserProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderMemberPathProperty = DependencyProperty.Register(
- "HeaderMemberPath", typeof (string), typeof (TabablzControl), new PropertyMetadata(default(string)));
-
- public string HeaderMemberPath
- {
- get { return (string) GetValue(HeaderMemberPathProperty); }
- set { SetValue(HeaderMemberPathProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderItemTemplateProperty = DependencyProperty.Register(
- "HeaderItemTemplate", typeof (DataTemplate), typeof (TabablzControl), new PropertyMetadata(default(DataTemplate)));
-
- public DataTemplate HeaderItemTemplate
- {
- get { return (DataTemplate) GetValue(HeaderItemTemplateProperty); }
- set { SetValue(HeaderItemTemplateProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderPrefixContentProperty = DependencyProperty.Register(
- "HeaderPrefixContent", typeof (object), typeof (TabablzControl), new PropertyMetadata(default(object)));
-
- public object HeaderPrefixContent
- {
- get { return (object) GetValue(HeaderPrefixContentProperty); }
- set { SetValue(HeaderPrefixContentProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderPrefixContentStringFormatProperty = DependencyProperty.Register(
- "HeaderPrefixContentStringFormat", typeof (string), typeof (TabablzControl), new PropertyMetadata(default(string)));
-
- public string HeaderPrefixContentStringFormat
- {
- get { return (string) GetValue(HeaderPrefixContentStringFormatProperty); }
- set { SetValue(HeaderPrefixContentStringFormatProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderPrefixContentTemplateProperty = DependencyProperty.Register(
- "HeaderPrefixContentTemplate", typeof (DataTemplate), typeof (TabablzControl), new PropertyMetadata(default(DataTemplate)));
-
- public DataTemplate HeaderPrefixContentTemplate
- {
- get { return (DataTemplate) GetValue(HeaderPrefixContentTemplateProperty); }
- set { SetValue(HeaderPrefixContentTemplateProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderPrefixContentTemplateSelectorProperty = DependencyProperty.Register(
- "HeaderPrefixContentTemplateSelector", typeof (DataTemplateSelector), typeof (TabablzControl), new PropertyMetadata(default(DataTemplateSelector)));
-
- public DataTemplateSelector HeaderPrefixContentTemplateSelector
- {
- get { return (DataTemplateSelector) GetValue(HeaderPrefixContentTemplateSelectorProperty); }
- set { SetValue(HeaderPrefixContentTemplateSelectorProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderSuffixContentProperty = DependencyProperty.Register(
- "HeaderSuffixContent", typeof(object), typeof(TabablzControl), new PropertyMetadata(default(object)));
-
- public object HeaderSuffixContent
- {
- get { return (object)GetValue(HeaderSuffixContentProperty); }
- set { SetValue(HeaderSuffixContentProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderSuffixContentStringFormatProperty = DependencyProperty.Register(
- "HeaderSuffixContentStringFormat", typeof(string), typeof(TabablzControl), new PropertyMetadata(default(string)));
-
- public string HeaderSuffixContentStringFormat
- {
- get { return (string)GetValue(HeaderSuffixContentStringFormatProperty); }
- set { SetValue(HeaderSuffixContentStringFormatProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderSuffixContentTemplateProperty = DependencyProperty.Register(
- "HeaderSuffixContentTemplate", typeof(DataTemplate), typeof(TabablzControl), new PropertyMetadata(default(DataTemplate)));
-
- public DataTemplate HeaderSuffixContentTemplate
- {
- get { return (DataTemplate)GetValue(HeaderSuffixContentTemplateProperty); }
- set { SetValue(HeaderSuffixContentTemplateProperty, value); }
- }
-
- public static readonly DependencyProperty HeaderSuffixContentTemplateSelectorProperty = DependencyProperty.Register(
- "HeaderSuffixContentTemplateSelector", typeof(DataTemplateSelector), typeof(TabablzControl), new PropertyMetadata(default(DataTemplateSelector)));
-
- public DataTemplateSelector HeaderSuffixContentTemplateSelector
- {
- get { return (DataTemplateSelector)GetValue(HeaderSuffixContentTemplateSelectorProperty); }
- set { SetValue(HeaderSuffixContentTemplateSelectorProperty, value); }
- }
-
- public static readonly DependencyProperty ShowDefaultCloseButtonProperty = DependencyProperty.Register(
- "ShowDefaultCloseButton", typeof (bool), typeof (TabablzControl), new PropertyMetadata(default(bool)));
-
- ///
- /// Indicates whether a default close button should be displayed. If manually templating the tab header content the close command
- /// can be called by executing the command (typically via a ).
- ///
- public bool ShowDefaultCloseButton
- {
- get { return (bool) GetValue(ShowDefaultCloseButtonProperty); }
- set { SetValue(ShowDefaultCloseButtonProperty, value); }
- }
-
- public static readonly DependencyProperty ShowDefaultAddButtonProperty = DependencyProperty.Register(
- "ShowDefaultAddButton", typeof (bool), typeof (TabablzControl), new PropertyMetadata(default(bool)));
-
- ///
- /// Indicates whether a default add button should be displayed. Alternately an add button
- /// could be added in or , utilising
- /// .
- ///
- public bool ShowDefaultAddButton
- {
- get { return (bool) GetValue(ShowDefaultAddButtonProperty); }
- set { SetValue(ShowDefaultAddButtonProperty, value); }
- }
-
- public static readonly DependencyProperty IsHeaderPanelVisibleProperty = DependencyProperty.Register(
- "IsHeaderPanelVisible", typeof(bool), typeof(TabablzControl), new PropertyMetadata(true));
-
- ///
- /// Indicates wither the heaeder panel is visible. Default is true .
- ///
- public bool IsHeaderPanelVisible
- {
- get { return (bool)GetValue(IsHeaderPanelVisibleProperty); }
- set { SetValue(IsHeaderPanelVisibleProperty, value); }
- }
-
- public static readonly DependencyProperty AddLocationHintProperty = DependencyProperty.Register(
- "AddLocationHint", typeof (AddLocationHint), typeof (TabablzControl), new PropertyMetadata(AddLocationHint.Last));
-
- ///
- /// Gets or sets the location to add new tab items in the header.
- ///
- ///
- /// The logical order of the header items might not add match the content of the source items,
- /// so this property allows control of where new items should appear.
- ///
- public AddLocationHint AddLocationHint
- {
- get { return (AddLocationHint) GetValue(AddLocationHintProperty); }
- set { SetValue(AddLocationHintProperty, value); }
- }
-
- public static readonly DependencyProperty FixedHeaderCountProperty = DependencyProperty.Register(
- "FixedHeaderCount", typeof (int), typeof (TabablzControl), new PropertyMetadata(default(int)));
-
- ///
- /// Allows a the first adjacent tabs to be fixed (no dragging, and default close button will not show).
- ///
- public int FixedHeaderCount
- {
- get { return (int) GetValue(FixedHeaderCountProperty); }
- set { SetValue(FixedHeaderCountProperty, value); }
- }
-
- public static readonly DependencyProperty InterTabControllerProperty = DependencyProperty.Register(
- "InterTabController", typeof (InterTabController), typeof (TabablzControl), new PropertyMetadata(null, InterTabControllerPropertyChangedCallback));
-
- private static void InterTabControllerPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
- {
- var instance = (TabablzControl)dependencyObject;
- if (dependencyPropertyChangedEventArgs.OldValue != null)
- instance.RemoveLogicalChild(dependencyPropertyChangedEventArgs.OldValue);
- if (dependencyPropertyChangedEventArgs.NewValue != null)
- instance.AddLogicalChild(dependencyPropertyChangedEventArgs.NewValue);
- }
-
- ///
- /// An must be provided to enable tab tearing. Behaviour customisations can be applied
- /// vie the controller.
- ///
- public InterTabController InterTabController
- {
- get { return (InterTabController) GetValue(InterTabControllerProperty); }
- set { SetValue(InterTabControllerProperty, value); }
- }
-
- ///
- /// Allows a factory to be provided for generating new items. Typically used in conjunction with .
- ///
- public static readonly DependencyProperty NewItemFactoryProperty = DependencyProperty.Register(
- "NewItemFactory", typeof (Func), typeof (TabablzControl), new PropertyMetadata(default(Func)));
-
- ///
- /// Allows a factory to be provided for generating new items. Typically used in conjunction with .
- ///
- public Func NewItemFactory
- {
- get { return (Func) GetValue(NewItemFactoryProperty); }
- set { SetValue(NewItemFactoryProperty, value); }
- }
-
- private static readonly DependencyPropertyKey IsEmptyPropertyKey =
- DependencyProperty.RegisterReadOnly(
- "IsEmpty", typeof (bool), typeof (TabablzControl),
- new PropertyMetadata(true, OnIsEmptyChanged));
-
- ///
- /// Indicates if there are no current tab items.
- ///
- public static readonly DependencyProperty IsEmptyProperty =
- IsEmptyPropertyKey.DependencyProperty;
-
- ///
- /// Indicates if there are no current tab items.
- ///
- public bool IsEmpty
- {
- get { return (bool) GetValue(IsEmptyProperty); }
- private set { SetValue(IsEmptyPropertyKey, value); }
- }
-
- ///
- /// Raised when changes.
- ///
- public static readonly RoutedEvent IsEmptyChangedEvent =
- EventManager.RegisterRoutedEvent(
- "IsEmptyChanged",
- RoutingStrategy.Bubble,
- typeof (RoutedPropertyChangedEventHandler),
- typeof (TabablzControl));
-
- ///
- /// Event handler to list to .
- ///
- public event RoutedPropertyChangedEventHandler IsEmptyChanged
- {
- add { AddHandler(IsEmptyChangedEvent, value); }
- remove { RemoveHandler(IsEmptyChangedEvent, value); }
- }
-
- private static void OnIsEmptyChanged(
- DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var instance = d as TabablzControl;
- var args = new RoutedPropertyChangedEventArgs(
- (bool) e.OldValue,
- (bool) e.NewValue) {RoutedEvent = IsEmptyChangedEvent};
- instance?.RaiseEvent(args);
- }
-
- ///
- /// Optionally allows a close item hook to be bound in. If this propety is provided, the func must return true for the close to continue.
- ///
- public static readonly DependencyProperty ClosingItemCallbackProperty = DependencyProperty.Register(
- "ClosingItemCallback", typeof(ItemActionCallback), typeof(TabablzControl), new PropertyMetadata(default(ItemActionCallback)));
-
- ///
- /// Optionally allows a close item hook to be bound in. If this propety is provided, the func must return true for the close to continue.
- ///
- public ItemActionCallback ClosingItemCallback
- {
- get { return (ItemActionCallback)GetValue(ClosingItemCallbackProperty); }
- set { SetValue(ClosingItemCallbackProperty, value); }
- }
-
- ///
- /// Set to true to have tabs automatically be moved to another tab is a window is closed, so that they arent lost.
- /// Can be useful for fixed/persistant tabs that may have been dragged into another Window. You can further control
- /// this behaviour on a per tab item basis by providing .
- ///
- public static readonly DependencyProperty ConsolidateOrphanedItemsProperty = DependencyProperty.Register(
- "ConsolidateOrphanedItems", typeof (bool), typeof (TabablzControl), new PropertyMetadata(default(bool)));
-
- ///
- /// Set to true to have tabs automatically be moved to another tab is a window is closed, so that they arent lost.
- /// Can be useful for fixed/persistant tabs that may have been dragged into another Window. You can further control
- /// this behaviour on a per tab item basis by providing .
- ///
- public bool ConsolidateOrphanedItems
- {
- get { return (bool) GetValue(ConsolidateOrphanedItemsProperty); }
- set { SetValue(ConsolidateOrphanedItemsProperty, value); }
- }
-
- ///
- /// Assuming is set to true , consolidation of individual
- /// tab items can be cancelled by providing this call back and cancelling the
- /// instance.
- ///
- public static readonly DependencyProperty ConsolidatingOrphanedItemCallbackProperty = DependencyProperty.Register(
- "ConsolidatingOrphanedItemCallback", typeof (ItemActionCallback), typeof (TabablzControl), new PropertyMetadata(default(ItemActionCallback)));
-
- ///
- /// Assuming is set to true , consolidation of individual
- /// tab items can be cancelled by providing this call back and cancelling the
- /// instance.
- ///
- public ItemActionCallback ConsolidatingOrphanedItemCallback
- {
- get { return (ItemActionCallback) GetValue(ConsolidatingOrphanedItemCallbackProperty); }
- set { SetValue(ConsolidatingOrphanedItemCallbackProperty, value); }
- }
-
-
-
- private static readonly DependencyPropertyKey IsDraggingWindowPropertyKey =
- DependencyProperty.RegisterReadOnly(
- "IsDraggingWindow", typeof (bool), typeof (TabablzControl),
- new PropertyMetadata(default(bool), OnIsDraggingWindowChanged));
-
- ///
- /// Readonly dependency property which indicates whether the owning
- /// is currently dragged
- ///
- public static readonly DependencyProperty IsDraggingWindowProperty =
- IsDraggingWindowPropertyKey.DependencyProperty;
-
- ///
- /// Readonly dependency property which indicates whether the owning
- /// is currently dragged
- ///
- public bool IsDraggingWindow
- {
- get { return (bool) GetValue(IsDraggingWindowProperty); }
- private set { SetValue(IsDraggingWindowPropertyKey, value); }
- }
-
- ///
- /// Event indicating has changed.
- ///
- public static readonly RoutedEvent IsDraggingWindowChangedEvent =
- EventManager.RegisterRoutedEvent(
- "IsDraggingWindowChanged",
- RoutingStrategy.Bubble,
- typeof (RoutedPropertyChangedEventHandler),
- typeof (TabablzControl));
-
- ///
- /// Event indicating has changed.
- ///
- public event RoutedPropertyChangedEventHandler IsDraggingWindowChanged
- {
- add { AddHandler(IsDraggingWindowChangedEvent, value); }
- remove { RemoveHandler(IsDraggingWindowChangedEvent, value); }
- }
-
- private static void OnIsDraggingWindowChanged(
- DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var instance = (TabablzControl) d;
- var args = new RoutedPropertyChangedEventArgs(
- (bool) e.OldValue,
- (bool) e.NewValue)
- {
- RoutedEvent = IsDraggingWindowChangedEvent
- };
- instance.RaiseEvent(args);
-
- }
-
- ///
- /// Temporarily set by the framework if a users drag opration causes a Window to close (e.g if a tab is dragging into another tab).
- ///
- public static readonly DependencyProperty IsClosingAsPartOfDragOperationProperty = DependencyProperty.RegisterAttached(
- "IsClosingAsPartOfDragOperation", typeof (bool), typeof (TabablzControl), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.NotDataBindable));
-
- internal static void SetIsClosingAsPartOfDragOperation(Window element, bool value)
- {
- element.SetValue(IsClosingAsPartOfDragOperationProperty, value);
- }
-
- ///
- /// Helper method which can tell you if a is being automatically closed due
- /// to a user instigated drag operation (typically when a single tab is dropped into another window.
- ///
- ///
- ///
- public static bool GetIsClosingAsPartOfDragOperation(Window element)
- {
- return (bool) element.GetValue(IsClosingAsPartOfDragOperationProperty);
- }
-
- ///
- /// Provide a hint for how the header should size itself if there are no tabs left (and a Window is still open).
- ///
- public static readonly DependencyProperty EmptyHeaderSizingHintProperty = DependencyProperty.Register(
- "EmptyHeaderSizingHint", typeof (EmptyHeaderSizingHint), typeof (TabablzControl), new PropertyMetadata(default(EmptyHeaderSizingHint)));
-
- ///
- /// Provide a hint for how the header should size itself if there are no tabs left (and a Window is still open).
- ///
- public EmptyHeaderSizingHint EmptyHeaderSizingHint
- {
- get { return (EmptyHeaderSizingHint) GetValue(EmptyHeaderSizingHintProperty); }
- set { SetValue(EmptyHeaderSizingHintProperty, value); }
- }
-
- public static readonly DependencyProperty IsWrappingTabItemProperty = DependencyProperty.RegisterAttached(
- "IsWrappingTabItem", typeof (bool), typeof (TabablzControl), new PropertyMetadata(default(bool)));
-
- internal static void SetIsWrappingTabItem(DependencyObject element, bool value)
- {
- element.SetValue(IsWrappingTabItemProperty, value);
- }
-
- public static bool GetIsWrappingTabItem(DependencyObject element)
- {
- return (bool) element.GetValue(IsWrappingTabItemProperty);
- }
-
- ///
- /// Adds an item to the source collection. If the InterTabController.InterTabClient is set that instance will be deferred to.
- /// Otherwise an attempt will be made to add to the property, and lastly .
- ///
- ///
- public void AddToSource(object item)
- {
- if (item == null) throw new ArgumentNullException("item");
-
- var manualInterTabClient = InterTabController == null ? null : InterTabController.InterTabClient as IManualInterTabClient;
- if (manualInterTabClient != null)
- {
- manualInterTabClient.Add(item);
- }
- else
- {
- CollectionTeaser collectionTeaser;
- if (CollectionTeaser.TryCreate(ItemsSource, out collectionTeaser))
- collectionTeaser.Add(item);
- else
- Items.Add(item);
- }
- }
-
- ///
- /// Removes an item from the source collection. If the InterTabController.InterTabClient is set that instance will be deferred to.
- /// Otherwise an attempt will be made to remove from the property, and lastly .
- ///
- ///
- public void RemoveFromSource(object item)
- {
- if (item == null) throw new ArgumentNullException("item");
-
- var manualInterTabClient = InterTabController == null ? null : InterTabController.InterTabClient as IManualInterTabClient;
- if (manualInterTabClient != null)
- {
- manualInterTabClient.Remove(item);
- }
- else
- {
- CollectionTeaser collectionTeaser;
- if (CollectionTeaser.TryCreate(ItemsSource, out collectionTeaser))
- collectionTeaser.Remove(item);
- else
- Items.Remove(item);
- }
- }
-
- ///
- /// Gets the header items, ordered according to their current visual position in the tab header.
- ///
- ///
- public IEnumerable GetOrderedHeaders()
- {
- return _dragablzItemsControl.ItemsOrganiser.Sort(_dragablzItemsControl.DragablzItems());
- }
-
- ///
- /// Called when is called.
- ///
- public override void OnApplyTemplate()
- {
- _templateSubscription?.Dispose();
- _templateSubscription = Disposable.Empty;
-
- _dragablzItemsControl = GetTemplateChild(HeaderItemsControlPartName) as DragablzItemsControl;
- if (_dragablzItemsControl != null)
- {
- _dragablzItemsControl.ItemContainerGenerator.StatusChanged += ItemContainerGeneratorOnStatusChanged;
- _templateSubscription =
- Disposable.Create(
- () =>
- _dragablzItemsControl.ItemContainerGenerator.StatusChanged -=
- ItemContainerGeneratorOnStatusChanged);
-
- _dragablzItemsControl.ContainerCustomisations = new ContainerCustomisations(null, PrepareChildContainerForItemOverride);
- }
-
- if (SelectedItem == null)
- SetCurrentValue(SelectedItemProperty, Items.OfType().FirstOrDefault());
-
- _itemsHolder = GetTemplateChild(ItemsHolderPartName) as Panel;
- UpdateSelectedItem();
- MarkWrappedTabItems();
- MarkInitialSelection();
-
- base.OnApplyTemplate();
- }
-
- ///
- /// update the visible child in the ItemsHolder
- ///
- ///
- protected override void OnSelectionChanged(SelectionChangedEventArgs e)
- {
- if (e.RemovedItems.Count > 0 && e.AddedItems.Count > 0)
- _previousSelection = new WeakReference(e.RemovedItems[0]);
-
- base.OnSelectionChanged(e);
- UpdateSelectedItem();
-
- if (_dragablzItemsControl == null) return;
-
- Func> notTabItems =
- l =>
- l.Cast()
- .Where(o => !(o is TabItem))
- .Select(o => _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(o))
- .OfType();
- foreach (var addedItem in notTabItems(e.AddedItems))
- {
- addedItem.IsSelected = true;
- addedItem.BringIntoView();
- }
- foreach (var removedItem in notTabItems(e.RemovedItems))
- {
- removedItem.IsSelected = false;
- }
-
- foreach (var tabItem in e.AddedItems.OfType().Select(t => _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(t)).OfType())
- {
- tabItem.IsSelected = true;
- tabItem.BringIntoView();
- }
- foreach (var tabItem in e.RemovedItems.OfType().Select(t => _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(t)).OfType())
- {
- tabItem.IsSelected = false;
- }
- }
-
- ///
- /// 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 (_itemsHolder == null)
- {
- return;
- }
-
- switch (e.Action)
- {
- case NotifyCollectionChangedAction.Reset:
- _itemsHolder.Children.Clear();
-
- if (Items.Count > 0)
- {
- SelectedItem = base.Items[0];
- UpdateSelectedItem();
- }
-
- break;
-
- case NotifyCollectionChangedAction.Add:
- UpdateSelectedItem();
- if (e.NewItems.Count == 1 && Items.Count > 1 && _dragablzItemsControl != null && _interTabTransfer == null)
- _dragablzItemsControl.MoveItem(new MoveItemRequest(e.NewItems[0], SelectedItem, AddLocationHint));
-
- break;
-
- case NotifyCollectionChangedAction.Remove:
- foreach (var item in e.OldItems)
- {
- var cp = FindChildContentPresenter(item);
- if (cp != null)
- _itemsHolder.Children.Remove(cp);
- }
-
- if (SelectedItem == null)
- RestorePreviousSelection();
- UpdateSelectedItem();
- break;
-
- case NotifyCollectionChangedAction.Replace:
- throw new NotImplementedException("Replace not implemented yet");
- }
-
- IsEmpty = Items.Count == 0;
- }
-
- ///
- /// Provides class handling for the routed event that occurs when the user presses a key.
- ///
- /// Provides data for .
- protected override void OnKeyDown(KeyEventArgs e)
- {
- var sortedDragablzItems = _dragablzItemsControl.ItemsOrganiser.Sort(_dragablzItemsControl.DragablzItems()).ToList();
- DragablzItem selectDragablzItem = null;
- switch (e.Key)
- {
- case Key.Tab:
- if (SelectedItem == null)
- {
- selectDragablzItem = sortedDragablzItems.FirstOrDefault();
- break;
- }
-
- if ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
- {
- var selectedDragablzItem = (DragablzItem)_dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(SelectedItem);
- var selectedDragablzItemIndex = sortedDragablzItems.IndexOf(selectedDragablzItem);
- var direction = ((e.KeyboardDevice.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
- ? -1 : 1;
- var newIndex = selectedDragablzItemIndex + direction;
- if (newIndex < 0) newIndex = sortedDragablzItems.Count - 1;
- else if (newIndex == sortedDragablzItems.Count) newIndex = 0;
-
- selectDragablzItem = sortedDragablzItems[newIndex];
- }
- break;
- case Key.Home:
- selectDragablzItem = sortedDragablzItems.FirstOrDefault();
- break;
- case Key.End:
- selectDragablzItem = sortedDragablzItems.LastOrDefault();
- break;
- }
-
- if (selectDragablzItem != null)
- {
- var item = _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(selectDragablzItem);
- SetCurrentValue(SelectedItemProperty, item);
- e.Handled = true;
- }
-
- if (!e.Handled)
- base.OnKeyDown(e);
- }
-
- ///
- /// Provides an appropriate automation peer implementation for this control
- /// as part of the WPF automation infrastructure.
- ///
- /// The type-specific System.Windows.Automation.Peers.AutomationPeer implementation.
- protected override AutomationPeer OnCreateAutomationPeer()
- {
- return new FrameworkElementAutomationPeer(this);
- }
-
- internal static TabablzControl GetOwnerOfHeaderItems(DragablzItemsControl itemsControl)
- {
- return LoadedInstances.FirstOrDefault(t => Equals(t._dragablzItemsControl, itemsControl));
- }
-
- private static void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
- {
- var tabablzControl = (TabablzControl) sender;
- if (tabablzControl.IsVisible)
- VisibleInstances.Add(tabablzControl);
- else if (VisibleInstances.Contains(tabablzControl))
- VisibleInstances.Remove(tabablzControl);
- }
-
- private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
- {
- LoadedInstances.Add(this);
- var window = Window.GetWindow(this);
- if (window == null) return;
- window.Closing += WindowOnClosing;
- _windowSubscription.Disposable = Disposable.Create(() => window.Closing -= WindowOnClosing);
- }
-
- private void WindowOnClosing(object sender, CancelEventArgs cancelEventArgs)
- {
- _windowSubscription.Disposable = Disposable.Empty;
- if (!ConsolidateOrphanedItems || InterTabController == null) return;
-
- var window = (Window)sender;
-
- var orphanedItems = _dragablzItemsControl.DragablzItems();
- if (ConsolidatingOrphanedItemCallback != null)
- {
- orphanedItems =
- orphanedItems.Where(
- di =>
- {
- var args = new ItemActionCallbackArgs(window, this, di);
- ConsolidatingOrphanedItemCallback(args);
- return !args.IsCancelled;
- }).ToList();
- }
-
- var target =
- LoadedInstances.Except(this)
- .FirstOrDefault(
- other =>
- other.InterTabController != null &&
- other.InterTabController.Partition == InterTabController.Partition);
- if (target == null) return;
-
- foreach (var item in orphanedItems.Select(orphanedItem => _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(orphanedItem)))
- {
- RemoveFromSource(item);
- target.AddToSource(item);
- }
- }
-
- private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
- {
- _windowSubscription.Disposable = Disposable.Empty;
- LoadedInstances.Remove(this);
- }
-
- private void MarkWrappedTabItems()
- {
- if (_dragablzItemsControl == null) return;
-
- foreach (var pair in _dragablzItemsControl.Items.OfType().Select(tabItem =>
- new
- {
- tabItem,
- dragablzItem = _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(tabItem) as DragablzItem
- }).Where(a => a.dragablzItem != null))
- {
- var toolTipBinding = new Binding("ToolTip") { Source = pair.tabItem };
- BindingOperations.SetBinding(pair.dragablzItem, ToolTipProperty, toolTipBinding);
- SetIsWrappingTabItem(pair.dragablzItem, true);
- }
- }
-
- private void MarkInitialSelection()
- {
- if (_dragablzItemsControl == null ||
- _dragablzItemsControl.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) return;
-
- if (_dragablzItemsControl == null || SelectedItem == null) return;
-
- var tabItem = SelectedItem as TabItem;
- tabItem?.SetCurrentValue(IsSelectedProperty, true);
-
- var containerFromItem =
- _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(SelectedItem) as DragablzItem;
-
- containerFromItem?.SetCurrentValue(DragablzItem.IsSelectedProperty, true);
- }
-
- private void ItemDragStarted(object sender, DragablzDragStartedEventArgs e)
- {
- if (!IsMyItem(e.DragablzItem)) return;
-
- //the thumb may steal the user selection, so we will try and apply it manually
- if (_dragablzItemsControl == null) return;
-
- e.DragablzItem.IsDropTargetFound = false;
-
- var sourceOfDragItemsControl = ItemsControlFromItemContainer(e.DragablzItem) as DragablzItemsControl;
- if (sourceOfDragItemsControl == null || !Equals(sourceOfDragItemsControl, _dragablzItemsControl)) return;
-
- var itemsControlOffset = Mouse.GetPosition(_dragablzItemsControl);
- _tabHeaderDragStartInformation = new TabHeaderDragStartInformation(e.DragablzItem, itemsControlOffset.X,
- itemsControlOffset.Y, e.DragStartedEventArgs.HorizontalOffset, e.DragStartedEventArgs.VerticalOffset);
-
- foreach (var otherItem in _dragablzItemsControl.Containers().Except(e.DragablzItem))
- otherItem.IsSelected = false;
- e.DragablzItem.IsSelected = true;
- e.DragablzItem.PartitionAtDragStart = InterTabController?.Partition;
- var item = _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(e.DragablzItem);
- var tabItem = item as TabItem;
- if (tabItem != null)
- tabItem.IsSelected = true;
- SelectedItem = item;
-
- if (ShouldDragWindow(sourceOfDragItemsControl))
- IsDraggingWindow = true;
- }
-
- private bool ShouldDragWindow(DragablzItemsControl sourceOfDragItemsControl)
- {
- return (Items.Count == 1
- && (InterTabController == null || InterTabController.MoveWindowWithSolitaryTabs)
- && !Layout.IsContainedWithinBranch(sourceOfDragItemsControl));
- }
-
- private void PreviewItemDragDelta(object sender, DragablzDragDeltaEventArgs e)
- {
- if (_dragablzItemsControl == null) return;
-
- var sourceOfDragItemsControl = ItemsControlFromItemContainer(e.DragablzItem) as DragablzItemsControl;
- if (sourceOfDragItemsControl == null || !Equals(sourceOfDragItemsControl, _dragablzItemsControl)) return;
-
- if (!ShouldDragWindow(sourceOfDragItemsControl)) return;
-
- if (MonitorReentry(e)) return;
-
- var myWindow = Window.GetWindow(this);
- if (myWindow == null) return;
-
- if (_interTabTransfer != null)
- {
- var cursorPos = Native.GetCursorPos().ToWpf();
- if (_interTabTransfer.BreachOrientation == Orientation.Vertical)
- {
- var vector = cursorPos - _interTabTransfer.DragStartWindowOffset;
- myWindow.Left = vector.X;
- myWindow.Top = vector.Y;
- }
- else
- {
- var offset = e.DragablzItem.TranslatePoint(_interTabTransfer.OriginatorContainer.MouseAtDragStart, myWindow);
- var borderVector = myWindow.PointToScreen(new Point()).ToWpf() - new Point(myWindow.Left, myWindow.Top);
- offset.Offset(borderVector.X, borderVector.Y);
- myWindow.Left = cursorPos.X - offset.X;
- myWindow.Top = cursorPos.Y - offset.Y;
- }
- }
- else
- {
- myWindow.Left += e.DragDeltaEventArgs.HorizontalChange;
- myWindow.Top += e.DragDeltaEventArgs.VerticalChange;
- }
-
- e.Handled = true;
- }
-
- private bool MonitorReentry(DragablzDragDeltaEventArgs e)
- {
- var screenMousePosition = _dragablzItemsControl.PointToScreen(Mouse.GetPosition(_dragablzItemsControl));
-
- var sourceTabablzControl = (TabablzControl) e.Source;
- if (sourceTabablzControl.Items.Count > 1 && e.DragablzItem.LogicalIndex < sourceTabablzControl.FixedHeaderCount)
- {
- return false;
- }
-
- var otherTabablzControls = LoadedInstances
- .Where(
- tc =>
- tc != this && tc.InterTabController != null && InterTabController != null
- && Equals(tc.InterTabController.Partition, InterTabController.Partition)
- && tc._dragablzItemsControl != null)
- .Select(tc =>
- {
- var topLeft = tc._dragablzItemsControl.PointToScreen(new Point());
- var lastFixedItem = tc._dragablzItemsControl.DragablzItems()
- .OrderBy(di=> di.LogicalIndex)
- .Take(tc._dragablzItemsControl.FixedItemCount)
- .LastOrDefault();
- //TODO work this for vert tabs
- if (lastFixedItem != null)
- topLeft.Offset(lastFixedItem.X + lastFixedItem.ActualWidth, 0);
- var bottomRight =
- tc._dragablzItemsControl.PointToScreen(new Point(tc._dragablzItemsControl.ActualWidth,
- tc._dragablzItemsControl.ActualHeight));
-
- return new {tc, topLeft, bottomRight};
- });
-
-
- var target = Native.SortWindowsTopToBottom(Application.Current.Windows.OfType())
- .Join(otherTabablzControls, w => w, a => Window.GetWindow(a.tc), (w, a) => a)
- .FirstOrDefault(a => new Rect(a.topLeft, a.bottomRight).Contains(screenMousePosition));
-
- if (target == null) return false;
-
- var mousePositionOnItem = Mouse.GetPosition(e.DragablzItem);
-
- var floatingItemSnapShots = this.VisualTreeDepthFirstTraversal()
- .OfType()
- .SelectMany(l => l.FloatingDragablzItems().Select(FloatingItemSnapShot.Take))
- .ToList();
-
- e.DragablzItem.IsDropTargetFound = true;
- var item = RemoveItem(e.DragablzItem);
-
- var interTabTransfer = new InterTabTransfer(item, e.DragablzItem, mousePositionOnItem, floatingItemSnapShots);
- e.DragablzItem.IsDragging = false;
-
- target.tc.ReceiveDrag(interTabTransfer);
- e.Cancel = true;
-
- return true;
- }
-
- internal object RemoveItem(DragablzItem dragablzItem)
- {
- var item = _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(dragablzItem);
-
- //stop the header shrinking if the tab stays open when empty
- var minSize = EmptyHeaderSizingHint == EmptyHeaderSizingHint.PreviousTab
- ? new Size(_dragablzItemsControl.ActualWidth, _dragablzItemsControl.ActualHeight)
- : new Size();
-
- _dragablzItemsControl.MinHeight = 0;
- _dragablzItemsControl.MinWidth = 0;
-
- var contentPresenter = FindChildContentPresenter(item);
- RemoveFromSource(item);
- _itemsHolder.Children.Remove(contentPresenter);
-
- if (Items.Count != 0) return item;
-
- var window = Window.GetWindow(this);
- if (window != null
- && InterTabController != null
- && InterTabController.InterTabClient.TabEmptiedHandler(this, window) == TabEmptiedResponse.CloseWindowOrLayoutBranch)
- {
- if (Layout.ConsolidateBranch(this)) return item;
-
- try
- {
- SetIsClosingAsPartOfDragOperation(window, true);
- window.Close();
- }
- finally
- {
- SetIsClosingAsPartOfDragOperation(window, false);
- }
- }
- else
- {
- _dragablzItemsControl.MinHeight = minSize.Height;
- _dragablzItemsControl.MinWidth = minSize.Width;
- }
- return item;
- }
-
- private void ItemDragCompleted(object sender, DragablzDragCompletedEventArgs e)
- {
- if (!IsMyItem(e.DragablzItem)) return;
-
- _interTabTransfer = null;
- _dragablzItemsControl.LockedMeasure = null;
- IsDraggingWindow = false;
- }
-
- private void ItemDragDelta(object sender, DragablzDragDeltaEventArgs e)
- {
- if (!IsMyItem(e.DragablzItem)) return;
- if (FixedHeaderCount > 0 &&
- _dragablzItemsControl.ItemsOrganiser.Sort(_dragablzItemsControl.DragablzItems())
- .Take(FixedHeaderCount)
- .Contains(e.DragablzItem))
- return;
-
- if (_tabHeaderDragStartInformation == null ||
- !Equals(_tabHeaderDragStartInformation.DragItem, e.DragablzItem) || InterTabController == null) return;
-
- if (InterTabController.InterTabClient == null)
- throw new InvalidOperationException("An InterTabClient must be provided on an InterTabController.");
-
- MonitorBreach(e);
- }
-
- private bool IsMyItem(DragablzItem item)
- {
- return _dragablzItemsControl != null && _dragablzItemsControl.DragablzItems().Contains(item);
- }
-
- private void MonitorBreach(DragablzDragDeltaEventArgs e)
- {
- var mousePositionOnHeaderItemsControl = Mouse.GetPosition(_dragablzItemsControl);
-
- Orientation? breachOrientation = null;
- if (mousePositionOnHeaderItemsControl.X < -InterTabController.HorizontalPopoutGrace
- || (mousePositionOnHeaderItemsControl.X - _dragablzItemsControl.ActualWidth) > InterTabController.HorizontalPopoutGrace)
- breachOrientation = Orientation.Horizontal;
- else if (mousePositionOnHeaderItemsControl.Y < -InterTabController.VerticalPopoutGrace
- || (mousePositionOnHeaderItemsControl.Y - _dragablzItemsControl.ActualHeight) > InterTabController.VerticalPopoutGrace)
- breachOrientation = Orientation.Vertical;
-
- if (!breachOrientation.HasValue) return;
-
- var newTabHost = InterTabController.InterTabClient.GetNewHost(InterTabController.InterTabClient,
- InterTabController.Partition, this);
- if (newTabHost?.TabablzControl == null || newTabHost.Container == null)
- throw new ApplicationException("New tab host was not correctly provided");
-
- var item = _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(e.DragablzItem);
- var isTransposing = IsTransposing(newTabHost.TabablzControl);
-
- var myWindow = Window.GetWindow(this);
- if (myWindow == null) throw new ApplicationException("Unable to find owning window.");
- var dragStartWindowOffset = ConfigureNewHostSizeAndGetDragStartWindowOffset(myWindow, newTabHost, e.DragablzItem, isTransposing);
-
- var dragableItemHeaderPoint = e.DragablzItem.TranslatePoint(new Point(), _dragablzItemsControl);
- var dragableItemSize = new Size(e.DragablzItem.ActualWidth, e.DragablzItem.ActualHeight);
- var floatingItemSnapShots = this.VisualTreeDepthFirstTraversal()
- .OfType()
- .SelectMany(l => l.FloatingDragablzItems().Select(FloatingItemSnapShot.Take))
- .ToList();
-
- var interTabTransfer = new InterTabTransfer(item, e.DragablzItem, breachOrientation.Value, dragStartWindowOffset, e.DragablzItem.MouseAtDragStart, dragableItemHeaderPoint, dragableItemSize, floatingItemSnapShots, isTransposing);
-
- if (myWindow.WindowState == WindowState.Maximized)
- {
- var desktopMousePosition = Native.GetCursorPos().ToWpf();
- newTabHost.Container.Left = desktopMousePosition.X - dragStartWindowOffset.X;
- newTabHost.Container.Top = desktopMousePosition.Y - dragStartWindowOffset.Y;
- }
- else
- {
- newTabHost.Container.Left = myWindow.Left;
- newTabHost.Container.Top = myWindow.Top;
- }
- newTabHost.Container.Show();
- var contentPresenter = FindChildContentPresenter(item);
-
- //stop the header shrinking if the tab stays open when empty
- var minSize = EmptyHeaderSizingHint == EmptyHeaderSizingHint.PreviousTab
- ? new Size(_dragablzItemsControl.ActualWidth, _dragablzItemsControl.ActualHeight)
- : new Size();
- System.Diagnostics.Debug.WriteLine("B " + minSize);
-
- RemoveFromSource(item);
- _itemsHolder.Children.Remove(contentPresenter);
- if (Items.Count == 0)
- {
- _dragablzItemsControl.MinHeight = minSize.Height;
- _dragablzItemsControl.MinWidth = minSize.Width;
- Layout.ConsolidateBranch(this);
- }
-
- RestorePreviousSelection();
-
- foreach (var dragablzItem in _dragablzItemsControl.DragablzItems())
- {
- dragablzItem.IsDragging = false;
- dragablzItem.IsSiblingDragging = false;
- }
-
- newTabHost.TabablzControl.ReceiveDrag(interTabTransfer);
- interTabTransfer.OriginatorContainer.IsDropTargetFound = true;
- e.Cancel = true;
- }
-
- private bool IsTransposing(TabControl target)
- {
- return IsVertical(this) != IsVertical(target);
- }
-
- private static bool IsVertical(TabControl tabControl)
- {
- return tabControl.TabStripPlacement == Dock.Left
- || tabControl.TabStripPlacement == Dock.Right;
- }
-
- private void RestorePreviousSelection()
- {
- var previousSelection = _previousSelection?.Target;
- if (previousSelection != null && Items.Contains(previousSelection))
- SelectedItem = previousSelection;
- else
- SelectedItem = Items.OfType().FirstOrDefault();
- }
-
- private Point ConfigureNewHostSizeAndGetDragStartWindowOffset(Window currentWindow, INewTabHost newTabHost, DragablzItem dragablzItem, bool isTransposing)
- {
- var layout = this.VisualTreeAncestory().OfType().FirstOrDefault();
- Point dragStartWindowOffset;
- if (layout != null)
- {
- newTabHost.Container.Width = ActualWidth + Math.Max(0, currentWindow.RestoreBounds.Width - layout.ActualWidth);
- newTabHost.Container.Height = ActualHeight + Math.Max(0, currentWindow.RestoreBounds.Height - layout.ActualHeight);
- dragStartWindowOffset = dragablzItem.TranslatePoint(new Point(), this);
- //dragStartWindowOffset.Offset(currentWindow.RestoreBounds.Width - layout.ActualWidth, currentWindow.RestoreBounds.Height - layout.ActualHeight);
- }
- else
- {
- if (newTabHost.Container.GetType() == currentWindow.GetType())
- {
- newTabHost.Container.Width = currentWindow.RestoreBounds.Width;
- newTabHost.Container.Height = currentWindow.RestoreBounds.Height;
- dragStartWindowOffset = isTransposing ? new Point(dragablzItem.MouseAtDragStart.X, dragablzItem.MouseAtDragStart.Y) : dragablzItem.TranslatePoint(new Point(), currentWindow);
- }
- else
- {
- newTabHost.Container.Width = ActualWidth;
- newTabHost.Container.Height = ActualHeight;
- dragStartWindowOffset = isTransposing ? new Point() : dragablzItem.TranslatePoint(new Point(), this);
- dragStartWindowOffset.Offset(dragablzItem.MouseAtDragStart.X, dragablzItem.MouseAtDragStart.Y);
- return dragStartWindowOffset;
- }
- }
-
- dragStartWindowOffset.Offset(dragablzItem.MouseAtDragStart.X, dragablzItem.MouseAtDragStart.Y);
- var borderVector = currentWindow.PointToScreen(new Point()).ToWpf() - new Point(currentWindow.GetActualLeft(), currentWindow.GetActualTop());
- dragStartWindowOffset.Offset(borderVector.X, borderVector.Y);
- return dragStartWindowOffset;
- }
-
- internal void ReceiveDrag(InterTabTransfer interTabTransfer)
- {
- var myWindow = Window.GetWindow(this);
- if (myWindow == null) throw new ApplicationException("Unable to find owning window.");
- myWindow.Activate();
-
- _interTabTransfer = interTabTransfer;
-
- if (Items.Count == 0)
- {
- if (interTabTransfer.IsTransposing)
- _dragablzItemsControl.LockedMeasure = new Size(
- interTabTransfer.ItemSize.Width,
- interTabTransfer.ItemSize.Height);
- else
- _dragablzItemsControl.LockedMeasure = new Size(
- interTabTransfer.ItemPositionWithinHeader.X + interTabTransfer.ItemSize.Width,
- interTabTransfer.ItemPositionWithinHeader.Y + interTabTransfer.ItemSize.Height);
- }
-
- var lastFixedItem = _dragablzItemsControl.DragablzItems()
- .OrderBy(i => i.LogicalIndex)
- .Take(_dragablzItemsControl.FixedItemCount)
- .LastOrDefault();
-
- AddToSource(interTabTransfer.Item);
- SelectedItem = interTabTransfer.Item;
-
- Dispatcher.BeginInvoke(new Action(() => Layout.RestoreFloatingItemSnapShots(this, interTabTransfer.FloatingItemSnapShots)), DispatcherPriority.Loaded);
- _dragablzItemsControl.InstigateDrag(interTabTransfer.Item, newContainer =>
- {
- newContainer.PartitionAtDragStart = interTabTransfer.OriginatorContainer.PartitionAtDragStart;
- newContainer.IsDropTargetFound = true;
-
- if (interTabTransfer.TransferReason == InterTabTransferReason.Breach)
- {
- if (interTabTransfer.IsTransposing)
- {
- newContainer.Y = 0;
- newContainer.X = 0;
- }
- else
- {
- newContainer.Y = interTabTransfer.OriginatorContainer.Y;
- newContainer.X = interTabTransfer.OriginatorContainer.X;
- }
- }
- else
- {
- if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom)
- {
- var mouseXOnItemsControl = Native.GetCursorPos().X -
- _dragablzItemsControl.PointToScreen(new Point()).X;
- var newX = mouseXOnItemsControl - interTabTransfer.DragStartItemOffset.X;
- if (lastFixedItem != null)
- {
- newX = Math.Max(newX, lastFixedItem.X + lastFixedItem.ActualWidth);
- }
- newContainer.X = newX;
- newContainer.Y = 0;
- }
- else
- {
- var mouseYOnItemsControl = Native.GetCursorPos().Y -
- _dragablzItemsControl.PointToScreen(new Point()).Y;
- var newY = mouseYOnItemsControl - interTabTransfer.DragStartItemOffset.Y;
- if (lastFixedItem != null)
- {
- newY = Math.Max(newY, lastFixedItem.Y + lastFixedItem.ActualHeight);
- }
- newContainer.X = 0;
- newContainer.Y = newY;
- }
- }
- newContainer.MouseAtDragStart = interTabTransfer.DragStartItemOffset;
- });
- }
-
- ///
- /// generate a ContentPresenter for the selected item
- ///
- private void UpdateSelectedItem()
- {
- if (_itemsHolder == null)
- {
- return;
- }
-
- CreateChildContentPresenter(SelectedItem);
-
- // show the right child
- var selectedContent = GetContent(SelectedItem);
- foreach (ContentPresenter child in _itemsHolder.Children)
- {
- var isSelected = (child.Content == selectedContent);
- child.Visibility = isSelected ? Visibility.Visible : Visibility.Collapsed;
- child.IsEnabled = isSelected;
- }
- }
-
- private static object GetContent(object item)
- {
- return (item is TabItem) ? ((TabItem) item).Content : item;
- }
-
- ///
- /// create the child ContentPresenter for the given item (could be data or a TabItem)
- ///
- ///
- ///
- private void CreateChildContentPresenter(object item)
- {
- if (item == null) return;
-
- var cp = FindChildContentPresenter(item);
- if (cp != null) return;
-
- // the actual child to be added. cp.Tag is a reference to the TabItem
- cp = new ContentPresenter
- {
- Content = GetContent(item),
- ContentTemplate = ContentTemplate,
- ContentTemplateSelector = ContentTemplateSelector,
- ContentStringFormat = ContentStringFormat,
- Visibility = Visibility.Collapsed,
- };
- _itemsHolder.Children.Add(cp);
- }
-
- ///
- /// Find the CP for the given object. data could be a TabItem or a piece of data
- ///
- ///
- ///
- private ContentPresenter FindChildContentPresenter(object data)
- {
- if (data is TabItem)
- data = ((TabItem) data).Content;
-
- return data == null
- ? null
- : _itemsHolder?.Children.Cast().FirstOrDefault(cp => cp.Content == data);
- }
-
- private void ItemContainerGeneratorOnStatusChanged(object sender, EventArgs eventArgs)
- {
- MarkWrappedTabItems();
- MarkInitialSelection();
- }
-
- private static void CloseItem(DragablzItem item, TabablzControl owner)
- {
- if (item == null)
- throw new ApplicationException("Valid DragablzItem to close is required.");
-
- if (owner == null)
- throw new ApplicationException("Valid TabablzControl container is required.");
-
- if (!owner.IsMyItem(item))
- throw new ApplicationException("TabablzControl container must be an owner of the DragablzItem to close");
-
- var cancel = false;
- if (owner.ClosingItemCallback != null)
- {
- var callbackArgs = new ItemActionCallbackArgs(Window.GetWindow(owner), owner, item);
- owner.ClosingItemCallback(callbackArgs);
- cancel = callbackArgs.IsCancelled;
- }
-
- if (!cancel)
- owner.RemoveItem(item);
- }
-
- private static void CloseItemCanExecuteClassHandler(object sender, CanExecuteRoutedEventArgs e)
- {
- e.CanExecute = FindOwner(e.Parameter, e.OriginalSource) != null;
- }
- private static void CloseItemClassHandler(object sender, ExecutedRoutedEventArgs e)
- {
- var owner = FindOwner(e.Parameter, e.OriginalSource);
-
- if (owner == null) throw new ApplicationException("Unable to ascertain DragablzItem to close.");
-
- CloseItem(owner.Item1, owner.Item2);
- }
-
- private static Tuple FindOwner(object eventParameter, object eventOriginalSource)
- {
- var dragablzItem = eventParameter as DragablzItem;
- if (dragablzItem == null)
- {
- var dependencyObject = eventOriginalSource as DependencyObject;
- dragablzItem = dependencyObject.VisualTreeAncestory().OfType().FirstOrDefault();
- if (dragablzItem == null)
- {
- var popup = dependencyObject.LogicalTreeAncestory().OfType().LastOrDefault();
- if (popup?.PlacementTarget != null)
- {
- dragablzItem = popup.PlacementTarget.VisualTreeAncestory().OfType().FirstOrDefault();
- }
- }
- }
-
- if (dragablzItem == null) return null;
-
- var tabablzControl = LoadedInstances.FirstOrDefault(tc => tc.IsMyItem(dragablzItem));
-
- return tabablzControl == null ? null : new Tuple(dragablzItem, tabablzControl);
- }
-
- private void AddItemHandler(object sender, ExecutedRoutedEventArgs e)
- {
- if (NewItemFactory == null)
- throw new InvalidOperationException("NewItemFactory must be provided.");
-
- var newItem = NewItemFactory();
- if (newItem == null) throw new ApplicationException("NewItemFactory returned null.");
-
- AddToSource(newItem);
- SelectedItem = newItem;
-
- Dispatcher.BeginInvoke(new Action(_dragablzItemsControl.InvalidateMeasure), DispatcherPriority.Loaded);
- }
-
- private void PrepareChildContainerForItemOverride(DependencyObject dependencyObject, object o)
- {
- var dragablzItem = dependencyObject as DragablzItem;
- if (dragablzItem != null && HeaderMemberPath != null)
- {
- var contentBinding = new Binding(HeaderMemberPath) { Source = o };
- dragablzItem.SetBinding(ContentControl.ContentProperty, contentBinding);
- dragablzItem.UnderlyingContent = o;
- }
-
- SetIsWrappingTabItem(dependencyObject, o is TabItem);
- }
- }
-}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows;
+using System.Windows.Automation.Peers;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Threading;
+using Dragablz.Core;
+using Dragablz.Dockablz;
+using Dragablz.Referenceless;
+
+namespace Dragablz
+{
+ //original code specific to keeping visual tree "alive" sourced from http://stackoverflow.com/questions/12432062/binding-to-itemssource-of-tabcontrol-in-wpf
+
+ ///
+ /// Extended tab control which supports tab repositioning, and drag and drop. Also
+ /// uses the common WPF technique for pesisting the visual tree across tabs.
+ ///
+ [TemplatePart(Name = HeaderItemsControlPartName, Type = typeof(DragablzItemsControl))]
+ [TemplatePart(Name = ItemsHolderPartName, Type = typeof(Panel))]
+ public class TabablzControl : TabControl
+ {
+ ///
+ /// Template part.
+ ///
+ public const string HeaderItemsControlPartName = "PART_HeaderItemsControl";
+ ///
+ /// Template part.
+ ///
+ public const string ItemsHolderPartName = "PART_ItemsHolder";
+
+ ///
+ /// Routed command which can be used to close a tab.
+ ///
+ public static RoutedCommand CloseItemCommand = new RoutedUICommand("Close", "Close", typeof(TabablzControl));
+
+ ///
+ /// Routed command which can be used to add a new tab. See .
+ ///
+ public static RoutedCommand AddItemCommand = new RoutedUICommand("Add", "Add", typeof(TabablzControl));
+
+ private static readonly HashSet LoadedInstances = new HashSet();
+ private static readonly HashSet VisibleInstances = new HashSet();
+
+ private Panel _itemsHolder;
+ private TabHeaderDragStartInformation _tabHeaderDragStartInformation;
+ private WeakReference _previousSelection;
+ private DragablzItemsControl _dragablzItemsControl;
+ private IDisposable _templateSubscription;
+ private readonly SerialDisposable _windowSubscription = new SerialDisposable();
+
+ private InterTabTransfer _interTabTransfer;
+
+ static TabablzControl()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(TabablzControl), new FrameworkPropertyMetadata(typeof(TabablzControl)));
+ CommandManager.RegisterClassCommandBinding(typeof(FrameworkElement), new CommandBinding(CloseItemCommand, CloseItemClassHandler, CloseItemCanExecuteClassHandler));
+ }
+
+ ///
+ /// Default constructor.
+ ///
+ public TabablzControl()
+ {
+ AddHandler(DragablzItem.DragStarted, new DragablzDragStartedEventHandler(ItemDragStarted), true);
+ AddHandler(DragablzItem.PreviewDragDelta, new DragablzDragDeltaEventHandler(PreviewItemDragDelta), true);
+ AddHandler(DragablzItem.DragDelta, new DragablzDragDeltaEventHandler(ItemDragDelta), true);
+ AddHandler(DragablzItem.DragCompleted, new DragablzDragCompletedEventHandler(ItemDragCompleted), true);
+ CommandBindings.Add(new CommandBinding(AddItemCommand, AddItemHandler));
+
+ Loaded += OnLoaded;
+ Unloaded += OnUnloaded;
+ IsVisibleChanged += OnIsVisibleChanged;
+ }
+
+ public static readonly DependencyProperty CustomHeaderItemStyleProperty = DependencyProperty.Register(
+ "CustomHeaderItemStyle", typeof (Style), typeof (TabablzControl), new PropertyMetadata(default(Style)));
+
+ ///
+ /// Helper method which returns all the currently loaded instances.
+ ///
+ ///
+ public static IEnumerable GetLoadedInstances()
+ {
+ return LoadedInstances.Union(VisibleInstances).Distinct().ToList();
+ }
+
+ ///
+ /// Helper method to close all tabs where the item is the tab's content (helpful with MVVM scenarios)
+ ///
+ ///
+ /// In MVVM scenarios where you don't want to bind the routed command to your ViewModel,
+ /// with this helper method and embedding the TabablzControl in a UserControl, you can keep
+ /// the View-specific dependencies out of the ViewModel.
+ ///
+ /// An existing Tab item content (a ViewModel in MVVM scenarios) which is backing a tab control
+ public static void CloseItem(object tabContentItem)
+ {
+ if (tabContentItem == null) return; //Do nothing.
+
+ //Find all loaded TabablzControl instances with tabs backed by this item and close them
+ foreach(var tabWithItemContent in
+ GetLoadedInstances().SelectMany(tc =>
+ tc._dragablzItemsControl.DragablzItems().Where(di => di.Content.Equals(tabContentItem)).Select(di => new { tc, di })))
+ {
+ TabablzControl.CloseItem(tabWithItemContent.di, tabWithItemContent.tc);
+ }
+ }
+
+ ///
+ /// Helper method to add an item next to an existing item.
+ ///
+ ///
+ /// Due to the organisable nature of the control, the order of items may not reflect the order in the source collection. This method
+ /// will add items to the source collection, managing their initial appearance on screen at the same time.
+ /// If you are using a this will be used to add the item into the source collection.
+ ///
+ /// New item to add.
+ /// Existing object/tab item content which defines which tab control should be used to add the object.
+ /// Location, relative to the object
+ public static void AddItem(object item, object nearItem, AddLocationHint addLocationHint)
+ {
+ if (nearItem == null) throw new ArgumentNullException("nearItem");
+
+ var existingLocation = GetLoadedInstances().SelectMany(tabControl =>
+ (tabControl.ItemsSource ?? tabControl.Items).OfType()
+ .Select(existingObject => new {tabControl, existingObject}))
+ .SingleOrDefault(a => nearItem.Equals(a.existingObject));
+
+ if (existingLocation == null)
+ throw new ArgumentException("Did not find precisely one instance of adjacentTo", "nearItem");
+
+ existingLocation.tabControl.AddToSource(item);
+ if (existingLocation.tabControl._dragablzItemsControl != null)
+ existingLocation.tabControl._dragablzItemsControl.MoveItem(new MoveItemRequest(item, nearItem, addLocationHint));
+ }
+
+ ///
+ /// Finds and selects an item.
+ ///
+ ///
+ public static void SelectItem(object item)
+ {
+ var existingLocation = GetLoadedInstances().SelectMany(tabControl =>
+ (tabControl.ItemsSource ?? tabControl.Items).OfType()
+ .Select(existingObject => new {tabControl, existingObject}))
+ .FirstOrDefault(a => item.Equals(a.existingObject));
+
+ if (existingLocation == null) return;
+
+ existingLocation.tabControl.SelectedItem = item;
+ }
+
+ ///
+ /// Style to apply to header items which are not their own item container ( ). Typically items bound via the will use this style.
+ ///
+ [Obsolete]
+ public Style CustomHeaderItemStyle
+ {
+ get { return (Style) GetValue(CustomHeaderItemStyleProperty); }
+ set { SetValue(CustomHeaderItemStyleProperty, value); }
+ }
+
+ public static readonly DependencyProperty CustomHeaderItemTemplateProperty = DependencyProperty.Register(
+ "CustomHeaderItemTemplate", typeof (DataTemplate), typeof (TabablzControl), new PropertyMetadata(default(DataTemplate)));
+
+ [Obsolete("Prefer HeaderItemTemplate")]
+ public DataTemplate CustomHeaderItemTemplate
+ {
+ get { return (DataTemplate) GetValue(CustomHeaderItemTemplateProperty); }
+ set { SetValue(CustomHeaderItemTemplateProperty, value); }
+ }
+
+ public static readonly DependencyProperty DefaultHeaderItemStyleProperty = DependencyProperty.Register(
+ "DefaultHeaderItemStyle", typeof (Style), typeof (TabablzControl), new PropertyMetadata(default(Style)));
+
+ [Obsolete]
+ public Style DefaultHeaderItemStyle
+ {
+ get { return (Style) GetValue(DefaultHeaderItemStyleProperty); }
+ set { SetValue(DefaultHeaderItemStyleProperty, value); }
+ }
+
+ public static readonly DependencyProperty AdjacentHeaderItemOffsetProperty = DependencyProperty.Register(
+ "AdjacentHeaderItemOffset", typeof (double), typeof (TabablzControl), new PropertyMetadata(default(double), AdjacentHeaderItemOffsetPropertyChangedCallback));
+
+ private static void AdjacentHeaderItemOffsetPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
+ {
+ dependencyObject.SetValue(HeaderItemsOrganiserProperty, new HorizontalOrganiser((double)dependencyPropertyChangedEventArgs.NewValue));
+ }
+
+ public double AdjacentHeaderItemOffset
+ {
+ get { return (double) GetValue(AdjacentHeaderItemOffsetProperty); }
+ set { SetValue(AdjacentHeaderItemOffsetProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderItemsOrganiserProperty = DependencyProperty.Register(
+ "HeaderItemsOrganiser", typeof (IItemsOrganiser), typeof (TabablzControl), new PropertyMetadata(new HorizontalOrganiser()));
+
+ public IItemsOrganiser HeaderItemsOrganiser
+ {
+ get { return (IItemsOrganiser) GetValue(HeaderItemsOrganiserProperty); }
+ set { SetValue(HeaderItemsOrganiserProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderMemberPathProperty = DependencyProperty.Register(
+ "HeaderMemberPath", typeof (string), typeof (TabablzControl), new PropertyMetadata(default(string)));
+
+ public string HeaderMemberPath
+ {
+ get { return (string) GetValue(HeaderMemberPathProperty); }
+ set { SetValue(HeaderMemberPathProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderItemTemplateProperty = DependencyProperty.Register(
+ "HeaderItemTemplate", typeof (DataTemplate), typeof (TabablzControl), new PropertyMetadata(default(DataTemplate)));
+
+ public DataTemplate HeaderItemTemplate
+ {
+ get { return (DataTemplate) GetValue(HeaderItemTemplateProperty); }
+ set { SetValue(HeaderItemTemplateProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderPrefixContentProperty = DependencyProperty.Register(
+ "HeaderPrefixContent", typeof (object), typeof (TabablzControl), new PropertyMetadata(default(object)));
+
+ public object HeaderPrefixContent
+ {
+ get { return (object) GetValue(HeaderPrefixContentProperty); }
+ set { SetValue(HeaderPrefixContentProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderPrefixContentStringFormatProperty = DependencyProperty.Register(
+ "HeaderPrefixContentStringFormat", typeof (string), typeof (TabablzControl), new PropertyMetadata(default(string)));
+
+ public string HeaderPrefixContentStringFormat
+ {
+ get { return (string) GetValue(HeaderPrefixContentStringFormatProperty); }
+ set { SetValue(HeaderPrefixContentStringFormatProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderPrefixContentTemplateProperty = DependencyProperty.Register(
+ "HeaderPrefixContentTemplate", typeof (DataTemplate), typeof (TabablzControl), new PropertyMetadata(default(DataTemplate)));
+
+ public DataTemplate HeaderPrefixContentTemplate
+ {
+ get { return (DataTemplate) GetValue(HeaderPrefixContentTemplateProperty); }
+ set { SetValue(HeaderPrefixContentTemplateProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderPrefixContentTemplateSelectorProperty = DependencyProperty.Register(
+ "HeaderPrefixContentTemplateSelector", typeof (DataTemplateSelector), typeof (TabablzControl), new PropertyMetadata(default(DataTemplateSelector)));
+
+ public DataTemplateSelector HeaderPrefixContentTemplateSelector
+ {
+ get { return (DataTemplateSelector) GetValue(HeaderPrefixContentTemplateSelectorProperty); }
+ set { SetValue(HeaderPrefixContentTemplateSelectorProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderSuffixContentProperty = DependencyProperty.Register(
+ "HeaderSuffixContent", typeof(object), typeof(TabablzControl), new PropertyMetadata(default(object)));
+
+ public object HeaderSuffixContent
+ {
+ get { return (object)GetValue(HeaderSuffixContentProperty); }
+ set { SetValue(HeaderSuffixContentProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderSuffixContentStringFormatProperty = DependencyProperty.Register(
+ "HeaderSuffixContentStringFormat", typeof(string), typeof(TabablzControl), new PropertyMetadata(default(string)));
+
+ public string HeaderSuffixContentStringFormat
+ {
+ get { return (string)GetValue(HeaderSuffixContentStringFormatProperty); }
+ set { SetValue(HeaderSuffixContentStringFormatProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderSuffixContentTemplateProperty = DependencyProperty.Register(
+ "HeaderSuffixContentTemplate", typeof(DataTemplate), typeof(TabablzControl), new PropertyMetadata(default(DataTemplate)));
+
+ public DataTemplate HeaderSuffixContentTemplate
+ {
+ get { return (DataTemplate)GetValue(HeaderSuffixContentTemplateProperty); }
+ set { SetValue(HeaderSuffixContentTemplateProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderSuffixContentTemplateSelectorProperty = DependencyProperty.Register(
+ "HeaderSuffixContentTemplateSelector", typeof(DataTemplateSelector), typeof(TabablzControl), new PropertyMetadata(default(DataTemplateSelector)));
+
+ public DataTemplateSelector HeaderSuffixContentTemplateSelector
+ {
+ get { return (DataTemplateSelector)GetValue(HeaderSuffixContentTemplateSelectorProperty); }
+ set { SetValue(HeaderSuffixContentTemplateSelectorProperty, value); }
+ }
+
+ public static readonly DependencyProperty ShowDefaultCloseButtonProperty = DependencyProperty.Register(
+ "ShowDefaultCloseButton", typeof (bool), typeof (TabablzControl), new PropertyMetadata(default(bool)));
+
+ ///
+ /// Indicates whether a default close button should be displayed. If manually templating the tab header content the close command
+ /// can be called by executing the command (typically via a ).
+ ///
+ public bool ShowDefaultCloseButton
+ {
+ get { return (bool) GetValue(ShowDefaultCloseButtonProperty); }
+ set { SetValue(ShowDefaultCloseButtonProperty, value); }
+ }
+
+ public static readonly DependencyProperty ShowDefaultAddButtonProperty = DependencyProperty.Register(
+ "ShowDefaultAddButton", typeof (bool), typeof (TabablzControl), new PropertyMetadata(default(bool)));
+
+ ///
+ /// Indicates whether a default add button should be displayed. Alternately an add button
+ /// could be added in or , utilising
+ /// .
+ ///
+ public bool ShowDefaultAddButton
+ {
+ get { return (bool) GetValue(ShowDefaultAddButtonProperty); }
+ set { SetValue(ShowDefaultAddButtonProperty, value); }
+ }
+
+ public static readonly DependencyProperty IsHeaderPanelVisibleProperty = DependencyProperty.Register(
+ "IsHeaderPanelVisible", typeof(bool), typeof(TabablzControl), new PropertyMetadata(true));
+
+ ///
+ /// Indicates wither the heaeder panel is visible. Default is true .
+ ///
+ public bool IsHeaderPanelVisible
+ {
+ get { return (bool)GetValue(IsHeaderPanelVisibleProperty); }
+ set { SetValue(IsHeaderPanelVisibleProperty, value); }
+ }
+
+ public static readonly DependencyProperty AddLocationHintProperty = DependencyProperty.Register(
+ "AddLocationHint", typeof (AddLocationHint), typeof (TabablzControl), new PropertyMetadata(AddLocationHint.Last));
+
+ ///
+ /// Gets or sets the location to add new tab items in the header.
+ ///
+ ///
+ /// The logical order of the header items might not add match the content of the source items,
+ /// so this property allows control of where new items should appear.
+ ///
+ public AddLocationHint AddLocationHint
+ {
+ get { return (AddLocationHint) GetValue(AddLocationHintProperty); }
+ set { SetValue(AddLocationHintProperty, value); }
+ }
+
+ public static readonly DependencyProperty FixedHeaderCountProperty = DependencyProperty.Register(
+ "FixedHeaderCount", typeof (int), typeof (TabablzControl), new PropertyMetadata(default(int)));
+
+ ///
+ /// Allows a the first adjacent tabs to be fixed (no dragging, and default close button will not show).
+ ///
+ public int FixedHeaderCount
+ {
+ get { return (int) GetValue(FixedHeaderCountProperty); }
+ set { SetValue(FixedHeaderCountProperty, value); }
+ }
+
+ public static readonly DependencyProperty InterTabControllerProperty = DependencyProperty.Register(
+ "InterTabController", typeof (InterTabController), typeof (TabablzControl), new PropertyMetadata(null, InterTabControllerPropertyChangedCallback));
+
+ private static void InterTabControllerPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
+ {
+ var instance = (TabablzControl)dependencyObject;
+ if (dependencyPropertyChangedEventArgs.OldValue != null)
+ instance.RemoveLogicalChild(dependencyPropertyChangedEventArgs.OldValue);
+ if (dependencyPropertyChangedEventArgs.NewValue != null)
+ instance.AddLogicalChild(dependencyPropertyChangedEventArgs.NewValue);
+ }
+
+ ///
+ /// An must be provided to enable tab tearing. Behaviour customisations can be applied
+ /// vie the controller.
+ ///
+ public InterTabController InterTabController
+ {
+ get { return (InterTabController) GetValue(InterTabControllerProperty); }
+ set { SetValue(InterTabControllerProperty, value); }
+ }
+
+ ///
+ /// Allows a factory to be provided for generating new items. Typically used in conjunction with .
+ ///
+ public static readonly DependencyProperty NewItemFactoryProperty = DependencyProperty.Register(
+ "NewItemFactory", typeof (Func), typeof (TabablzControl), new PropertyMetadata(default(Func)));
+
+ ///
+ /// Allows a factory to be provided for generating new items. Typically used in conjunction with .
+ ///
+ public Func NewItemFactory
+ {
+ get { return (Func) GetValue(NewItemFactoryProperty); }
+ set { SetValue(NewItemFactoryProperty, value); }
+ }
+
+ private static readonly DependencyPropertyKey IsEmptyPropertyKey =
+ DependencyProperty.RegisterReadOnly(
+ "IsEmpty", typeof (bool), typeof (TabablzControl),
+ new PropertyMetadata(true, OnIsEmptyChanged));
+
+ ///
+ /// Indicates if there are no current tab items.
+ ///
+ public static readonly DependencyProperty IsEmptyProperty =
+ IsEmptyPropertyKey.DependencyProperty;
+
+ ///
+ /// Indicates if there are no current tab items.
+ ///
+ public bool IsEmpty
+ {
+ get { return (bool) GetValue(IsEmptyProperty); }
+ private set { SetValue(IsEmptyPropertyKey, value); }
+ }
+
+ ///
+ /// Raised when changes.
+ ///
+ public static readonly RoutedEvent IsEmptyChangedEvent =
+ EventManager.RegisterRoutedEvent(
+ "IsEmptyChanged",
+ RoutingStrategy.Bubble,
+ typeof (RoutedPropertyChangedEventHandler),
+ typeof (TabablzControl));
+
+ ///
+ /// Event handler to list to .
+ ///
+ public event RoutedPropertyChangedEventHandler IsEmptyChanged
+ {
+ add { AddHandler(IsEmptyChangedEvent, value); }
+ remove { RemoveHandler(IsEmptyChangedEvent, value); }
+ }
+
+ private static void OnIsEmptyChanged(
+ DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var instance = d as TabablzControl;
+ var args = new RoutedPropertyChangedEventArgs(
+ (bool) e.OldValue,
+ (bool) e.NewValue) {RoutedEvent = IsEmptyChangedEvent};
+ instance?.RaiseEvent(args);
+ }
+
+ ///
+ /// Optionally allows a close item hook to be bound in. If this propety is provided, the func must return true for the close to continue.
+ ///
+ public static readonly DependencyProperty ClosingItemCallbackProperty = DependencyProperty.Register(
+ "ClosingItemCallback", typeof(ItemActionCallback), typeof(TabablzControl), new PropertyMetadata(default(ItemActionCallback)));
+
+ ///
+ /// Optionally allows a close item hook to be bound in. If this propety is provided, the func must return true for the close to continue.
+ ///
+ public ItemActionCallback ClosingItemCallback
+ {
+ get { return (ItemActionCallback)GetValue(ClosingItemCallbackProperty); }
+ set { SetValue(ClosingItemCallbackProperty, value); }
+ }
+
+ ///
+ /// Set to true to have tabs automatically be moved to another tab is a window is closed, so that they arent lost.
+ /// Can be useful for fixed/persistant tabs that may have been dragged into another Window. You can further control
+ /// this behaviour on a per tab item basis by providing .
+ ///
+ public static readonly DependencyProperty ConsolidateOrphanedItemsProperty = DependencyProperty.Register(
+ "ConsolidateOrphanedItems", typeof (bool), typeof (TabablzControl), new PropertyMetadata(default(bool)));
+
+ ///
+ /// Set to true to have tabs automatically be moved to another tab is a window is closed, so that they arent lost.
+ /// Can be useful for fixed/persistant tabs that may have been dragged into another Window. You can further control
+ /// this behaviour on a per tab item basis by providing .
+ ///
+ public bool ConsolidateOrphanedItems
+ {
+ get { return (bool) GetValue(ConsolidateOrphanedItemsProperty); }
+ set { SetValue(ConsolidateOrphanedItemsProperty, value); }
+ }
+
+ ///
+ /// Assuming is set to true , consolidation of individual
+ /// tab items can be cancelled by providing this call back and cancelling the
+ /// instance.
+ ///
+ public static readonly DependencyProperty ConsolidatingOrphanedItemCallbackProperty = DependencyProperty.Register(
+ "ConsolidatingOrphanedItemCallback", typeof (ItemActionCallback), typeof (TabablzControl), new PropertyMetadata(default(ItemActionCallback)));
+
+ ///
+ /// Assuming is set to true , consolidation of individual
+ /// tab items can be cancelled by providing this call back and cancelling the
+ /// instance.
+ ///
+ public ItemActionCallback ConsolidatingOrphanedItemCallback
+ {
+ get { return (ItemActionCallback) GetValue(ConsolidatingOrphanedItemCallbackProperty); }
+ set { SetValue(ConsolidatingOrphanedItemCallbackProperty, value); }
+ }
+
+
+
+ private static readonly DependencyPropertyKey IsDraggingWindowPropertyKey =
+ DependencyProperty.RegisterReadOnly(
+ "IsDraggingWindow", typeof (bool), typeof (TabablzControl),
+ new PropertyMetadata(default(bool), OnIsDraggingWindowChanged));
+
+ ///
+ /// Readonly dependency property which indicates whether the owning
+ /// is currently dragged
+ ///
+ public static readonly DependencyProperty IsDraggingWindowProperty =
+ IsDraggingWindowPropertyKey.DependencyProperty;
+
+ ///
+ /// Readonly dependency property which indicates whether the owning
+ /// is currently dragged
+ ///
+ public bool IsDraggingWindow
+ {
+ get { return (bool) GetValue(IsDraggingWindowProperty); }
+ private set { SetValue(IsDraggingWindowPropertyKey, value); }
+ }
+
+ ///
+ /// Event indicating has changed.
+ ///
+ public static readonly RoutedEvent IsDraggingWindowChangedEvent =
+ EventManager.RegisterRoutedEvent(
+ "IsDraggingWindowChanged",
+ RoutingStrategy.Bubble,
+ typeof (RoutedPropertyChangedEventHandler),
+ typeof (TabablzControl));
+
+ ///
+ /// Event indicating has changed.
+ ///
+ public event RoutedPropertyChangedEventHandler IsDraggingWindowChanged
+ {
+ add { AddHandler(IsDraggingWindowChangedEvent, value); }
+ remove { RemoveHandler(IsDraggingWindowChangedEvent, value); }
+ }
+
+ private static void OnIsDraggingWindowChanged(
+ DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var instance = (TabablzControl) d;
+ var args = new RoutedPropertyChangedEventArgs(
+ (bool) e.OldValue,
+ (bool) e.NewValue)
+ {
+ RoutedEvent = IsDraggingWindowChangedEvent
+ };
+ instance.RaiseEvent(args);
+
+ }
+
+ ///
+ /// Temporarily set by the framework if a users drag opration causes a Window to close (e.g if a tab is dragging into another tab).
+ ///
+ public static readonly DependencyProperty IsClosingAsPartOfDragOperationProperty = DependencyProperty.RegisterAttached(
+ "IsClosingAsPartOfDragOperation", typeof (bool), typeof (TabablzControl), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.NotDataBindable));
+
+ internal static void SetIsClosingAsPartOfDragOperation(Window element, bool value)
+ {
+ element.SetValue(IsClosingAsPartOfDragOperationProperty, value);
+ }
+
+ ///
+ /// Helper method which can tell you if a is being automatically closed due
+ /// to a user instigated drag operation (typically when a single tab is dropped into another window.
+ ///
+ ///
+ ///
+ public static bool GetIsClosingAsPartOfDragOperation(Window element)
+ {
+ return (bool) element.GetValue(IsClosingAsPartOfDragOperationProperty);
+ }
+
+ ///
+ /// Provide a hint for how the header should size itself if there are no tabs left (and a Window is still open).
+ ///
+ public static readonly DependencyProperty EmptyHeaderSizingHintProperty = DependencyProperty.Register(
+ "EmptyHeaderSizingHint", typeof (EmptyHeaderSizingHint), typeof (TabablzControl), new PropertyMetadata(default(EmptyHeaderSizingHint)));
+
+ ///
+ /// Provide a hint for how the header should size itself if there are no tabs left (and a Window is still open).
+ ///
+ public EmptyHeaderSizingHint EmptyHeaderSizingHint
+ {
+ get { return (EmptyHeaderSizingHint) GetValue(EmptyHeaderSizingHintProperty); }
+ set { SetValue(EmptyHeaderSizingHintProperty, value); }
+ }
+
+ public static readonly DependencyProperty IsWrappingTabItemProperty = DependencyProperty.RegisterAttached(
+ "IsWrappingTabItem", typeof (bool), typeof (TabablzControl), new PropertyMetadata(default(bool)));
+
+ internal static void SetIsWrappingTabItem(DependencyObject element, bool value)
+ {
+ element.SetValue(IsWrappingTabItemProperty, value);
+ }
+
+ public static bool GetIsWrappingTabItem(DependencyObject element)
+ {
+ return (bool) element.GetValue(IsWrappingTabItemProperty);
+ }
+
+ ///
+ /// Adds an item to the source collection. If the InterTabController.InterTabClient is set that instance will be deferred to.
+ /// Otherwise an attempt will be made to add to the property, and lastly .
+ ///
+ ///
+ public void AddToSource(object item)
+ {
+ if (item == null) throw new ArgumentNullException("item");
+
+ var manualInterTabClient = InterTabController == null ? null : InterTabController.InterTabClient as IManualInterTabClient;
+ if (manualInterTabClient != null)
+ {
+ manualInterTabClient.Add(item);
+ }
+ else
+ {
+ CollectionTeaser collectionTeaser;
+ if (CollectionTeaser.TryCreate(ItemsSource, out collectionTeaser))
+ collectionTeaser.Add(item);
+ else
+ Items.Add(item);
+ }
+ }
+
+ ///
+ /// Removes an item from the source collection. If the InterTabController.InterTabClient is set that instance will be deferred to.
+ /// Otherwise an attempt will be made to remove from the property, and lastly .
+ ///
+ ///
+ public void RemoveFromSource(object item)
+ {
+ if (item == null) throw new ArgumentNullException("item");
+
+ var manualInterTabClient = InterTabController == null ? null : InterTabController.InterTabClient as IManualInterTabClient;
+ if (manualInterTabClient != null)
+ {
+ manualInterTabClient.Remove(item);
+ }
+ else
+ {
+ CollectionTeaser collectionTeaser;
+ if (CollectionTeaser.TryCreate(ItemsSource, out collectionTeaser))
+ collectionTeaser.Remove(item);
+ else
+ Items.Remove(item);
+ }
+ }
+
+ ///
+ /// Gets the header items, ordered according to their current visual position in the tab header.
+ ///
+ ///
+ public IEnumerable GetOrderedHeaders()
+ {
+ return _dragablzItemsControl.ItemsOrganiser.Sort(_dragablzItemsControl.DragablzItems());
+ }
+
+ ///
+ /// Called when is called.
+ ///
+ public override void OnApplyTemplate()
+ {
+ _templateSubscription?.Dispose();
+ _templateSubscription = Disposable.Empty;
+
+ _dragablzItemsControl = GetTemplateChild(HeaderItemsControlPartName) as DragablzItemsControl;
+ if (_dragablzItemsControl != null)
+ {
+ _dragablzItemsControl.ItemContainerGenerator.StatusChanged += ItemContainerGeneratorOnStatusChanged;
+ _templateSubscription =
+ Disposable.Create(
+ () =>
+ _dragablzItemsControl.ItemContainerGenerator.StatusChanged -=
+ ItemContainerGeneratorOnStatusChanged);
+
+ _dragablzItemsControl.ContainerCustomisations = new ContainerCustomisations(null, PrepareChildContainerForItemOverride);
+ }
+
+ if (SelectedItem == null)
+ SetCurrentValue(SelectedItemProperty, Items.OfType().FirstOrDefault());
+
+ _itemsHolder = GetTemplateChild(ItemsHolderPartName) as Panel;
+ UpdateSelectedItem();
+ MarkWrappedTabItems();
+ MarkInitialSelection();
+
+ base.OnApplyTemplate();
+ }
+
+ ///
+ /// update the visible child in the ItemsHolder
+ ///
+ ///
+ protected override void OnSelectionChanged(SelectionChangedEventArgs e)
+ {
+ if (e.RemovedItems.Count > 0 && e.AddedItems.Count > 0)
+ _previousSelection = new WeakReference(e.RemovedItems[0]);
+
+ base.OnSelectionChanged(e);
+ UpdateSelectedItem();
+
+ if (_dragablzItemsControl == null) return;
+
+ Func> notTabItems =
+ l =>
+ l.Cast()
+ .Where(o => !(o is TabItem))
+ .Select(o => _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(o))
+ .OfType();
+ foreach (var addedItem in notTabItems(e.AddedItems))
+ {
+ addedItem.IsSelected = true;
+ addedItem.BringIntoView();
+ }
+ foreach (var removedItem in notTabItems(e.RemovedItems))
+ {
+ removedItem.IsSelected = false;
+ }
+
+ foreach (var tabItem in e.AddedItems.OfType().Select(t => _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(t)).OfType())
+ {
+ tabItem.IsSelected = true;
+ tabItem.BringIntoView();
+ }
+ foreach (var tabItem in e.RemovedItems.OfType().Select(t => _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(t)).OfType())
+ {
+ tabItem.IsSelected = false;
+ }
+ }
+
+ ///
+ /// 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 (_itemsHolder == null)
+ {
+ return;
+ }
+
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Reset:
+ _itemsHolder.Children.Clear();
+
+ if (Items.Count > 0)
+ {
+ SelectedItem = base.Items[0];
+ UpdateSelectedItem();
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Add:
+ UpdateSelectedItem();
+ if (e.NewItems.Count == 1 && Items.Count > 1 && _dragablzItemsControl != null && _interTabTransfer == null)
+ _dragablzItemsControl.MoveItem(new MoveItemRequest(e.NewItems[0], SelectedItem, AddLocationHint));
+
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ foreach (var item in e.OldItems)
+ {
+ var cp = FindChildContentPresenter(item);
+ if (cp != null)
+ _itemsHolder.Children.Remove(cp);
+ }
+
+ if (SelectedItem == null)
+ RestorePreviousSelection();
+ UpdateSelectedItem();
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ throw new NotImplementedException("Replace not implemented yet");
+ }
+
+ IsEmpty = Items.Count == 0;
+ }
+
+ ///
+ /// Provides class handling for the routed event that occurs when the user presses a key.
+ ///
+ /// Provides data for .
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ var sortedDragablzItems = _dragablzItemsControl.ItemsOrganiser.Sort(_dragablzItemsControl.DragablzItems()).ToList();
+ DragablzItem selectDragablzItem = null;
+ switch (e.Key)
+ {
+ case Key.Tab:
+ if (SelectedItem == null)
+ {
+ selectDragablzItem = sortedDragablzItems.FirstOrDefault();
+ break;
+ }
+
+ if ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
+ {
+ var selectedDragablzItem = (DragablzItem)_dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(SelectedItem);
+ var selectedDragablzItemIndex = sortedDragablzItems.IndexOf(selectedDragablzItem);
+ var direction = ((e.KeyboardDevice.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
+ ? -1 : 1;
+ var newIndex = selectedDragablzItemIndex + direction;
+ if (newIndex < 0) newIndex = sortedDragablzItems.Count - 1;
+ else if (newIndex == sortedDragablzItems.Count) newIndex = 0;
+
+ selectDragablzItem = sortedDragablzItems[newIndex];
+ }
+ break;
+ case Key.Home:
+ selectDragablzItem = sortedDragablzItems.FirstOrDefault();
+ break;
+ case Key.End:
+ selectDragablzItem = sortedDragablzItems.LastOrDefault();
+ break;
+ }
+
+ if (selectDragablzItem != null)
+ {
+ var item = _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(selectDragablzItem);
+ SetCurrentValue(SelectedItemProperty, item);
+ e.Handled = true;
+ }
+
+ if (!e.Handled)
+ base.OnKeyDown(e);
+ }
+
+ ///
+ /// Provides an appropriate automation peer implementation for this control
+ /// as part of the WPF automation infrastructure.
+ ///
+ /// The type-specific System.Windows.Automation.Peers.AutomationPeer implementation.
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new FrameworkElementAutomationPeer(this);
+ }
+
+ internal static TabablzControl GetOwnerOfHeaderItems(DragablzItemsControl itemsControl)
+ {
+ return LoadedInstances.FirstOrDefault(t => Equals(t._dragablzItemsControl, itemsControl));
+ }
+
+ private static void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
+ {
+ var tabablzControl = (TabablzControl) sender;
+ if (tabablzControl.IsVisible)
+ VisibleInstances.Add(tabablzControl);
+ else if (VisibleInstances.Contains(tabablzControl))
+ VisibleInstances.Remove(tabablzControl);
+ }
+
+ private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
+ {
+ LoadedInstances.Add(this);
+ var window = Window.GetWindow(this);
+ if (window == null) return;
+ window.Closing += WindowOnClosing;
+ _windowSubscription.Disposable = Disposable.Create(() => window.Closing -= WindowOnClosing);
+ }
+
+ private void WindowOnClosing(object sender, CancelEventArgs cancelEventArgs)
+ {
+ _windowSubscription.Disposable = Disposable.Empty;
+ if (!ConsolidateOrphanedItems || InterTabController == null) return;
+
+ var window = (Window)sender;
+
+ var orphanedItems = _dragablzItemsControl.DragablzItems();
+ if (ConsolidatingOrphanedItemCallback != null)
+ {
+ orphanedItems =
+ orphanedItems.Where(
+ di =>
+ {
+ var args = new ItemActionCallbackArgs(window, this, di);
+ ConsolidatingOrphanedItemCallback(args);
+ return !args.IsCancelled;
+ }).ToList();
+ }
+
+ var target =
+ LoadedInstances.Except(this)
+ .FirstOrDefault(
+ other =>
+ other.InterTabController != null &&
+ other.InterTabController.Partition == InterTabController.Partition);
+ if (target == null) return;
+
+ foreach (var item in orphanedItems.Select(orphanedItem => _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(orphanedItem)))
+ {
+ RemoveFromSource(item);
+ target.AddToSource(item);
+ }
+ }
+
+ private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
+ {
+ _windowSubscription.Disposable = Disposable.Empty;
+ LoadedInstances.Remove(this);
+ }
+
+ private void MarkWrappedTabItems()
+ {
+ if (_dragablzItemsControl == null) return;
+
+ foreach (var pair in _dragablzItemsControl.Items.OfType().Select(tabItem =>
+ new
+ {
+ tabItem,
+ dragablzItem = _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(tabItem) as DragablzItem
+ }).Where(a => a.dragablzItem != null))
+ {
+ var toolTipBinding = new Binding("ToolTip") { Source = pair.tabItem };
+ BindingOperations.SetBinding(pair.dragablzItem, ToolTipProperty, toolTipBinding);
+ SetIsWrappingTabItem(pair.dragablzItem, true);
+ }
+ }
+
+ private void MarkInitialSelection()
+ {
+ if (_dragablzItemsControl == null ||
+ _dragablzItemsControl.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) return;
+
+ if (_dragablzItemsControl == null || SelectedItem == null) return;
+
+ var tabItem = SelectedItem as TabItem;
+ tabItem?.SetCurrentValue(IsSelectedProperty, true);
+
+ var containerFromItem =
+ _dragablzItemsControl.ItemContainerGenerator.ContainerFromItem(SelectedItem) as DragablzItem;
+
+ containerFromItem?.SetCurrentValue(DragablzItem.IsSelectedProperty, true);
+ }
+
+ private void ItemDragStarted(object sender, DragablzDragStartedEventArgs e)
+ {
+ if (!IsMyItem(e.DragablzItem)) return;
+
+ //the thumb may steal the user selection, so we will try and apply it manually
+ if (_dragablzItemsControl == null) return;
+
+ e.DragablzItem.IsDropTargetFound = false;
+
+ var sourceOfDragItemsControl = ItemsControlFromItemContainer(e.DragablzItem) as DragablzItemsControl;
+ if (sourceOfDragItemsControl == null || !Equals(sourceOfDragItemsControl, _dragablzItemsControl)) return;
+
+ var itemsControlOffset = Mouse.GetPosition(_dragablzItemsControl);
+ _tabHeaderDragStartInformation = new TabHeaderDragStartInformation(e.DragablzItem, itemsControlOffset.X,
+ itemsControlOffset.Y, e.DragStartedEventArgs.HorizontalOffset, e.DragStartedEventArgs.VerticalOffset);
+
+ foreach (var otherItem in _dragablzItemsControl.Containers().Except(e.DragablzItem))
+ otherItem.IsSelected = false;
+ e.DragablzItem.IsSelected = true;
+ e.DragablzItem.PartitionAtDragStart = InterTabController?.Partition;
+ var item = _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(e.DragablzItem);
+ var tabItem = item as TabItem;
+ if (tabItem != null)
+ tabItem.IsSelected = true;
+ SelectedItem = item;
+
+ if (ShouldDragWindow(sourceOfDragItemsControl))
+ IsDraggingWindow = true;
+ }
+
+ private bool ShouldDragWindow(DragablzItemsControl sourceOfDragItemsControl)
+ {
+ return (Items.Count == 1
+ && (InterTabController == null || InterTabController.MoveWindowWithSolitaryTabs)
+ && !Layout.IsContainedWithinBranch(sourceOfDragItemsControl));
+ }
+
+ private void PreviewItemDragDelta(object sender, DragablzDragDeltaEventArgs e)
+ {
+ if (_dragablzItemsControl == null) return;
+
+ var sourceOfDragItemsControl = ItemsControlFromItemContainer(e.DragablzItem) as DragablzItemsControl;
+ if (sourceOfDragItemsControl == null || !Equals(sourceOfDragItemsControl, _dragablzItemsControl)) return;
+
+ if (!ShouldDragWindow(sourceOfDragItemsControl)) return;
+
+ if (MonitorReentry(e)) return;
+
+ var myWindow = Window.GetWindow(this);
+ if (myWindow == null) return;
+
+ if (_interTabTransfer != null)
+ {
+ var cursorPos = Native.GetCursorPos().ToWpf();
+ if (_interTabTransfer.BreachOrientation == Orientation.Vertical)
+ {
+ var vector = cursorPos - _interTabTransfer.DragStartWindowOffset;
+ myWindow.Left = vector.X;
+ myWindow.Top = vector.Y;
+
+ }
+ else
+ {
+ var offset = e.DragablzItem.TranslatePoint(_interTabTransfer.OriginatorContainer.MouseAtDragStart, myWindow);
+ var borderVector = myWindow.PointToScreen(new Point()).ToWpf() - new Point(myWindow.Left, myWindow.Top);
+ offset.Offset(borderVector.X, borderVector.Y);
+ myWindow.Left = cursorPos.X - offset.X;
+ myWindow.Top = cursorPos.Y - offset.Y;
+ }
+ }
+ else
+ {
+ // the following lines were replaced to support right to left window when returning tab item
+ //myWindow.Left += e.DragDeltaEventArgs.HorizontalChange;
+ //myWindow.Top += e.DragDeltaEventArgs.VerticalChange;
+
+ if (myWindow.FlowDirection == FlowDirection.RightToLeft)
+ {
+ myWindow.Left -= e.DragDeltaEventArgs.HorizontalChange;
+ }
+ else
+ {
+ myWindow.Left += e.DragDeltaEventArgs.HorizontalChange;
+ }
+
+ myWindow.Top += e.DragDeltaEventArgs.VerticalChange;
+
+ }
+
+ e.Handled = true;
+ }
+
+ private bool MonitorReentry(DragablzDragDeltaEventArgs e)
+ {
+ var screenMousePosition = _dragablzItemsControl.PointToScreen(Mouse.GetPosition(_dragablzItemsControl));
+
+ var sourceTabablzControl = (TabablzControl) e.Source;
+ if (sourceTabablzControl.Items.Count > 1 && e.DragablzItem.LogicalIndex < sourceTabablzControl.FixedHeaderCount)
+ {
+ return false;
+ }
+
+ var otherTabablzControls = LoadedInstances
+ .Where(
+ tc =>
+ tc != this && tc.InterTabController != null && InterTabController != null
+ && Equals(tc.InterTabController.Partition, InterTabController.Partition)
+ && tc._dragablzItemsControl != null)
+ .Select(tc =>
+ {
+ var topLeft = tc._dragablzItemsControl.PointToScreen(new Point());
+ var lastFixedItem = tc._dragablzItemsControl.DragablzItems()
+ .OrderBy(di=> di.LogicalIndex)
+ .Take(tc._dragablzItemsControl.FixedItemCount)
+ .LastOrDefault();
+ //TODO work this for vert tabs
+ if (lastFixedItem != null)
+ topLeft.Offset(lastFixedItem.X + lastFixedItem.ActualWidth, 0);
+ var bottomRight =
+ tc._dragablzItemsControl.PointToScreen(new Point(tc._dragablzItemsControl.ActualWidth,
+ tc._dragablzItemsControl.ActualHeight));
+
+ return new {tc, topLeft, bottomRight};
+ });
+
+
+ var target = Native.SortWindowsTopToBottom(Application.Current.Windows.OfType())
+ .Join(otherTabablzControls, w => w, a => Window.GetWindow(a.tc), (w, a) => a)
+ .FirstOrDefault(a => new Rect(a.topLeft, a.bottomRight).Contains(screenMousePosition));
+
+ if (target == null) return false;
+
+ var mousePositionOnItem = Mouse.GetPosition(e.DragablzItem);
+
+ var floatingItemSnapShots = this.VisualTreeDepthFirstTraversal()
+ .OfType()
+ .SelectMany(l => l.FloatingDragablzItems().Select(FloatingItemSnapShot.Take))
+ .ToList();
+
+ e.DragablzItem.IsDropTargetFound = true;
+ var item = RemoveItem(e.DragablzItem);
+
+ var interTabTransfer = new InterTabTransfer(item, e.DragablzItem, mousePositionOnItem, floatingItemSnapShots);
+ e.DragablzItem.IsDragging = false;
+
+ target.tc.ReceiveDrag(interTabTransfer);
+ e.Cancel = true;
+
+ return true;
+ }
+
+ internal object RemoveItem(DragablzItem dragablzItem)
+ {
+ var item = _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(dragablzItem);
+
+ //stop the header shrinking if the tab stays open when empty
+ var minSize = EmptyHeaderSizingHint == EmptyHeaderSizingHint.PreviousTab
+ ? new Size(_dragablzItemsControl.ActualWidth, _dragablzItemsControl.ActualHeight)
+ : new Size();
+
+ _dragablzItemsControl.MinHeight = 0;
+ _dragablzItemsControl.MinWidth = 0;
+
+ var contentPresenter = FindChildContentPresenter(item);
+ RemoveFromSource(item);
+ _itemsHolder.Children.Remove(contentPresenter);
+
+ if (Items.Count != 0) return item;
+
+ var window = Window.GetWindow(this);
+ if (window != null
+ && InterTabController != null
+ && InterTabController.InterTabClient.TabEmptiedHandler(this, window) == TabEmptiedResponse.CloseWindowOrLayoutBranch)
+ {
+ if (Layout.ConsolidateBranch(this)) return item;
+
+ try
+ {
+ SetIsClosingAsPartOfDragOperation(window, true);
+ window.Close();
+ }
+ finally
+ {
+ SetIsClosingAsPartOfDragOperation(window, false);
+ }
+ }
+ else
+ {
+ _dragablzItemsControl.MinHeight = minSize.Height;
+ _dragablzItemsControl.MinWidth = minSize.Width;
+ }
+ return item;
+ }
+
+ private void ItemDragCompleted(object sender, DragablzDragCompletedEventArgs e)
+ {
+ if (!IsMyItem(e.DragablzItem)) return;
+
+ _interTabTransfer = null;
+ _dragablzItemsControl.LockedMeasure = null;
+ IsDraggingWindow = false;
+ }
+
+ private void ItemDragDelta(object sender, DragablzDragDeltaEventArgs e)
+ {
+ if (!IsMyItem(e.DragablzItem)) return;
+ if (FixedHeaderCount > 0 &&
+ _dragablzItemsControl.ItemsOrganiser.Sort(_dragablzItemsControl.DragablzItems())
+ .Take(FixedHeaderCount)
+ .Contains(e.DragablzItem))
+ return;
+
+ if (_tabHeaderDragStartInformation == null ||
+ !Equals(_tabHeaderDragStartInformation.DragItem, e.DragablzItem) || InterTabController == null) return;
+
+ if (InterTabController.InterTabClient == null)
+ throw new InvalidOperationException("An InterTabClient must be provided on an InterTabController.");
+
+ MonitorBreach(e);
+ }
+
+ private bool IsMyItem(DragablzItem item)
+ {
+ return _dragablzItemsControl != null && _dragablzItemsControl.DragablzItems().Contains(item);
+ }
+
+ private void MonitorBreach(DragablzDragDeltaEventArgs e)
+ {
+ var mousePositionOnHeaderItemsControl = Mouse.GetPosition(_dragablzItemsControl);
+
+ Orientation? breachOrientation = null;
+ if (mousePositionOnHeaderItemsControl.X < -InterTabController.HorizontalPopoutGrace
+ || (mousePositionOnHeaderItemsControl.X - _dragablzItemsControl.ActualWidth) > InterTabController.HorizontalPopoutGrace)
+ breachOrientation = Orientation.Horizontal;
+ else if (mousePositionOnHeaderItemsControl.Y < -InterTabController.VerticalPopoutGrace
+ || (mousePositionOnHeaderItemsControl.Y - _dragablzItemsControl.ActualHeight) > InterTabController.VerticalPopoutGrace)
+ breachOrientation = Orientation.Vertical;
+
+ if (!breachOrientation.HasValue) return;
+
+ var newTabHost = InterTabController.InterTabClient.GetNewHost(InterTabController.InterTabClient,
+ InterTabController.Partition, this);
+ if (newTabHost?.TabablzControl == null || newTabHost.Container == null)
+ throw new ApplicationException("New tab host was not correctly provided");
+
+ var item = _dragablzItemsControl.ItemContainerGenerator.ItemFromContainer(e.DragablzItem);
+ var isTransposing = IsTransposing(newTabHost.TabablzControl);
+
+ var myWindow = Window.GetWindow(this);
+ if (myWindow == null) throw new ApplicationException("Unable to find owning window.");
+ var dragStartWindowOffset = ConfigureNewHostSizeAndGetDragStartWindowOffset(myWindow, newTabHost, e.DragablzItem, isTransposing);
+
+ var dragableItemHeaderPoint = e.DragablzItem.TranslatePoint(new Point(), _dragablzItemsControl);
+ var dragableItemSize = new Size(e.DragablzItem.ActualWidth, e.DragablzItem.ActualHeight);
+ var floatingItemSnapShots = this.VisualTreeDepthFirstTraversal()
+ .OfType()
+ .SelectMany(l => l.FloatingDragablzItems().Select(FloatingItemSnapShot.Take))
+ .ToList();
+
+ var interTabTransfer = new InterTabTransfer(item, e.DragablzItem, breachOrientation.Value, dragStartWindowOffset, e.DragablzItem.MouseAtDragStart, dragableItemHeaderPoint, dragableItemSize, floatingItemSnapShots, isTransposing);
+
+ if (myWindow.WindowState == WindowState.Maximized)
+ {
+ var desktopMousePosition = Native.GetCursorPos().ToWpf();
+ newTabHost.Container.Left = desktopMousePosition.X - dragStartWindowOffset.X;
+ newTabHost.Container.Top = desktopMousePosition.Y - dragStartWindowOffset.Y;
+ }
+ else
+ {
+ newTabHost.Container.Left = myWindow.Left;
+ newTabHost.Container.Top = myWindow.Top;
+ }
+ newTabHost.Container.Show();
+ var contentPresenter = FindChildContentPresenter(item);
+
+ //stop the header shrinking if the tab stays open when empty
+ var minSize = EmptyHeaderSizingHint == EmptyHeaderSizingHint.PreviousTab
+ ? new Size(_dragablzItemsControl.ActualWidth, _dragablzItemsControl.ActualHeight)
+ : new Size();
+ //System.Diagnostics.Debug.WriteLine("B " + minSize);
+
+ RemoveFromSource(item);
+ _itemsHolder.Children.Remove(contentPresenter);
+ if (Items.Count == 0)
+ {
+ _dragablzItemsControl.MinHeight = minSize.Height;
+ _dragablzItemsControl.MinWidth = minSize.Width;
+ Layout.ConsolidateBranch(this);
+ }
+
+ RestorePreviousSelection();
+
+ foreach (var dragablzItem in _dragablzItemsControl.DragablzItems())
+ {
+ dragablzItem.IsDragging = false;
+ dragablzItem.IsSiblingDragging = false;
+ }
+
+ newTabHost.TabablzControl.ReceiveDrag(interTabTransfer);
+ interTabTransfer.OriginatorContainer.IsDropTargetFound = true;
+ e.Cancel = true;
+ }
+
+ private bool IsTransposing(TabControl target)
+ {
+ return IsVertical(this) != IsVertical(target);
+ }
+
+ private static bool IsVertical(TabControl tabControl)
+ {
+ return tabControl.TabStripPlacement == Dock.Left
+ || tabControl.TabStripPlacement == Dock.Right;
+ }
+
+ private void RestorePreviousSelection()
+ {
+ var previousSelection = _previousSelection?.Target;
+ if (previousSelection != null && Items.Contains(previousSelection))
+ SelectedItem = previousSelection;
+ else
+ SelectedItem = Items.OfType().FirstOrDefault();
+ }
+
+ private Point ConfigureNewHostSizeAndGetDragStartWindowOffset(Window currentWindow, INewTabHost newTabHost, DragablzItem dragablzItem, bool isTransposing)
+ {
+ var layout = this.VisualTreeAncestory().OfType().FirstOrDefault();
+ Point dragStartWindowOffset;
+ if (layout != null)
+ {
+ newTabHost.Container.Width = ActualWidth + Math.Max(0, currentWindow.RestoreBounds.Width - layout.ActualWidth);
+ newTabHost.Container.Height = ActualHeight + Math.Max(0, currentWindow.RestoreBounds.Height - layout.ActualHeight);
+
+ if (currentWindow.FlowDirection == FlowDirection.RightToLeft)
+ {
+ dragStartWindowOffset = dragablzItem.TranslatePoint(new Point(dragablzItem.MouseAtDragStart.X, 0), this);
+ var d2 = currentWindow.TranslatePoint(new Point(dragStartWindowOffset.X, 0), this);
+ dragStartWindowOffset.Offset(d2.X - dragStartWindowOffset.X - dragablzItem.MouseAtDragStart.X, 0);
+ }
+ else
+ {
+ dragStartWindowOffset = dragablzItem.TranslatePoint(new Point(0, 0), this);
+ }
+
+ //dragStartWindowOffset.Offset(currentWindow.RestoreBounds.Width - layout.ActualWidth, currentWindow.RestoreBounds.Height - layout.ActualHeight);
+ }
+ else
+ {
+ if (newTabHost.Container.GetType() == currentWindow.GetType())
+ {
+ newTabHost.Container.Width = currentWindow.RestoreBounds.Width;
+ newTabHost.Container.Height = currentWindow.RestoreBounds.Height;
+ dragStartWindowOffset = isTransposing ? new Point(dragablzItem.MouseAtDragStart.X, dragablzItem.MouseAtDragStart.Y) : dragablzItem.TranslatePoint(new Point(), currentWindow);
+ }
+ else
+ {
+ newTabHost.Container.Width = ActualWidth;
+ newTabHost.Container.Height = ActualHeight;
+ dragStartWindowOffset = isTransposing ? new Point() : dragablzItem.TranslatePoint(new Point(), this);
+ dragStartWindowOffset.Offset(dragablzItem.MouseAtDragStart.X, dragablzItem.MouseAtDragStart.Y);
+ return dragStartWindowOffset;
+ }
+ }
+
+ dragStartWindowOffset.Offset(dragablzItem.MouseAtDragStart.X, dragablzItem.MouseAtDragStart.Y);
+ var borderVector = currentWindow.PointToScreen(new Point()).ToWpf() - new Point(currentWindow.GetActualLeft(), currentWindow.GetActualTop());
+ dragStartWindowOffset.Offset(borderVector.X, borderVector.Y);
+
+ return dragStartWindowOffset;
+ }
+
+ internal void ReceiveDrag(InterTabTransfer interTabTransfer)
+ {
+ var myWindow = Window.GetWindow(this);
+ if (myWindow == null) throw new ApplicationException("Unable to find owning window.");
+ myWindow.Activate();
+
+ _interTabTransfer = interTabTransfer;
+
+ if (Items.Count == 0)
+ {
+ if (interTabTransfer.IsTransposing)
+ _dragablzItemsControl.LockedMeasure = new Size(
+ interTabTransfer.ItemSize.Width,
+ interTabTransfer.ItemSize.Height);
+ else
+ _dragablzItemsControl.LockedMeasure = new Size(
+ interTabTransfer.ItemPositionWithinHeader.X + interTabTransfer.ItemSize.Width,
+ interTabTransfer.ItemPositionWithinHeader.Y + interTabTransfer.ItemSize.Height);
+ }
+
+ var lastFixedItem = _dragablzItemsControl.DragablzItems()
+ .OrderBy(i => i.LogicalIndex)
+ .Take(_dragablzItemsControl.FixedItemCount)
+ .LastOrDefault();
+
+ AddToSource(interTabTransfer.Item);
+ SelectedItem = interTabTransfer.Item;
+
+ Dispatcher.BeginInvoke(new Action(() => Layout.RestoreFloatingItemSnapShots(this, interTabTransfer.FloatingItemSnapShots)), DispatcherPriority.Loaded);
+ _dragablzItemsControl.InstigateDrag(interTabTransfer.Item, newContainer =>
+ {
+ newContainer.PartitionAtDragStart = interTabTransfer.OriginatorContainer.PartitionAtDragStart;
+ newContainer.IsDropTargetFound = true;
+
+ if (interTabTransfer.TransferReason == InterTabTransferReason.Breach)
+ {
+ if (interTabTransfer.IsTransposing)
+ {
+ newContainer.Y = 0;
+ newContainer.X = 0;
+ }
+ else
+ {
+ newContainer.Y = interTabTransfer.OriginatorContainer.Y;
+ newContainer.X = interTabTransfer.OriginatorContainer.X;
+ }
+ }
+ else
+ {
+ if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom)
+ {
+ var mouseXOnItemsControl = Native.GetCursorPos().X -
+ _dragablzItemsControl.PointToScreen(new Point()).X;
+ var newX = mouseXOnItemsControl - interTabTransfer.DragStartItemOffset.X;
+ if (lastFixedItem != null)
+ {
+ newX = Math.Max(newX, lastFixedItem.X + lastFixedItem.ActualWidth);
+ }
+ newContainer.X = newX;
+ newContainer.Y = 0;
+ }
+ else
+ {
+ var mouseYOnItemsControl = Native.GetCursorPos().Y -
+ _dragablzItemsControl.PointToScreen(new Point()).Y;
+ var newY = mouseYOnItemsControl - interTabTransfer.DragStartItemOffset.Y;
+ if (lastFixedItem != null)
+ {
+ newY = Math.Max(newY, lastFixedItem.Y + lastFixedItem.ActualHeight);
+ }
+ newContainer.X = 0;
+ newContainer.Y = newY;
+ }
+ }
+ newContainer.MouseAtDragStart = interTabTransfer.DragStartItemOffset;
+ });
+ }
+
+ ///
+ /// generate a ContentPresenter for the selected item
+ ///
+ private void UpdateSelectedItem()
+ {
+ if (_itemsHolder == null)
+ {
+ return;
+ }
+
+ CreateChildContentPresenter(SelectedItem);
+
+ // show the right child
+ var selectedContent = GetContent(SelectedItem);
+ foreach (ContentPresenter child in _itemsHolder.Children)
+ {
+ var isSelected = (child.Content == selectedContent);
+ child.Visibility = isSelected ? Visibility.Visible : Visibility.Collapsed;
+ child.IsEnabled = isSelected;
+ }
+ }
+
+ private static object GetContent(object item)
+ {
+ return (item is TabItem) ? ((TabItem) item).Content : item;
+ }
+
+ ///
+ /// create the child ContentPresenter for the given item (could be data or a TabItem)
+ ///
+ ///
+ ///
+ private void CreateChildContentPresenter(object item)
+ {
+ if (item == null) return;
+
+ var cp = FindChildContentPresenter(item);
+ if (cp != null) return;
+
+ // the actual child to be added. cp.Tag is a reference to the TabItem
+ cp = new ContentPresenter
+ {
+ Content = GetContent(item),
+ ContentTemplate = ContentTemplate,
+ ContentTemplateSelector = ContentTemplateSelector,
+ ContentStringFormat = ContentStringFormat,
+ Visibility = Visibility.Collapsed,
+ };
+ _itemsHolder.Children.Add(cp);
+ }
+
+ ///
+ /// Find the CP for the given object. data could be a TabItem or a piece of data
+ ///
+ ///
+ ///
+ private ContentPresenter FindChildContentPresenter(object data)
+ {
+ if (data is TabItem)
+ data = ((TabItem) data).Content;
+
+ return data == null
+ ? null
+ : _itemsHolder?.Children.Cast().FirstOrDefault(cp => cp.Content == data);
+ }
+
+ private void ItemContainerGeneratorOnStatusChanged(object sender, EventArgs eventArgs)
+ {
+ MarkWrappedTabItems();
+ MarkInitialSelection();
+ }
+
+ private static void CloseItem(DragablzItem item, TabablzControl owner)
+ {
+ if (item == null)
+ throw new ApplicationException("Valid DragablzItem to close is required.");
+
+ if (owner == null)
+ throw new ApplicationException("Valid TabablzControl container is required.");
+
+ if (!owner.IsMyItem(item))
+ throw new ApplicationException("TabablzControl container must be an owner of the DragablzItem to close");
+
+ var cancel = false;
+ if (owner.ClosingItemCallback != null)
+ {
+ var callbackArgs = new ItemActionCallbackArgs(Window.GetWindow(owner), owner, item);
+ owner.ClosingItemCallback(callbackArgs);
+ cancel = callbackArgs.IsCancelled;
+ }
+
+ if (!cancel)
+ owner.RemoveItem(item);
+ }
+
+ private static void CloseItemCanExecuteClassHandler(object sender, CanExecuteRoutedEventArgs e)
+ {
+ e.CanExecute = FindOwner(e.Parameter, e.OriginalSource) != null;
+ }
+ private static void CloseItemClassHandler(object sender, ExecutedRoutedEventArgs e)
+ {
+ var owner = FindOwner(e.Parameter, e.OriginalSource);
+
+ if (owner == null) throw new ApplicationException("Unable to ascertain DragablzItem to close.");
+
+ CloseItem(owner.Item1, owner.Item2);
+ }
+
+ private static Tuple FindOwner(object eventParameter, object eventOriginalSource)
+ {
+ var dragablzItem = eventParameter as DragablzItem;
+ if (dragablzItem == null)
+ {
+ var dependencyObject = eventOriginalSource as DependencyObject;
+ dragablzItem = dependencyObject.VisualTreeAncestory().OfType().FirstOrDefault();
+ if (dragablzItem == null)
+ {
+ var popup = dependencyObject.LogicalTreeAncestory().OfType().LastOrDefault();
+ if (popup?.PlacementTarget != null)
+ {
+ dragablzItem = popup.PlacementTarget.VisualTreeAncestory().OfType().FirstOrDefault();
+ }
+ }
+ }
+
+ if (dragablzItem == null) return null;
+
+ var tabablzControl = LoadedInstances.FirstOrDefault(tc => tc.IsMyItem(dragablzItem));
+
+ return tabablzControl == null ? null : new Tuple(dragablzItem, tabablzControl);
+ }
+
+ private void AddItemHandler(object sender, ExecutedRoutedEventArgs e)
+ {
+ if (NewItemFactory == null)
+ throw new InvalidOperationException("NewItemFactory must be provided.");
+
+ var newItem = NewItemFactory();
+ if (newItem == null) throw new ApplicationException("NewItemFactory returned null.");
+
+ AddToSource(newItem);
+ SelectedItem = newItem;
+
+ Dispatcher.BeginInvoke(new Action(_dragablzItemsControl.InvalidateMeasure), DispatcherPriority.Loaded);
+ }
+
+ private void PrepareChildContainerForItemOverride(DependencyObject dependencyObject, object o)
+ {
+ var dragablzItem = dependencyObject as DragablzItem;
+ if (dragablzItem != null && HeaderMemberPath != null)
+ {
+ var contentBinding = new Binding(HeaderMemberPath) { Source = o };
+ dragablzItem.SetBinding(ContentControl.ContentProperty, contentBinding);
+ dragablzItem.UnderlyingContent = o;
+ }
+
+ SetIsWrappingTabItem(dependencyObject, o is TabItem);
+ }
+ }
+}