void IActivationSite.TunnelActivation() { if (this.lastActiveChildSite == null) { if (this.host != null) { // We need to pick one. Pick the biggest one by area. var slotTable = new Dictionary <string, ActivatableTabControl>(); foreach (var child in this.host.Children.OfType <ActivatableTabControl>()) { slotTable[SlotPanel.GetSlotName(child)] = child; } var pair = slotTable.OrderBy(kvp => kvp.Value.ActualHeight * kvp.Value.ActualWidth).FirstOrDefault(); this.lastActiveChildSite = pair.Value; } } if (this.lastActiveChildSite != null) { this.lastActiveChildSite.TunnelActivation(); } }
void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { parent = this.FindParent <SlotPanel>(); if (parent != null) { e.MouseDevice.Capture(this); startPoint = e.GetPosition(parent); slot1ActualSizeStart = Slot1.ActualSize; slot2ActualSizeStart = Slot2.ActualSize; slot1LengthStart = Slot1.Length; slot2LengthStart = Slot2.Length; uv = SlotPanel.UVHelper.CreateInstance(this.SizeDirection); double minU1 = Math.Max(Slot1.MinLength, parent.SlotSpacing); double minU2 = Math.Max(Slot2.MinLength, parent.SlotSpacing); double maxU1 = Slot1.MaxLength; double maxU2 = Slot2.MaxLength; double minU = Math.Max(uv.U(startPoint) - uv.U(slot1ActualSizeStart) + minU1, uv.U(startPoint) + uv.U(slot2ActualSizeStart) - maxU2); double maxU = Math.Min(uv.U(startPoint) + uv.U(slot2ActualSizeStart) - minU2, uv.U(startPoint) - uv.U(slot1ActualSizeStart) + maxU1); minPoint = uv.Point(minU, 0); maxPoint = uv.Point(maxU, 0); this.MouseMove += OnMouseMove; this.MouseLeftButtonUp += OnMouseLeftButtonUp; this.LostMouseCapture += OnLostMouseCapture; } e.Handled = true; }
public override void OnApplyTemplate() { base.OnApplyTemplate(); this.host = this.Template.FindName("PART_Host", this) as SlotPanel; if (this.host == null) { throw new InvalidOperationException(); // This is a style authoring issue; make sure the template has the appropriate part in it } if (this.rootTabNode == null) { // If our template is applied before we're asked to load state, then we create a default state. // This is wasteful if state actually gets loaded... so be sure to load any state before this control is visualized. this.rootTabNode = new TabNode { Slot = new Slot { Name = GetNextSlotName() }, TabControl = new ActivatableTabControl() }; this.rootTabNode.TabControl.ParentSite = this; SlotPanel.SetSlotName(this.rootTabNode.TabControl, this.rootTabNode.Slot.Name); this.activeTabNode = this.rootTabNode; this.activeTabNode.TabControl.IsActive = true; this.rootTabNode.TabControl.TabNode = this.rootTabNode; this.host.Children.Add(this.rootTabNode.TabControl); } else { // Walk the tree and add all tab controls to the host AddTabControls(this.rootTabNode); } this.host.SlotDefinition = this.rootTabNode.Slot; }
public override void OnApplyTemplate() { base.OnApplyTemplate(); this.host = this.Template.FindName("PART_Host", this) as SlotPanel; if (this.host == null) throw new InvalidOperationException(); // This is a style authoring issue; make sure the template has the appropriate part in it if (this.rootTabNode == null) { // If our template is applied before we're asked to load state, then we create a default state. // This is wasteful if state actually gets loaded... so be sure to load any state before this control is visualized. this.rootTabNode = new TabNode { Slot = new Slot { Name = GetNextSlotName() }, TabControl = new ActivatableTabControl() }; this.rootTabNode.TabControl.ParentSite = this; SlotPanel.SetSlotName(this.rootTabNode.TabControl, this.rootTabNode.Slot.Name); this.activeTabNode = this.rootTabNode; this.activeTabNode.TabControl.IsActive = true; this.rootTabNode.TabControl.TabNode = this.rootTabNode; this.host.Children.Add(this.rootTabNode.TabControl); } else { // Walk the tree and add all tab controls to the host AddTabControls(this.rootTabNode); } this.host.SlotDefinition = this.rootTabNode.Slot; }
public override void OnApplyTemplate() { base.OnApplyTemplate(); this.slotPanel = GetTemplateChild("PART_SlotPanel") as SlotPanel; this.slotPanel.SetBinding(SlotPanel.SlotDefinitionProperty, new Binding { Source = this, Path = new PropertyPath(SlotDefinitionProperty) }); this.LayoutInstance.EnsureSlotContentPopulation(); }
public bool AddSlotPanelChild(FrameworkElement child, string slotName) { if (this.slotPanel != null) { SlotPanel.SetSlotName(child, slotName); this.slotPanel.Children.Add(child); return(true); } return(false); }
void UpdateSlotTable() { if (slotTable == null || !slotStructureValid) { slotTable = new Dictionary <string, SlotData>(); topSlotData.Slot.Children.Clear(); if (SlotDefinition != null) { topSlotData.Slot.Children.Add(SlotDefinition); AddSlotToTable(SlotDefinition); } foreach (UIElement element in InternalChildren) { if (element != null) { string name = SlotPanel.GetSlotName(element) ?? ""; SlotData data; if (!slotTable.TryGetValue(name, out data)) { data = topSlotData; } if (data.Elements == null) { data.Elements = new List <UIElement>(); } data.Elements.Add(element); } } topSlotData.Slot.ElementCount = (topSlotData.Elements != null) ? topSlotData.Elements.Count : 0; foreach (var slotData in slotTable.Values) { slotData.Slot.ElementCount = (slotData.Elements != null) ? slotData.Elements.Count : 0; } slotStructureValid = true; } }
public TabNode SplitNode(TabNode nodeToSplit, Dock newSideDock) { Orientation orientation = (newSideDock == Dock.Left || newSideDock == Dock.Right) ? Orientation.Horizontal : Orientation.Vertical; TabNode newNode = new TabNode { Slot = new Slot { Name = GetNextSlotName() }, TabControl = new ActivatableTabControl() }; SlotPanel.SetSlotName(newNode.TabControl, newNode.Slot.Name); host.Children.Add(newNode.TabControl); newNode.TabControl.TabNode = newNode; // There are different ways a node can be "split". // 1: The node HAS a parent node with the same orientation as the requested split. The new node is inserted into the parent node's children. // 2: The node IS a parent node with the same orientation as the requested split. The new node is appended/prepended to this node's children. // 3: The node "splits", one child being the new node and the other having the original tab control / children. if (nodeToSplit.Parent != null && nodeToSplit.Parent.Slot.Orientation == orientation) { // This is case 1. Insert the new node into the parent, either before or after this node based on dock. double newSize = nodeToSplit.Parent.Children.Average(n => n.Slot.Length.Value); newNode.Slot.Length = new GridLength(newSize, GridUnitType.Star); int index = nodeToSplit.Parent.Children.IndexOf(nodeToSplit); if (newSideDock == Dock.Right || newSideDock == Dock.Bottom) { index += 1; } nodeToSplit.Parent.Children.Insert(index, newNode); nodeToSplit.Parent.Slot.Children.Insert(index, newNode.Slot); newNode.Parent = nodeToSplit.Parent; } else if (nodeToSplit.Children != null && nodeToSplit.Slot.Orientation == orientation) { double newSize = nodeToSplit.Children.Average(n => n.Slot.Length.Value); newNode.Slot.Length = new GridLength(newSize, GridUnitType.Star); // This is case 2. Insert the new node into this node (at the beginning or end based on dock). if (newSideDock == Dock.Left || newSideDock == Dock.Top) { nodeToSplit.Children.Insert(0, newNode); nodeToSplit.Slot.Children.Insert(0, newNode.Slot); } else { nodeToSplit.Children.Add(newNode); nodeToSplit.Slot.Children.Add(newNode.Slot); } newNode.Parent = nodeToSplit; } else { // This is case 3. Create another new node containing nodeToSplit's content, and make nodeToSplit contain both new nodes. TabNode otherNewNode = new TabNode { Slot = new Slot { Name = GetNextSlotName() } }; if (nodeToSplit.Children != null) { otherNewNode.Children = nodeToSplit.Children; foreach (var child in otherNewNode.Children) { child.Parent = otherNewNode; } nodeToSplit.Children = null; foreach (var slot in nodeToSplit.Slot.Children) { otherNewNode.Slot.Children.Add(slot); } nodeToSplit.Slot.Children.Clear(); otherNewNode.Slot.Orientation = nodeToSplit.Slot.Orientation; } else { otherNewNode.TabControl = nodeToSplit.TabControl; otherNewNode.TabControl.TabNode = otherNewNode; SlotPanel.SetSlotName(otherNewNode.TabControl, otherNewNode.Slot.Name); } nodeToSplit.Children = new List <TabNode>(2); nodeToSplit.Slot.Orientation = orientation; if (newSideDock == Dock.Left || newSideDock == Dock.Top) { nodeToSplit.Slot.Children.Add(newNode.Slot); nodeToSplit.Children.Add(newNode); nodeToSplit.Slot.Children.Add(otherNewNode.Slot); nodeToSplit.Children.Add(otherNewNode); } else { nodeToSplit.Slot.Children.Add(otherNewNode.Slot); nodeToSplit.Children.Add(otherNewNode); nodeToSplit.Slot.Children.Add(newNode.Slot); nodeToSplit.Children.Add(newNode); } newNode.Parent = otherNewNode.Parent = nodeToSplit; } return(newNode); }
void CreateNewTabGroup(Orientation orientation) { TabNode newTab = new TabNode { Slot = new Slot { Name = GetNextSlotName() }, TabControl = new ActivatableTabControl() }; ActivatableTabControl originTabControl = this.activeTabNode.TabControl; TabNode nodeToSplit; double starSize; int newIndex; SlotPanel.SetSlotName(newTab.TabControl, newTab.Slot.Name); host.Children.Add(newTab.TabControl); newTab.TabControl.TabNode = newTab; if (this.activeTabNode.Parent != null && this.activeTabNode.Parent.Slot.Orientation == orientation) { // We're further splitting the parent in the same orientation, so we are only adding the one node nodeToSplit = this.activeTabNode.Parent; newTab.Parent = this.activeTabNode.Parent; newIndex = nodeToSplit.Children.IndexOf(this.activeTabNode) + 1; starSize = 0d; foreach (var d in nodeToSplit.Slot.Children) { starSize += d.Length.Value; } starSize = starSize / nodeToSplit.Slot.Children.Count; // The currently active tab doesn't get altered, but the parent gets a new child. nodeToSplit.Children.Insert(newIndex, newTab); nodeToSplit.Slot.Children.Insert(newIndex, newTab.Slot); } else { // This is a new split of the active node, so we need to add a pair of child nodes TabNode top = new TabNode { Parent = this.activeTabNode, Slot = new Slot { Name = GetNextSlotName() }, TabControl = originTabControl }; nodeToSplit = this.activeTabNode; newTab.Parent = this.activeTabNode; SlotPanel.SetSlotName(top.TabControl, top.Slot.Name); top.TabControl.TabNode = top; nodeToSplit.Slot.Children.Add(top.Slot); nodeToSplit.Slot.Children.Add(newTab.Slot); nodeToSplit.Slot.Orientation = orientation; } // Move the active tab to the newly created tab control var item = originTabControl.SelectedItem as ActivatableTabItem; if (item != null) { if (!item.IsKeyboardFocusWithin) { item.Focus(); } var focusedElement = Keyboard.FocusedElement as FrameworkElement; originTabControl.Items.Remove(item); newTab.TabControl.Items.Add(item); // This may not work, depending on template behavior (recreation of elements?) if (focusedElement != null) { RoutedEventHandler reacquireFocus = null; reacquireFocus = (s, e) => { focusedElement.Focus(); focusedElement.Loaded -= reacquireFocus; }; focusedElement.Loaded += reacquireFocus; } } else { Debug.Fail("Only ActivatableTabItem objects should be added to ActivatableTabControls"); } ActivateTabNode(newTab); }
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); }