Customable TabControl for CloseableTabs

| 这里有一些“可关闭”选项卡的解决方案,但它们都依赖于集合的所有者来提供关闭功能。 我认为通过创建Custom
TabControl
TabItem
也许可以在CodeBehind中完成。 这个想法是要以某种方式提醒ѭ1中的
TabControl
已按下关闭按钮(也许是冒泡事件或某些事,请提出建议),然后从ѭ5中取出包裹在ѭ1中的特定物品。 问题是我不确定如何或什至可行。您能以某种方式从
TabControl
中的
ItemsSource
集合中删除项目吗?也许我们可以设置一个新的DP母猪,我们可以完全控制Collection并将其设置为Code Behind中的
ItemsSource
。 任何人都这样做或有任何想法要这样做。甚至有可能吗?     
已邀请:
        即使在技术上可行,我还是建议您坚持使用M-V-VM。让集合的所有者,即ViewModel控件添加/删除集合中的项目。 让View变薄,并通过数据绑定与VM的集合同步。这样可以避免视图变得复杂,并消除了GUI,这阻碍了可测试性。 您应该发现这是WPF阻力最小的路径。     
        这出现在我们的应用程序中,所以我想我会发布我的解决方案。 ClosableTabControl通过从绑定的ItemsSource中删除项目来关闭任何选项卡。从其他溶液中将其部分刮掉(Szymon Kobalczyk等)。 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>
    

要回复问题请先登录注册