// NOTE: This method is static, because the actual SplitTabsControl that created the view may not be the one in which // that view lives at the time it is closed. Being static forces the method to use the sender to obtain the appropriate // parent control. static void OnViewClosed(object sender, EventArgs e) { View view = sender as View; if (view == null) { return; } ActivatableTabItem item = ActivatableTabItem.GetTabItem(view); ActivatableTabControl tabControl = item.TabControlParent; SplitTabsControl splitTabsControl = tabControl.FindParent <SplitTabsControl>(); TabNode node = tabControl.TabNode; bool switchFocus = tabControl.IsKeyboardFocusWithin; splitTabsControl.RemoveItemFromControl(item); if (switchFocus) { if (node.TabControl.SelectedItem is ActivatableTabItem) { ((ActivatableTabItem)node.TabControl.SelectedItem).Focus(); } else if (node == splitTabsControl.activeTabNode) { // We just closed the last view in the active node. Need to switch the active // tab node to the last active one. splitTabsControl.ActivateLastActiveTabNode(); } } view.Closed -= OnViewClosed; }
ActivatableTabItem CreateTabItem(IViewCreationCommand viewCreator, IServiceProvider serviceProvider) { View view = viewCreator.CreateView(serviceProvider); view.Title = viewCreator.DisplayName; var item = new ActivatableTabItem() { ViewCreator = viewCreator, View = view }; ActivatableTabItem.SetTabItem(view, item); view.Site = item; item.SetBinding(ActivatableTabItem.ContentProperty, new Binding { Source = view, Path = new PropertyPath("ViewContent") }); item.SetBinding(ActivatableTabItem.HeaderProperty, new Binding { Source = view, Path = new PropertyPath("Title") }); AutomationProperties.SetAutomationId(item, viewCreator.RegisteredName); view.Closed += OnViewClosed; return(item); }
public void NotifyActivation(object child) { var tabItem = child as ActivatableTabItem; if (tabItem != null) { this.lastActiveChildSite = tabItem; this.ParentSite.NotifyActivation(this); } }
public void BubbleActivation(object child) { var tabItem = child as ActivatableTabItem; if (tabItem != null && this.Items.Contains(tabItem)) { this.lastActiveChildSite = tabItem; this.SelectedIndex = this.Items.IndexOf(tabItem); this.ParentSite.BubbleActivation(this); } }
public ActivatableTabItem CreateView(IViewCreationCommand viewCreator, IServiceProvider serviceProvider, TabNode destinationNode = null) { ActivatableTabItem item = CreateTabItem(viewCreator, serviceProvider); if (destinationNode == null) { destinationNode = this.activeTabNode; } AddItemToControl(item, destinationNode.TabControl, true); return(item); }
public void TunnelActivation() { if (this.lastActiveChildSite == null) { this.lastActiveChildSite = this.SelectedItem as ActivatableTabItem; } if (this.lastActiveChildSite != null) { this.lastActiveChildSite.TunnelActivation(); } }
private void OnMouseLeftButtonDown(ActivatableTabItem item, MouseButtonEventArgs e) { e.MouseDevice.Capture(item); this.draggedItem = item; this.sourceControl = item.FindParent<ActivatableTabControl>(); this.sourceSplitTabsControl = this.sourceControl.FindParent<SplitTabsControl>(); this.offset = e.GetPosition(item); this.preMoving = true; item.LostMouseCapture += this.OnLostMouseCapture; item.MouseLeftButtonUp += this.OnMouseLeftButtonUp; item.MouseMove += this.OnMouseMove; }
private void OnMouseLeftButtonDown(ActivatableTabItem item, MouseButtonEventArgs e) { e.MouseDevice.Capture(item); this.draggedItem = item; this.sourceControl = item.FindParent <ActivatableTabControl>(); this.sourceSplitTabsControl = this.sourceControl.FindParent <SplitTabsControl>(); this.offset = e.GetPosition(item); this.preMoving = true; item.LostMouseCapture += this.OnLostMouseCapture; item.MouseLeftButtonUp += this.OnMouseLeftButtonUp; item.MouseMove += this.OnMouseMove; }
void OnTabClosed(object sender, RoutedEventArgs e) { ActivatableTabItem item = e.OriginalSource as ActivatableTabItem; if (item != null) { var view = item.View; if (view != null) { // Note that "tab closed" is different than "view closed" // This allows programmatic closing of a view by calling Close on the actual View // object, and having it have the same effect as clicking the close box on a tab. view.Close(); } } }
public void AddItemToControl(ActivatableTabItem item, ActivatableTabControl tabControl, bool giveFocus) { Debug.Assert(tabControl.FindParent <SplitTabsControl>() == this, "Can't add item to a tab control not owned by this split tabs control!"); if (giveFocus) { RoutedEventHandler loadedHandler = null; loadedHandler = (s, e2) => { item.Focus(); item.Loaded -= loadedHandler; }; item.Loaded += loadedHandler; } tabControl.ParentSite = this; tabControl.Items.Add(item); }
XElement CreateTabElement(ActivatableTabItem item) { var element = new XElement("Tab", new XAttribute("RegisteredName", item.ViewCreator.RegisteredName), new XAttribute("IsSelected", item.IsSelected)); var view = item.View; if (view != null) { var state = view.GetViewState(); if (state != null) { element.Add(new XElement("PrivateState", view.GetViewState())); } } return(element); }
void OnTabActivated(object sender, RoutedEventArgs e) { ActivatableTabItem item = e.OriginalSource as ActivatableTabItem; if (item == null) { return; } if (item.View != null) { item.View.HandleActivation(); } TabNode node = item.TabControlParent.TabNode; if (node != null) { ActivateTabNode(node); } }
public static void SetTabItem(DependencyObject obj, ActivatableTabItem value) { obj.SetValue(TabItemProperty, value); }
XElement CreateTabElement(ActivatableTabItem item) { var element = new XElement("Tab", new XAttribute("RegisteredName", item.ViewCreator.RegisteredName), new XAttribute("IsSelected", item.IsSelected)); var view = item.View; if (view != null) { var state = view.GetViewState(); if (state != null) { element.Add(new XElement("PrivateState", view.GetViewState())); } } return element; }
ActivatableTabItem CreateTabItem(IViewCreationCommand viewCreator, IServiceProvider serviceProvider) { View view = viewCreator.CreateView(serviceProvider); view.Title = viewCreator.DisplayName; var item = new ActivatableTabItem() { ViewCreator = viewCreator, View = view }; ActivatableTabItem.SetTabItem(view, item); view.Site = item; item.SetBinding(ActivatableTabItem.ContentProperty, new Binding { Source = view, Path = new PropertyPath("ViewContent") }); item.SetBinding(ActivatableTabItem.HeaderProperty, new Binding { Source = view, Path = new PropertyPath("Title") }); AutomationProperties.SetAutomationId(item, viewCreator.RegisteredName); view.Closed += OnViewClosed; return item; }
public void RemoveItemFromControl(ActivatableTabItem item) { var control = item.FindParent <ActivatableTabControl>(); if (control == null) { Debug.Fail("Can't find parent tab control!"); return; } var node = control.TabNode; if (node == null) { Debug.Fail("Tab control does not have a tab node set!"); return; } node.TabControl.Items.Remove(item); if (node.TabControl.Items.Count == 0) { bool wasActive = (node == this.activeTabNode); // No more tabs in this control. if (node.Parent != null) { // This node is no longer needed. Remove the tab control and fix the tree. this.host.Children.Remove(node.TabControl); node.Parent.Children.Remove(node); node.Parent.Slot.Children.Remove(node.Slot); TabNode lastChild = node.Parent.Children[0]; if (node.Parent.Children.Count == 1) { // The parent now only has a single child. That means the last node (lastChild) is no longer // necessary; its contents should collapse up into the parent. Note that this could "bleed up" // into the grandparent, as in this case: // // ------------------------ // | | | | | // | | | | | // | | | | | // | |-------| | // | | | | // | | X | | // | | | | // ------------------------ // // Closing the X node leaves a single node in its parent, so that node replaces the parent. // That node contains two children, and has horizontal orientation like it's *new* parent // (the grandparent). In these cases, the parent node also becomes unnecessary, and should // be replaced in the grandparent node with all of its children. // // Note that programmatically, it is possible to create a node tree such that node has children // (with grandchildren) that have the same orientation as the parent. However, using the drag // manager, this should never happen; the logic in SplitNode detects such cases and inserts // nodes appropriately such that orientation always flips as you go down the tree. So, any time // the remaining child (lastChild) has children, it should always result in this "bleed up" // effect. But we check just in case. if (lastChild.Children != null) { var grandparent = node.Parent.Parent; if (grandparent != null && lastChild.Slot.Orientation == grandparent.Slot.Orientation) { // Here's the "bleed up" -- skip right past parent int index = grandparent.Children.IndexOf(node.Parent); double totalSizeInLastChild = lastChild.Slot.Children.Sum(s => s.Length.Value); double totalSizeInGrandParent = grandparent.Slot.Children.Sum(s => s.Length.Value); grandparent.Children.RemoveAt(index); grandparent.Slot.Children.RemoveAt(index); foreach (var child in lastChild.Children) { grandparent.Children.Insert(index, child); child.Parent = grandparent; grandparent.Slot.Children.Insert(index, child.Slot); child.Slot.Length = new GridLength((child.Slot.Length.Value / totalSizeInLastChild) * totalSizeInGrandParent, GridUnitType.Star); index += 1; } } else { // Just put lastChild's children into the parent, replacing itself. This case should only // get hit if the parent is the root, unless someone does programmatic manipulation of the slots. node.Parent.Children.Clear(); node.Parent.Slot.Children.Clear(); foreach (var child in lastChild.Children) { node.Parent.Children.Add(child); child.Parent = node.Parent; node.Parent.Slot.Children.Add(child.Slot); } node.Parent.Slot.Orientation = lastChild.Slot.Orientation; } } else { // Just put lastChild's tab control into the parent, removing itself node.Parent.Children = null; node.Parent.Slot.Children.Clear(); node.Parent.TabControl = lastChild.TabControl; SlotPanel.SetSlotName(node.Parent.TabControl, node.Parent.Slot.Name); node.Parent.TabControl.TabNode = node.Parent; } } if (wasActive) { ActivateLastActiveTabNode(); } } else { // We're now empty, completely. If we're a floating host, go away. if (this.masterControl != null) { var parent = this.FindParent <FloatingWindow>(); if (parent != null) { parent.Close(); } } } } }
public void RemoveItemFromControl(ActivatableTabItem item) { var control = item.FindParent<ActivatableTabControl>(); if (control == null) { Debug.Fail("Can't find parent tab control!"); return; } var node = control.TabNode; if (node == null) { Debug.Fail("Tab control does not have a tab node set!"); return; } node.TabControl.Items.Remove(item); if (node.TabControl.Items.Count == 0) { bool wasActive = (node == this.activeTabNode); // No more tabs in this control. if (node.Parent != null) { // This node is no longer needed. Remove the tab control and fix the tree. this.host.Children.Remove(node.TabControl); node.Parent.Children.Remove(node); node.Parent.Slot.Children.Remove(node.Slot); TabNode lastChild = node.Parent.Children[0]; if (node.Parent.Children.Count == 1) { // The parent now only has a single child. That means the last node (lastChild) is no longer // necessary; its contents should collapse up into the parent. Note that this could "bleed up" // into the grandparent, as in this case: // // ------------------------ // | | | | | // | | | | | // | | | | | // | |-------| | // | | | | // | | X | | // | | | | // ------------------------ // // Closing the X node leaves a single node in its parent, so that node replaces the parent. // That node contains two children, and has horizontal orientation like it's *new* parent // (the grandparent). In these cases, the parent node also becomes unnecessary, and should // be replaced in the grandparent node with all of its children. // // Note that programmatically, it is possible to create a node tree such that node has children // (with grandchildren) that have the same orientation as the parent. However, using the drag // manager, this should never happen; the logic in SplitNode detects such cases and inserts // nodes appropriately such that orientation always flips as you go down the tree. So, any time // the remaining child (lastChild) has children, it should always result in this "bleed up" // effect. But we check just in case. if (lastChild.Children != null) { var grandparent = node.Parent.Parent; if (grandparent != null && lastChild.Slot.Orientation == grandparent.Slot.Orientation) { // Here's the "bleed up" -- skip right past parent int index = grandparent.Children.IndexOf(node.Parent); double totalSizeInLastChild = lastChild.Slot.Children.Sum(s => s.Length.Value); double totalSizeInGrandParent = grandparent.Slot.Children.Sum(s => s.Length.Value); grandparent.Children.RemoveAt(index); grandparent.Slot.Children.RemoveAt(index); foreach (var child in lastChild.Children) { grandparent.Children.Insert(index, child); child.Parent = grandparent; grandparent.Slot.Children.Insert(index, child.Slot); child.Slot.Length = new GridLength((child.Slot.Length.Value / totalSizeInLastChild) * totalSizeInGrandParent, GridUnitType.Star); index += 1; } } else { // Just put lastChild's children into the parent, replacing itself. This case should only // get hit if the parent is the root, unless someone does programmatic manipulation of the slots. node.Parent.Children.Clear(); node.Parent.Slot.Children.Clear(); foreach (var child in lastChild.Children) { node.Parent.Children.Add(child); child.Parent = node.Parent; node.Parent.Slot.Children.Add(child.Slot); } node.Parent.Slot.Orientation = lastChild.Slot.Orientation; } } else { // Just put lastChild's tab control into the parent, removing itself node.Parent.Children = null; node.Parent.Slot.Children.Clear(); node.Parent.TabControl = lastChild.TabControl; SlotPanel.SetSlotName(node.Parent.TabControl, node.Parent.Slot.Name); node.Parent.TabControl.TabNode = node.Parent; } } if (wasActive) { ActivateLastActiveTabNode(); } } else { // We're now empty, completely. If we're a floating host, go away. if (this.masterControl != null) { var parent = this.FindParent<FloatingWindow>(); if (parent != null) parent.Close(); } } } }
TabNode LoadTabNode(XElement nodeElement, IDictionary <string, IViewCreationCommand> viewCreators, IServiceProvider serviceProvider) { XElement children = nodeElement.Element("Children"); TabNode node = new TabNode { Slot = new Slot { Name = GetNextSlotName() } }; if (children != null) { node.Slot.Orientation = (Orientation)Enum.Parse(typeof(Orientation), nodeElement.Attribute("Orientation").Value); node.Children = new List <TabNode>(); foreach (var childElement in children.Elements("TabNode")) { TabNode childNode = LoadTabNode(childElement, viewCreators, serviceProvider); double size = double.Parse(childElement.Attribute("Length").Value, CultureInfo.InvariantCulture); childNode.Slot.Length = new GridLength(size, GridUnitType.Star); childNode.Parent = node; node.Children.Add(childNode); node.Slot.Children.Add(childNode.Slot); } } else { XElement tabs = nodeElement.Element("Tabs"); bool isActive = bool.Parse(nodeElement.Attribute("IsActive").Value); node.TabControl = new ActivatableTabControl(); SlotPanel.SetSlotName(node.TabControl, node.Slot.Name); node.TabControl.TabNode = node; foreach (var tab in tabs.Elements("Tab")) { string registeredName = tab.Attribute("RegisteredName").Value; bool isSelected = bool.Parse(tab.Attribute("IsSelected").Value); IViewCreationCommand creator; if (!viewCreators.TryGetValue(registeredName, out creator)) { // If the view is bogus, just skip it. continue; } ActivatableTabItem item = CreateTabItem(creator, serviceProvider); XElement privateState = tab.Element("PrivateState"); if (privateState != null && privateState.HasElements) { item.View.LoadViewState(privateState.Elements().First()); } node.TabControl.Items.Add(item); if (isSelected) { node.TabControl.SelectedItem = item; if (isActive) { this.activeTabNode = node; node.TabControl.IsActive = true; } } } } return(node); }
public static void BeginDrag(ActivatableTabItem item, MouseButtonEventArgs e) { new TabItemDragManager().OnMouseLeftButtonDown(item, e); }
public void AddItemToControl(ActivatableTabItem item, ActivatableTabControl tabControl, bool giveFocus) { Debug.Assert(tabControl.FindParent<SplitTabsControl>() == this, "Can't add item to a tab control not owned by this split tabs control!"); if (giveFocus) { RoutedEventHandler loadedHandler = null; loadedHandler = (s, e2) => { item.Focus(); item.Loaded -= loadedHandler; }; item.Loaded += loadedHandler; } tabControl.ParentSite = this; tabControl.Items.Add(item); }