防止对接 AvalonDock

Prevent docking AvalonDock

是否可以通过使用一些过滤来防止将一个可锚定元素停靠到另一个可锚定元素?例如,在视图模型中,我可以包含一些 属性 doctype。对于一个可锚定元素,doctype 的值将为 "a",而对于另一个可锚定元素,值为 "b"。如果值为 "a" 的可锚定元素无法停靠到具有 doctype "b" 的可锚定元素,我如何使用 属性 doctype 来防止停靠?也许,还有其他解决方案。

我在 LayoutAnchorableFloatingWindowControl.cs

更改了 IOverlayWindowHost.GetDropAreas 方法

第一步是初始化内容类型。

var floatingWindowContentHost = Content as FloatingWindowContentHost;
if (floatingWindowContentHost == null) return _dropAreas;
var rootVisual = floatingWindowContentHost.RootVisual;
Type type = null;

var layoutAnchorableFloatingWindow = draggingWindow.Model as LayoutAnchorableFloatingWindow;
if (layoutAnchorableFloatingWindow != null)
{
    //big part of code for getting type
    var layoutAnchorablePane = layoutAnchorableFloatingWindow.SinglePane as LayoutAnchorablePane;

    if (layoutAnchorablePane != null
        && (layoutAnchorableFloatingWindow.IsSinglePane
        && layoutAnchorablePane.SelectedContent != null))
    {
        var layoutAnchorable = ((LayoutAnchorablePane)layoutAnchorableFloatingWindow.SinglePane).SelectedContent as LayoutAnchorable;
        if (layoutAnchorable != null)
            type = layoutAnchorable.Content;
    }
    else
    {
        var pane = GetLayoutAnchorablePane(layoutAnchorableFloatingWindow.RootPanel);
        if (pane == null || pane.SelectedContent == null) 
            return null;

        var layoutAnchorable = pane.SelectedContent as LayoutAnchorable;
        if (layoutAnchorable != null)
            type = layoutAnchorable.Content;
    }
}

在此之后我只是比较两种类型并将需要的放入 _dropAreas

foreach (var areaHost in rootVisual.FindVisualChildren<LayoutAnchorablePaneControl>())
{
    Type areahostType = null;
    var layoutAnchorablePane = areaHost.Model as LayoutAnchorablePane;
    if (layoutAnchorablePane != null && layoutAnchorablePane.SelectedContent != null)
    {
        var layoutAnchorable = ((LayoutAnchorablePane)areaHost.Model).SelectedContent as LayoutAnchorable;
        if (layoutAnchorable != null)
            areahostType = layoutAnchorable.Content;
    }
    // prevent docking different documents
    if (type != null && areahostType != null && areahostType == type)
    {
        _dropAreas.Add(new DropArea<LayoutAnchorablePaneControl>( areaHost, DropAreaType.AnchorablePane));
    }
}

为了防止停靠到文档区域,需要在DockingManager.cs

处对相同的方法进行更改

为了防止 DropTargets 的视觉效果需要更改 OverlayWindow.cs

修改是正确的IOverlayWindowHost.GetDropAreas,但修改对我来说似乎容易多了。

我在 "LayoutAnchorableFloatingWindowControl.cs" 中删除了这个 foreach:

...
// Remove this foreach
foreach (var areaHost in rootVisual.FindVisualChildren<LayoutDocumentPaneControl>())
{
    _dropAreas.Add(new DropArea<LayoutDocumentPaneControl>(
        areaHost,
        DropAreaType.DocumentPane));
}

return _dropAreas;

并且在 "DockingManager.cs" 中的其他实现中,您必须添加一个 else 原因:

if (!isDraggingDocuments)
{
    _areas.Add(new DropArea<DockingManager>(
        this,
        DropAreaType.DockingManager));

    foreach (var areaHost in this.FindVisualChildren<LayoutAnchorablePaneControl>())
    {
        if (areaHost.Model.Descendents().Any())
        {
            _areas.Add(new DropArea<LayoutAnchorablePaneControl>(
                areaHost,
                DropAreaType.AnchorablePane));
        }
    }
}
// -----> This else is new
else
{ 
    foreach (var areaHost in this.FindVisualChildren<LayoutDocumentPaneControl>())
    {
        _areas.Add(new DropArea<LayoutDocumentPaneControl>(
            areaHost,
            DropAreaType.DocumentPane));
    }

    foreach (var areaHost in this.FindVisualChildren<LayoutDocumentPaneGroupControl>())
    {
        var documentGroupModel = areaHost.Model as LayoutDocumentPaneGroup;
        if (documentGroupModel.Children.Where(c => c.IsVisible).Count() == 0)
        {
            _areas.Add(new DropArea<LayoutDocumentPaneGroupControl>(
                areaHost,
                DropAreaType.DocumentPaneGroup));
        }
    }
}

return _areas;

因此,如果您不拖动 LayoutDocument,请不要将文档窗格控件添加到拖放区域。

就是这样 - 适合我。

在 AvalonDock 3.5(我认为是 3.2)中有一个 LayoutAnchorable 属性 CanDockAsTabbedDocument 可以设置为 false 以防止工具窗口停靠到 DocumentPane.这仅适用于 AvalonDock 3.8 and earlier 中的上下文菜单,而拖放行为仍然存在问题。

但是您可以使用此修复程序来获得 AvalonDock 3.5 的预期拖放行为。 下面的代码扩展了 DockingManager.cs

中的 IOverlayWindowHost.GetDropAreas

此解决方案的关键是 dockAsDocument 布尔变量。它保护 foreach 循环,该循环负责将任何项目插入 MainWindow.

DocumentsPane

一旦在 LayoutFloatingWindowControl 中找到一个 LayoutAnchorable(工具窗口)并配置为 CanDockAsTabbedDocument="False"dockAsDocument 变量就会设置为 false .

IEnumerable<IDropArea> IOverlayWindowHost.GetDropAreas( LayoutFloatingWindowControl draggingWindow )
{
  if( _areas != null )
    return _areas;

  bool isDraggingDocuments = draggingWindow.Model is LayoutDocumentFloatingWindow;

  _areas = new List<IDropArea>();

  if( !isDraggingDocuments )
  {
    _areas.Add( new DropArea<DockingManager>(
        this,
        DropAreaType.DockingManager ) );

    foreach( var areaHost in this.FindVisualChildren<LayoutAnchorablePaneControl>() )
    {
      if( areaHost.Model.Descendents().Any() )
      {
        _areas.Add( new DropArea<LayoutAnchorablePaneControl>(
            areaHost,
            DropAreaType.AnchorablePane ) );
      }
    }
  }

  // Determine if floatingWindow is configured to dock as document or not
  bool dockAsDocument = true;
  if (isDraggingDocuments == false)
  {
    var toolWindow = draggingWindow.Model as LayoutAnchorableFloatingWindow;
    if (toolWindow != null)
    {
      foreach (var item in GetAnchorableInFloatingWindow(draggingWindow))
      {
        if (item.CanDockAsTabbedDocument == false)
        {
          dockAsDocument = false;
          break;
        }
      }
    }
  }

  // Dock only documents and tools in DocumentPane if configuration does allow that
  if (dockAsDocument == true)
  {
    foreach( var areaHost in this.FindVisualChildren<LayoutDocumentPaneControl>() )
    {
      _areas.Add( new DropArea<LayoutDocumentPaneControl>(
          areaHost,
          DropAreaType.DocumentPane ) );
    }
  }

  foreach( var areaHost in this.FindVisualChildren<LayoutDocumentPaneGroupControl>() )
  {
    var documentGroupModel = areaHost.Model as LayoutDocumentPaneGroup;
    if( documentGroupModel.Children.Where( c => c.IsVisible ).Count() == 0 )
    {
      _areas.Add( new DropArea<LayoutDocumentPaneGroupControl>(
          areaHost,
          DropAreaType.DocumentPaneGroup ) );
    }
  }

  return _areas;
}

/// <summary>
/// Finds all <see cref="LayoutAnchorable"/> objects (toolwindows) within a
/// <see cref="LayoutFloatingWindow"/> (if any) and return them.
/// </summary>
/// <param name="draggingWindow"></param>
/// <returns></returns>
private IEnumerable<LayoutAnchorable> GetAnchorableInFloatingWindow(LayoutFloatingWindowControl draggingWindow)
{
  var layoutAnchorableFloatingWindow = draggingWindow.Model as LayoutAnchorableFloatingWindow;
  if (layoutAnchorableFloatingWindow != null)
  {
      //big part of code for getting type
      var layoutAnchorablePane = layoutAnchorableFloatingWindow.SinglePane as LayoutAnchorablePane;

      if (layoutAnchorablePane != null
          && (layoutAnchorableFloatingWindow.IsSinglePane
          && layoutAnchorablePane.SelectedContent != null))
      {
          var layoutAnchorable = ((LayoutAnchorablePane)layoutAnchorableFloatingWindow.SinglePane).SelectedContent as LayoutAnchorable;
          yield return layoutAnchorable;
      }
      else
      {
        foreach (var item in GetLayoutAnchorable(layoutAnchorableFloatingWindow.RootPanel))
        {
          yield return item;
        }
      }
  }
}

/// <summary>
/// Finds all <see cref="LayoutAnchorable"/> objects (toolwindows) within a
/// <see cref="LayoutAnchorablePaneGroup"/> (if any) and return them.
/// </summary>
/// <param name="layoutAnchPaneGroup"></param>
/// <returns></returns>
internal IEnumerable<LayoutAnchorable> GetLayoutAnchorable(LayoutAnchorablePaneGroup layoutAnchPaneGroup)
{
  if (layoutAnchPaneGroup != null)
  {
    foreach (var anchorable in layoutAnchPaneGroup.Descendents().OfType<LayoutAnchorable>())
    {
      yield return anchorable;
    }
  }
}