private void Commit() { if (_layoutChanged) { if (IsDraggingDockTabItems) { // Dragging ended in DockTabPanel. RestoreFloatWindowPosition(); } else if (_floatWindow != null) { // Dragging ended outside of a DockTabPanel. Check the dock indicators to find the // desired target position. IDockPane target = null; DockPosition position = DockPosition.None; if (HasResult(_borderDockIndicators)) { target = _dockStrategy.DockControl.RootPane; position = _borderDockIndicators.Result; } else if (HasResult(_paneDockIndicators)) { target = DockHelper.GetViewModel <IDockPane>(_paneDockIndicators.Target); position = _paneDockIndicators.Result; } if (position != DockPosition.None && target != null) { // User has dropped FloatWindow on a DockIndicator. // --> Dock content. var floatWindowVM = _floatWindow.GetViewModel(); foreach (var item in _draggedItems) { DockHelper.Remove(floatWindowVM, item); item.DockState = DockState.Hide; } var dockTabPane = _dockStrategy.CreateDockTabPane(_draggedItems[0], DockState.Hide); for (int i = 1; i < _draggedItems.Count; i++) { dockTabPane.Items.Add(_draggedItems[i]); } _dockStrategy.Dock(dockTabPane, target, position); RestoreFloatWindowPosition(); } else { // The final state is DockState.Float. if (!_canFloat && _originalDockState != DockState.Float) { // DockState.Float is not allowed. Rollback(); return; } } } } // Keep the items at their new position. // --> Remove the item proxies. var dockState = _draggedItems[0].DockState; RemoveItemProxies(dockState); if (_layoutChanged && dockState == DockState.Dock) { // The position within the DockControl may have changed. The assignment to the // auto-hide bar is no longer valid. // --> Also remove item proxies from auto-hide bars. RemoveItemProxies(DockState.AutoHide); } // Restore the original dock state of the dragged items. RestoreItemsFromProxies(); }
private void DragDockTabItemsIntoFloatWindow() { Debug.Assert(_floatWindow == null); if (_targetDockTabPane != null) { ClearTranslateTransform(); _targetDockTabPane.PreviewKeyDown -= OnPreviewKeyDown; _targetDockTabPane = null; } _dockControl.LostMouseCapture -= OnLostMouseCapture; _dockControl.MouseLeftButtonUp -= OnMouseLeftButtonUp; _dockControl.MouseMove -= OnMouseMove; _dockControl.PreviewKeyDown -= OnPreviewKeyDown; _dockControl.ReleaseMouseCapture(); // Remove the dragged items from their current location. foreach (var item in _draggedItems) { DockHelper.Remove(_dockStrategy.DockControl.RootPane, item); foreach (var floatWindow in _dockStrategy.DockControl.FloatWindows) { DockHelper.Remove(floatWindow, item); } DockHelper.Remove(_dockStrategy.DockControl.AutoHideLeft, item); DockHelper.Remove(_dockStrategy.DockControl.AutoHideRight, item); DockHelper.Remove(_dockStrategy.DockControl.AutoHideTop, item); DockHelper.Remove(_dockStrategy.DockControl.AutoHideBottom, item); } // Move items into a new FloatWindow. foreach (var item in _draggedItems) { item.DockState = DockState.Float; } var newPaneVM = _dockStrategy.CreateDockTabPane(_draggedItems[0], DockState.Float); for (int i = 1; i < _draggedItems.Count; i++) { newPaneVM.Items.Add(_draggedItems[i]); } var floatWindowVM = _dockStrategy.CreateFloatWindow(); floatWindowVM.RootPane = newPaneVM; _dockStrategy.DockControl.FloatWindows.Add(floatWindowVM); _dockStrategy.Activate(_activeItem); _dockStrategy.Cleanup(); // Get the newly created FloatWindow (view) from the DockControl. _floatWindow = GetFloatWindow(floatWindowVM); Debug.Assert(_floatWindow != null); LimitFloatWindowSize(_floatWindow, _initialSize); // Limit mouse offset to FloatWindow size. double actualWidth = _floatWindow.ActualWidth; if (actualWidth > 0) { _mouseOffset.X = Math.Min(_mouseOffset.X, actualWidth / 2); } Point position = GetFloatWindowPosition(); _floatWindow.Left = position.X; _floatWindow.Top = position.Y; // Wait until FloatWindow is loaded, initiate the Win32 move window loop. _floatWindow.Dispatcher.BeginInvoke(new Action(() => { if (_floatWindow != null) { LimitFloatWindowSize(_floatWindow, _initialSize); // Limit mouse offset to FloatWindow size. _mouseOffset.X = Math.Min(_mouseOffset.X, _floatWindow.ActualWidth / 2); _mouseOffset.Y = Math.Max(_mouseOffset.Y, 8); Point pos = GetFloatWindowPosition(); _floatWindow.Left = pos.X; _floatWindow.Top = pos.Y; if (Mouse.LeftButton == MouseButtonState.Pressed) { _floatWindow.DragMove(); } else { EndDrag(true); } } }), DispatcherPriority.Loaded); // Important: Action needs to be invoked before input. }
/// <summary> /// Tries to move the dragged items from the <see cref="FloatWindow"/> into a /// <see cref="DockTabPanel"/>. /// </summary> private void DragFloatWindowIntoDockTabPanel() { Debug.Assert(_targetDockTabPane == null); Debug.Assert(_floatWindow != null); var dockTabPane = GetTargetPane() as DockTabPane; if (dockTabPane == null) { return; // No DockTabPane (view). } var dockTabPaneVM = dockTabPane.GetViewModel(); if (dockTabPaneVM == null) { return; // No IDockTabPane (view-model). } if (GetDockTabItemAtMouse(dockTabPane) == null) { return; // No DockTabItem hit. } if (!CanDock(dockTabPaneVM, DockPosition.Inside)) { return; // Docking not allowed. } // Remove currently dragged FloatWindow. var floatWindowVM = _floatWindow.GetViewModel(); foreach (var item in _draggedItems) { DockHelper.Remove(floatWindowVM, item); } _floatWindow = null; Win32.ReleaseCapture(); // Exit Win32 move window loop. // Add items into target DockTabPane. _targetDockTabPane = dockTabPane; foreach (var item in _draggedItems) { item.DockState = dockTabPaneVM.DockState; dockTabPaneVM.Items.Add(item); } // Make sure the current item is selected in DockTabPane. _dockStrategy.Activate(_activeItem); // When the Win32 move window loop exits, the DockControl receives a LostMouseCapture // event. --> Defer dragging of the DockTabItems. _dockControl.Dispatcher.BeginInvoke(new Action(() => { if (!_dockControl.IsMouseCaptured) { if (!_dockControl.CaptureMouse()) { // Failed to capture the mouse. EndDrag(false); return; } _dockControl.LostMouseCapture += OnLostMouseCapture; _dockControl.MouseMove += OnMouseMove; _dockControl.MouseLeftButtonUp += OnMouseLeftButtonUp; _dockControl.PreviewKeyDown += OnPreviewKeyDown; _targetDockTabPane.PreviewKeyDown += OnPreviewKeyDown; DragDockTabItems(); } })); HideBorderDockIndicators(); HidePaneIndicators(); _dockControl.UpdateFloatWindows(); _layoutChanged = true; }
public static void Load(IDockControl dockControl, XElement storedLayout /*, bool keepDockTabItems */) { ///// <param name="keepDockTabItems"> ///// If set to <see langword="true"/> all existing <see cref="IDockTabItem"/>s are kept. If ///// set to <see langword="false"/>, all current <see cref="IDockTabItem"/>s are closed ///// before the new layout is loaded. ///// </param> if (dockControl == null) { throw new ArgumentNullException(nameof(dockControl)); } if (storedLayout == null) { throw new ArgumentNullException(nameof(storedLayout)); } var dockStrategy = dockControl.DockStrategy; if (dockStrategy == null) { throw new ArgumentException("The IDockControl does not have a DockStrategy."); } const bool keepNonPersistentItems = true; // Non-persistent items are usually documents and should be kept. const bool keepPersistentItems = false; // Persistent items are usually tool windows. // Remember current dock items (visible and hidden items). var items = dockControl.GetDockElements() .OfType <IDockTabItem>() .Distinct() .ToArray(); // Validate DockIds. foreach (var item in items) { if (item.DockId == null) { throw new DockException("Could not load docking layout. IDockTabItem does not have a valid DockId."); } } // Check for duplicate DockIds. bool duplicateDockIds = items.GroupBy(item => item.DockId) .Select(group => group.Count()) .Any(count => count > 1); if (duplicateDockIds) { throw new DockException("Could not load docking layout. Two or more IDockTabItems have the same DockId."); } var context = new DeserializationContext { DockControl = dockControl }; dockStrategy.Begin(); try { // Remember current IDockTabItems. // context.OldItems stores all (because OldItems is used in LoadDockTabItem). // Another list stores only the old items which are visible. var oldVisibleItems = new List <IDockTabItem>(); foreach (var item in items) { context.OldItems[item.DockId] = item; if (item.DockState != DockState.Hide) { oldVisibleItems.Add(item); } } // Load IDockTabItems. Do not add to the dock control yet. foreach (var itemXElement in storedLayout.Elements("DockTabItems").Elements()) { var item = LoadDockTabItem(context, itemXElement, onlyReference: false); if (item != null) { context.NewItems[item.DockId] = item; } } // Try to close all IDockTabItems which we will not keep or which will be hidden. // We keep a list of items which we need to keep open. var oldItemsToShow = new List <IDockTabItem>(); foreach (var oldDockTabItem in oldVisibleItems) { if (context.NewItems.ContainsKey(oldDockTabItem.DockId) && oldDockTabItem.DockState != DockState.Hide) { // Item remains in the layout and visible. continue; } // Previously visible item is removed from layout or hidden. // Try to close it. Items that cannot be closed will be shown at the end. bool closed = false; if ((oldDockTabItem.IsPersistent && !keepPersistentItems) || (!oldDockTabItem.IsPersistent && !keepNonPersistentItems)) { if (dockStrategy.CanClose(oldDockTabItem)) { // Successfully closed. dockStrategy.Close(oldDockTabItem); closed = true; } } if (!closed) { oldItemsToShow.Add(oldDockTabItem); } } // The DockControl still contains the old layout with the previous IDockPane // view model. The old view models are still attached to the DockControl and // react to changes, which may cause the wrong screen conduction. // --> Detach IDockTabItems from old layout. foreach (var item in items) { DockHelper.Remove(dockControl, item); } dockControl.FloatWindows.Clear(); dockControl.AutoHideLeft.Clear(); dockControl.AutoHideRight.Clear(); dockControl.AutoHideTop.Clear(); dockControl.AutoHideBottom.Clear(); var isLocked = (bool?)context.Checked(storedLayout.Attribute("IsLocked")); if (isLocked != null) { dockControl.IsLocked = isLocked.Value; } // Load float windows. { foreach (var xElement in storedLayout.Elements("FloatWindows").Elements()) { var floatWindow = LoadFloatWindow(context, xElement); if (floatWindow != null) { dockControl.FloatWindows.Add(floatWindow); } } } // Load auto-hide panes. { var autoHideBars = new[] { new { Bar = dockControl.AutoHideLeft, Name = "AutoHideLeft" }, new { Bar = dockControl.AutoHideRight, Name = "AutoHideRight" }, new { Bar = dockControl.AutoHideTop, Name = "AutoHideTop" }, new { Bar = dockControl.AutoHideBottom, Name = "AutoHideBottom" }, }; foreach (var bar in autoHideBars) { foreach (var xElement in storedLayout.Elements(bar.Name).Elements()) { var dockPane = LoadDockPane(context, xElement); if (dockPane != null) { bar.Bar.Add((IDockTabPane)dockPane); } } } } // Load root pane. (We do this after loading the float windows and auto-hide bars, // because the dock strategy might want to activate an item in a float window or // auto-hide bar). dockControl.RootPane = LoadDockPane(context, storedLayout.Elements("RootPane").Elements().FirstOrDefault()); // Run cleanup to update IsVisible flags. Those should be up-to-date before calling // Show() to find good default dock target positions. dockStrategy.Cleanup(); // This is not done in cleanup if we are inside Begin()/End(). dockControl.ActiveDockTabPane = null; dockControl.ActiveDockTabItem = null; // Show all old items that are not visible in the loaded layout but could not be closed. foreach (var item in oldItemsToShow) { dockStrategy.Show(item); } // Activate item. if (context.ActiveItem != null) { dockStrategy.Show(context.ActiveItem); } context.ResetLineInfo(); } catch (Exception exception) { var message = "Could not load docking layout."; if (context.LineInfo != null && context.LineInfo.HasLineInfo()) { message += Invariant($" Error at line {context.LineInfo.LineNumber}, column {context.LineInfo.LinePosition}."); } message += " See inner exception for more details."; throw new DockException(message, exception); } finally { dockStrategy.End(); } }