Как вы можете улучшить TabControl, чтобы закреплять и перемещать TabItems или документы? - PullRequest
4 голосов
/ 29 февраля 2012

У меня есть TabControl, который позволяет пользователям управлять документами, такими как:

enter image description here

В какой-то момент я хочу добавить функцию, которая позволяет пользователям перемещать TabItem s и вставлять их обратно в TabControl по аналогии с тем, что вы можете делать в Visual Studio. Эта функция позволит пользователям легче сравнивать документы и копировать / вставлять между ними и т. Д.

У меня есть несколько общих идей о том, как это сделать. TabControl имеет ItemsSource, связанный со списком моделей представлений документов.

Чтобы открыть вкладку:

  1. Добавление элемента управления Thumb в область полосы вкладок TabItem.
  2. Когда пользователь перетаскивает Thumb, связанная модель представления документа удаляется из списка TabControl.
  3. Создается отдельный документ Window, связанный с моделью представления документа, для отображения / редактирования этого документа.

Чтобы закрепить вкладку:

  1. Добавьте обработчик DragOver в TabControl для распознавания документа Window, перетаскивая область области вкладок.
  2. Соответствующая модель представления документа добавлена ​​в список TabControl.
  3. Документ Window закрыт.

Есть ли какие-нибудь примеры того, как это сделать, или у вас есть подход для этого?

Спасибо.

Ответы [ 2 ]

2 голосов
/ 23 марта 2013

Я наконец-то нашел способ реализовать эту функцию и использовал AvalonDock 2.0 , который дружественен к MVVM.Все, что мне нужно было сделать, это заменить TabControl на DockingManager и изменить несколько Style s.

Настройка 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 s, которые уже были настроены для предыдущих TabControl.

, которые я модифицировалLayoutItem, LayoutDocumentPaneControl и LayoutDocumentTabItem Style s для дополнительной привязки к моделям представлений и другим различиям в макете (потребовалось некоторое время, чтобы выяснить, как связывать модели представлений, которые находятся в 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>

Это пример конечного результата:

enter image description here

2 голосов
/ 29 февраля 2012

Если вы не можете найти или не хотите использовать ранее существующий элемент управления, я настоятельно рекомендую статью Беа Столльниц о перетаскивании между элементами управления с привязкой к данным . Возможно, вам придется немного изменить его, чтобы работать с DockPanel, чтобы определить, что DockPanel.Dock должен использовать объект с привязкой к данным, однако в прошлом я обнаружил, что код легко изменить.

Затем вы должны установить два элемента управления с привязкой к данным, таких как TabControl и DockPanel, и при перетаскивании / отбрасывании между ними вы фактически перетаскиваете элементы привязки к данным между ItemsSources.

...