Wpf 如何增强TabControl以停靠和浮动TabItems或文档?

Wpf 如何增强TabControl以停靠和浮动TabItems或文档?,wpf,mvvm,tabcontrol,dock,avalondock,Wpf,Mvvm,Tabcontrol,Dock,Avalondock,我有一个选项卡控件,允许用户管理以下文档: 在某种程度上,我想添加一个功能,允许用户浮动TabItems,并将它们停靠回TabControl中,这与您在VisualStudio中可以执行的操作大致相同。此功能允许用户更轻松地比较文档并在文档之间进行复制/粘贴等 关于如何着手做这件事,我有一些大致的想法。TabControl将其ItemsSource绑定到文档视图模型列表 要浮动选项卡,请执行以下操作: 将Thumb控件添加到TabItem的选项卡条区域 当用户拖动拇指时,关联的文档视图模型将从

我有一个
选项卡控件
,允许用户管理以下文档:

在某种程度上,我想添加一个功能,允许用户浮动
TabItem
s,并将它们停靠回
TabControl
中,这与您在VisualStudio中可以执行的操作大致相同。此功能允许用户更轻松地比较文档并在文档之间进行复制/粘贴等

关于如何着手做这件事,我有一些大致的想法。
TabControl
将其
ItemsSource
绑定到文档视图模型列表

要浮动选项卡,请执行以下操作:

  • Thumb
    控件添加到
    TabItem
    的选项卡条区域
  • 当用户拖动
    拇指时
    ,关联的文档视图模型将从
    选项卡控件
    列表中删除
  • 将打开一个单独的文档
    窗口
    ,与文档视图模型绑定,以显示/编辑该文档
  • 要停靠选项卡,请执行以下操作:

  • TabControl
    中添加
    DragOver
    事件处理程序,以识别在选项卡条区域上拖动的文档
    窗口
  • 关联的文档视图模型将添加到
    TabControl
    列表中
  • 文档
    窗口
    关闭
  • 有没有关于如何做到这一点的例子,或者你有什么方法可以做到这一点


    谢谢。

    如果您找不到或不想使用预先存在的控件,我强烈推荐Bea Stollnitz关于的文章。为了使用
    DockPanel
    来识别数据绑定对象应该使用什么
    DockPanel.Dock
    ,您可能需要对其进行一些修改,但是我发现在过去代码很容易修改


    然后,您将设置两个数据绑定控件,例如
    TabControl
    DockPanel
    ,当在这两个控件之间拖放时,实际上是在
    ItemsSources

    之间拖放数据绑定项。我终于开始实现此功能,并使用了MVVM友好型。我所需要做的就是用一个
    DockingManager
    替换
    TabControl
    ,并修改一些
    样式

    DockingManager
    设置(我只有文档,没有工具等):

    这是最终结果的一个例子:


    看一看精彩的,我去看看AvalonDock,谢谢!看一看。我相信AvalonDock确实使用单独的窗口。如果使用MVVM,我认为您必须为每个ViewModel定义一个
    DataTemplate
    (即,您不能在Window类中定义视图)。感谢jberger,这可能是我将选择的路线,我会让问题再公开一会儿。没问题。这个项目已经停滞了几个月,最近开始活跃起来。。所以它可能值得一看,很好的参考,谢谢瑞秋。对于该示例,您是否尝试过仅解决当前窗口的限制?理想情况下,我希望为未固定的文档设置单独的窗口。@DaveClemer不,我没有在多个窗口中尝试过它,尽管我用它在不同的
    用户控件之间拖放项目。
    @DaveClemer:我相信AvalonDock确实使用单独的窗口。如果使用MVVM,我认为您必须为每个ViewModel定义一个
    DataTemplate
    (即,您不能在窗口类中定义视图)。@jberger,AvalonDock方法看起来非常适合我的需要,我已经设置了
    DataTemplate
    。您可以将您的评论作为答案发布。
    <avalonDock:DockingManager x:Name="tabDesigner" DocumentsSource="{Binding Items}">
        <avalonDock:DockingManager.LayoutItemContainerStyle>
            <Style TargetType="{x:Type avalonDockControls:LayoutItem}" BasedOn="{StaticResource DocumentItem}"/>
        </avalonDock:DockingManager.LayoutItemContainerStyle>
        <avalonDock:DockingManager.DocumentPaneControlStyle>
            <Style TargetType="{x:Type avalonDockControls:LayoutDocumentPaneControl}" BasedOn="{StaticResource DocumentPane}"/>
        </avalonDock:DockingManager.DocumentPaneControlStyle>
        <avalonDockLayout:LayoutRoot>
            <avalonDockLayout:LayoutPanel Orientation="Horizontal">
                <avalonDockLayout:LayoutDocumentPane/>
            </avalonDockLayout:LayoutPanel>
        </avalonDockLayout:LayoutRoot>
    </avalonDock:DockingManager>
    
    <Style x:Key="DocumentItem" TargetType="{x:Type avalonDockControls:LayoutItem}">
        <Setter Property="Title" Value="{Binding Model.TabTitle}"/>
        <Setter Property="CloseCommand" Value="{Binding Model.CloseConfirmCommand}"/>
        <Setter Property="IsSelected" Value="{Binding Model.IsSelected, Mode=TwoWay}"/>
    </Style>
    
    <Style x:Key="DocumentPane" TargetType="{x:Type avalonDockControls:LayoutDocumentPaneControl}">
        ...
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type avalonDockControls:LayoutDocumentPaneControl}">
                    <Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
                        ...
                        <Grid  Panel.ZIndex="1" Background="{DynamicResource TabControlHeaderBrush}" >
                            ...
                            <avalonDockControls:DocumentPaneTabPanel x:Name="HeaderPanel" Grid.Column="0" IsItemsHost="true" Margin="4,0,16,0" Grid.Row="0" KeyboardNavigation.TabIndex="1"/>
                            <avalonDockControls:DropDownButton
                                ...
                                Style="{DynamicResource ToolBarHorizontalOverflowButtonStyle}"
                                Grid.Column="1">
                                ...
                            </avalonDockControls:DropDownButton>
                        </Grid>
                        <Border x:Name="ContentPanel" 
                                ...
                                CornerRadius="3">
                            <Border
                                ...
                                >
                                <Border
                                    ...
                                    >
                                    <ContentPresenter x:Name="PART_SelectedContentHost" 
                                                  ContentSource="SelectedContent" 
                                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </Border>
                            </Border>
                        </Border>
                    </Grid>
                    <ControlTemplate.Triggers>
                        ...
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemContainerStyle">
            <Setter.Value>
                <Style TargetType="{x:Type TabItem}">
                    ...
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type TabItem}">
                                <Grid>
                                    <ContentPresenter 
                                        x:Name="Content" 
                                        ContentSource="Header" 
                                        ... 
                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemTemplate">
            <Setter.Value>
                <DataTemplate>
                    <avalonDockControls:LayoutDocumentTabItem Model="{Binding}"/>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <avalonDockControls:LayoutDocumentControl Model="{Binding}"/>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <Style TargetType="{x:Type avalonDockControls:LayoutDocumentTabItem}">
        <Setter Property="Template">
                <Setter.Value>
                <ControlTemplate TargetType="{x:Type avalonDockControls:LayoutDocumentTabItem}">
                    <ControlTemplate.Resources>
                        ...
                    </ControlTemplate.Resources>
                    <Grid x:Name="grid" Margin="8,1,8,0">
                        ...
                        <Grid RenderTransformOrigin="0.5,0.5">
                            ...
                            <StackPanel Orientation="Horizontal" Margin="3,0,2,0">
                                <ContentPresenter x:Name="TabContent" Content="{Binding Model, RelativeSource={RelativeSource TemplatedParent}}" TextBlock.Foreground="{DynamicResource UnselectedTabText}"
                                                  ContentTemplate="{Binding DocumentHeaderTemplate, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type avalonDock:DockingManager}, Mode=FindAncestor}}"
                                                  ContentTemplateSelector="{Binding DocumentHeaderTemplateSelector, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type avalonDock:DockingManager}, Mode=FindAncestor}}"
                                                  Margin="5,2,5,2"/>
                                <Button
                                    x:Name="TabItemButton"
                                    Command="{Binding Path=Model.Content.CloseConfirmCommand, RelativeSource={RelativeSource TemplatedParent}}"
                                    Content="X"
                                    ...
                                />
                                <StackPanel.ContextMenu>
                                    <ContextMenu>
                                        <MenuItem Header="{Binding Model.Content.CloseTabLabel, RelativeSource={RelativeSource TemplatedParent}}" Command="{Binding Model.Content.CloseTab, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{Binding Model.Content.CloseTabToolTipLabel, RelativeSource={RelativeSource TemplatedParent}}"></MenuItem>
                                        <MenuItem Header="{Binding Model.Content.CloseOtherTabsLabel, RelativeSource={RelativeSource TemplatedParent}}" Command="{Binding Model.Content.CloseOtherTabs, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{Binding Model.Content.CloseOtherTabsToolTipLabel, RelativeSource={RelativeSource TemplatedParent}}"></MenuItem>
                                        <MenuItem Header="{Binding Model.Content.NextTabLabel, RelativeSource={RelativeSource TemplatedParent}}" Command="{Binding Model.Content.NextTab, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{Binding Model.Content.NextTabToolTipLabel, RelativeSource={RelativeSource TemplatedParent}}"></MenuItem>
                                    </ContextMenu>
                                </StackPanel.ContextMenu>
                            </StackPanel>
                        </Grid>
                    </Grid>
                    <ControlTemplate.Triggers>
                        ...
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>