我有一个TabControl
,允许用户管理以下文档:
在某些时候,我想添加一个功能,允许用户浮动TabItem
并将它们重新停靠在TabControl
中,就像在Visual Studio中可以做的那样。此功能将允许用户更轻松地比较文档并在它们之间复制/粘贴等。
我对如何做到这一点有一些一般性的想法。 TabControl
将ItemsSource
绑定到文档视图模型列表。
浮动标签:
Thumb
控件添加到TabItem
。Thumb
时,相关的文档视图模型将从TabControl
列表中删除。Window
,与文档视图模型绑定,以显示/编辑该文档。停靠标签:
DragOver
中添加TabControl
事件处理程序,以识别文件Window
拖动标签条区域。TabControl
列表中。Window
已关闭。有没有关于如何做到这一点的例子,或者你有办法做到这一点吗?
感谢。
答案 0 :(得分:2)
如果您无法找到或不想使用预先存在的控件,我强烈推荐Bea Stollnitz关于dragging and dropping between databound controls的文章。您可能需要稍微更改它以使用DockPanel
来识别数据绑定对象应该使用的DockPanel.Dock
,但是我发现代码在过去很容易改变。
然后您将设置两个数据绑定控件,例如TabControl
和DockPanel
,当在两者之间拖放时,您实际上是在{{1}之间拖放数据绑定项}。
答案 1 :(得分:2)
我终于开始实现这个功能了,我使用了AvalonDock 2.0,这是MVVM友好的。我需要做的就是用TabControl
替换DockingManager
并修改一些Style
。
DockingManager
设置(我只有文档,而不是工具等):
<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>
我不需要使用AvalonDock的模板选择器,我可以使用已经为之前DataTemplate
设置的TabControl
。
我修改了LayoutItem
,LayoutDocumentPaneControl
和LayoutDocumentTabItem
Style
以对视图模型和其他布局差异进行额外绑定(需要一段时间才能完成弄清楚如何绑定到AvalonDock模型中的视图模型:
<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>
这是最终结果的一个例子: