Пользовательский TabControl для CloseableTabs - PullRequest
0 голосов
/ 27 мая 2011

Здесь есть несколько решений для закрываемых вкладок, но все они зависят от владельца коллекции, чтобы обеспечить закрывающую функциональность.

Я подумал, что, возможно, это будет возможно при создании пользовательских TabControl и TabItem, что это можно сделать в CodeBehind.

Идея состоит в том, чтобы как-то предупредить TabControl из TabItem о том, что была нажата кнопка закрытия (возможно, с пузыряющимся событием или чем-то, пожалуйста,предложить), а затем удалить эти конкретные элементы, завернутые в эту TabItem, из ItemsSource.

Проблема в том, что я не уверен, как или даже если это возможно.Можете ли вы каким-то образом удалить элементы из набора, установленного как ItemsSource в TabControl?Могли бы мы, возможно, установить новую свиноматку DP, у нас есть полный контроль над Коллекцией и просто установить это как ItemsSource в Code Behind.

Кто-нибудь сделал это или имеет какие-либо идеи, как это сделать.Это вообще возможно?

Ответы [ 2 ]

1 голос
/ 30 октября 2013

Это появилось в нашем приложении, поэтому я решил опубликовать свое решение. ClosableTabControl закрывает любую вкладку, удаляя элемент из связанного источника ItemsSource. Это частично очищено от других решений ( Szymon Kobalczyk и др.).

Example style for ClosableTabControl

ClosableTabControl.cs:

[StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(ClosableTabItem))]
public class ClosableTabControl : TabControl
{
    static ClosableTabControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ClosableTabControl),
            new FrameworkPropertyMetadata(typeof(ClosableTabControl)));

        CloseTabCommand = new RoutedCommand();
    }

    public ClosableTabControl()
        : base()
    {
        CommandBindings.Add(new CommandBinding(CloseTabCommand, CloseTabCommand_Execute));
    }

    public static ICommand CloseTabCommand { get; private set; }

    private void CloseTabCommand_Execute(object sender, ExecutedRoutedEventArgs args)
    {
        if (args.Parameter == null || !(args.Parameter is ClosableTabItem))
            throw new ArgumentNullException("parameter must be of type ClosableTabItem");

        var item = this.ItemContainerGenerator.ItemFromContainer((ClosableTabItem)args.Parameter);
        if (item == null)
            throw new InvalidOperationException("Item not in collection");

        IEditableCollectionView view = this.Items;
        if (!view.CanRemove)
            throw new InvalidOperationException("Read-only collection");

        view.Remove(item);
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ClosableTabItem();
    }
}

ClosableTabItem.cs:

public class ClosableTabItem : TabItem
{
    static ClosableTabItem()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ClosableTabItem),
            new FrameworkPropertyMetadata(typeof(ClosableTabItem)));
    }
}

Темы / Generic.xaml:

<Style TargetType="{x:Type local:ClosableTabControl}" BasedOn="{StaticResource {x:Type TabControl}}" />

<Style x:Key="TabItemFocusVisual">
    <Setter Property="Control.Template">
        <Setter.Value>
            <ControlTemplate>
                <Rectangle SnapsToDevicePixels="true" Stroke="Black" StrokeDashArray="1 2" StrokeThickness="1" Margin="3,3,3,1"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<SolidColorBrush x:Key="TabControlNormalBorderBrush" Color="#8C8E94"/>
<LinearGradientBrush x:Key="TabItemHotBackground" EndPoint="0,1" StartPoint="0,0">
    <GradientStop Color="#EAF6FD" Offset="0.15"/>
    <GradientStop Color="#D9F0FC" Offset=".5"/>
    <GradientStop Color="#BEE6FD" Offset=".5"/>
    <GradientStop Color="#A7D9F5" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="TabItemSelectedBackground" Color="#F9F9F9"/>
<SolidColorBrush x:Key="TabItemHotBorderBrush" Color="#3C7FB1"/>
<SolidColorBrush x:Key="TabItemDisabledBackground" Color="#F4F4F4"/>
<SolidColorBrush x:Key="TabItemDisabledBorderBrush" Color="#FFC9C7BA"/>

<Style TargetType="{x:Type local:ClosableTabItem}">
    <Style.Resources>
        <LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#F3F3F3" Offset="0"/>
            <GradientStop Color="#EBEBEB" Offset="0.5"/>
            <GradientStop Color="#DDDDDD" Offset="0.5"/>
            <GradientStop Color="#CDCDCD" Offset="1"/>
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="ButtonOverBackground" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFFAFAFA" Offset="0"/>
            <GradientStop Color="#FFE0E0E3" Offset="1"/>
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="ButtonPressedBackground" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFE0E0E2" Offset="0"/>
            <GradientStop Color="#FFF8F8F8" Offset="1"/>
        </LinearGradientBrush>
        <SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF969696"/>
        <Style x:Key="CloseableTabItemButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
            <Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="4"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid>
                            <Border SnapsToDevicePixels="true" x:Name="Chrome" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" Opacity="0" />
                            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Opacity" TargetName="Chrome" Value="1"/>
                                <Setter Property="Background" TargetName="Chrome" Value="{DynamicResource ButtonOverBackground}" />
                            </Trigger>
                            <Trigger Property="IsPressed" Value="True">
                                <Setter Property="Opacity" TargetName="Chrome" Value="1"/>
                                <Setter Property="Background" TargetName="Chrome" Value="{DynamicResource ButtonPressedBackground}" />
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="#ADADAD"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Style.Resources>
    <Setter Property="FocusVisualStyle" Value="{StaticResource TabItemFocusVisual}"/>
    <Setter Property="Foreground" Value="Black"/>
    <Setter Property="Padding" Value="6,1,6,1"/>
    <Setter Property="BorderBrush" Value="{StaticResource TabControlNormalBorderBrush}"/>
    <Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ClosableTabItem}">
                <Grid SnapsToDevicePixels="true">
                    <Border x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" >
                        <DockPanel x:Name="ContentPanel">
                            <Button x:Name="PART_Close" HorizontalAlignment="Center" Margin="3,0,3,0" VerticalAlignment="Center" Width="16" Height="16" DockPanel.Dock="Right" Style="{DynamicResource CloseableTabItemButtonStyle}" ToolTip="Close" Command="{x:Static local:ClosableTabControl.CloseTabCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:ClosableTabItem}}}">
                                <Path x:Name="Path" Stretch="Fill" StrokeThickness="0.5" Stroke="#FF333333" Fill="#FF969696" Data="F1 M 2.28484e-007,1.33331L 1.33333,0L 4.00001,2.66669L 6.66667,6.10352e-005L 8,1.33331L 5.33334,4L 8,6.66669L 6.66667,8L 4,5.33331L 1.33333,8L 1.086e-007,6.66669L 2.66667,4L 2.28484e-007,1.33331 Z " HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
                            </Button>
                            <ContentPresenter x:Name="Content" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="{TemplateBinding Padding}"/>
                        </DockPanel>
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" SourceName="PART_Close" Value="True">
                        <Setter Property="Fill" TargetName="Path" Value="#FFB83C3D"/>
                    </Trigger>
                    <Trigger Property="IsPressed" SourceName="PART_Close" Value="True">
                        <Setter Property="Fill" TargetName="Path" Value="#FF9D3838"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="true">
                        <Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemHotBackground}"/>
                    </Trigger>
                    <Trigger Property="IsSelected" Value="true">
                        <Setter Property="Panel.ZIndex" Value="1"/>
                        <Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemSelectedBackground}"/>
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsSelected" Value="false"/>
                            <Condition Property="IsMouseOver" Value="true"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource TabItemHotBorderBrush}"/>
                    </MultiTrigger>
                    <Trigger Property="TabStripPlacement" Value="Bottom">
                        <Setter Property="BorderThickness" TargetName="Bd" Value="1,0,1,1"/>
                    </Trigger>
                    <Trigger Property="TabStripPlacement" Value="Left">
                        <Setter Property="BorderThickness" TargetName="Bd" Value="1,1,0,1"/>
                    </Trigger>
                    <Trigger Property="TabStripPlacement" Value="Right">
                        <Setter Property="BorderThickness" TargetName="Bd" Value="0,1,1,1"/>
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsSelected" Value="true"/>
                            <Condition Property="TabStripPlacement" Value="Top"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Margin" Value="-2,-2,-2,-1"/>
                        <Setter Property="Margin" TargetName="ContentPanel" Value="0,0,0,1"/>
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsSelected" Value="true"/>
                            <Condition Property="TabStripPlacement" Value="Bottom"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Margin" Value="-2,-1,-2,-2"/>
                        <Setter Property="Margin" TargetName="ContentPanel" Value="0,1,0,0"/>
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsSelected" Value="true"/>
                            <Condition Property="TabStripPlacement" Value="Left"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Margin" Value="-2,-2,-1,-2"/>
                        <Setter Property="Margin" TargetName="ContentPanel" Value="0,0,1,0"/>
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsSelected" Value="true"/>
                            <Condition Property="TabStripPlacement" Value="Right"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Margin" Value="-1,-2,-2,-2"/>
                        <Setter Property="Margin" TargetName="ContentPanel" Value="1,0,0,0"/>
                    </MultiTrigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemDisabledBackground}"/>
                        <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource TabItemDisabledBorderBrush}"/>
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
1 голос
/ 27 мая 2011

Даже если это технически возможно, я бы посоветовал придерживаться M-V-VM. Позволить владельцу коллекции, то есть элементу управления ViewModel, добавлять / удалять элементы из коллекции. Пусть представление будет тонким и синхронизируется с коллекцией виртуальной машины посредством привязки данных. Это предотвращает усложнение представлений и устраняет графический интерфейс, который сдерживает тестируемость.

Вы должны найти это как путь наименьшего сопротивления WPF.

...