2011-02-10 2 views
2

Je veux juste un TabControl qui a des TabItems personnalisés qui sont fermables, basé sur le code de here.wpf personnalisé TabControl

Je pensais que this question était le même que le mien, mais la combinaison du code & xaml ci-dessous laisse des onglets vides.

public class ClosableTabControl : TabControl 
{ 
    protected override DependencyObject GetContainerForItemOverride() 
    { 
     return new ClosableTabItem(); 
    } 
} 

     <uc:ClosableTabControl x:Name="Items" Grid.Column="1"> 
      <uc:ClosableTabControl.ItemTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding DisplayName}" /> 
       </DataTemplate> 
      </uc:ClosableTabControl.ItemTemplate> 
      <uc:ClosableTabControl.ContentTemplate> 
       <DataTemplate> 
        <ContentControl> 
         <local:EmpView DataContext="{Binding ., Mode=TwoWay}"/> 
        </ContentControl> 
       </DataTemplate> 
      </uc:ClosableTabControl.ContentTemplate> 
     </uc:ClosableTabControl> 

Le xaml fonctionne sur un TabControl (au lieu de uc: ClosableTabControl).

Quelle est la meilleure façon d'avoir un TabControl qui a ClosableTabItems pour les enfants?

Cheers,
Berryl

P.S. Je posterais le code pour le ClosableTabItem mais je voulais garder le niveau de bruit bas sur le premier post. Demandez-moi de l'afficher si cela peut vous aider.

+0

Vous pouvez vous rappeler mon exemple de TabControl, j'ai utilisé TabControl.ItemTemplate avec un bouton de fermeture qui est lié à une commande dans un ViewModel. L'avantage de ma solution est la possibilité de fermer les éléments de l'onglet en utilisant des boutons internes comme Enregistrer ou Annuler. – vorrtex

+0

C'était un exemple Silverlight, mais de toute façon, voici le lien: http: // stackoverflow.com/questions/4828661/silverlight-4-making-closeable-tabitems/4830314 # 4830314 Si vous êtes prêt à travailler avec un si grand nombre de ViewModels, je peux réécrire cet exemple à WPF. – vorrtex

+0

Hey vortex. Je n'ai pas encore eu le temps de regarder ton exemple, mais je me souviens que tu avais un bouton de fermeture. Le code ClosableTab que j'ai référencé dans ma question a l'avantage de cacher/montrer le bouton lorsqu'il est sélectionné ou non. Je suppose que le vôtre pourrait le faire de façon déclarative, oui? Revenez bientôt - laissez quelque chose que je peux marquer comme une réponse aussi! – Berryl

Répondre

6

Voici une capture d'écran de mon TabControl:

enter image description here

Tout d'abord, le bouton de fermeture est un contrôle personnalisé qui permet d'utiliser des couleurs différentes pour Hover et états emboutie.

Add -> New Item -> Custom Control -> GlyphButton 

GlyphButton.cs

public class GlyphButton : Button 
{ 
    public static readonly DependencyProperty GlyphForegroundProperty = DependencyProperty.Register("GlyphForeground", typeof(Brush), typeof(GlyphButton)); 
    public static readonly DependencyProperty HoverBackgroundProperty = DependencyProperty.Register("HoverBackground", typeof(Brush), typeof(GlyphButton)); 
    public static readonly DependencyProperty HoverBorderBrushProperty = DependencyProperty.Register("HoverBorderBrush", typeof(Brush), typeof(GlyphButton)); 
    public static readonly DependencyProperty HoverBorderThicknessProperty = DependencyProperty.Register("HoverBorderThickness", typeof(Thickness), typeof(GlyphButton)); 
    public static readonly DependencyProperty HoverForegroundProperty = DependencyProperty.Register("HoverForeground", typeof(Brush), typeof(GlyphButton)); 
    public static readonly DependencyProperty PressedBackgroundProperty = DependencyProperty.Register("PressedBackground", typeof(Brush), typeof(GlyphButton)); 
    public static readonly DependencyProperty PressedBorderBrushProperty = DependencyProperty.Register("PressedBorderBrush", typeof(Brush), typeof(GlyphButton)); 
    public static readonly DependencyProperty PressedBorderThicknessProperty = DependencyProperty.Register("PressedBorderThickness", typeof(Thickness), typeof(GlyphButton)); 
    public static readonly DependencyProperty PressedForegroundProperty = DependencyProperty.Register("PressedForeground", typeof(Brush), typeof(GlyphButton)); 

    static GlyphButton() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata(typeof(GlyphButton), new FrameworkPropertyMetadata(typeof(GlyphButton))); 
    } 

    public Brush GlyphForeground 
    { 
     get 
     { 
      return (Brush)base.GetValue(GlyphForegroundProperty); 
     } 
     set 
     { 
      base.SetValue(GlyphForegroundProperty, value); 
     } 
    } 

    public Brush HoverBackground 
    { 
     get 
     { 
      return (Brush)base.GetValue(HoverBackgroundProperty); 
     } 
     set 
     { 
      base.SetValue(HoverBackgroundProperty, value); 
     } 
    } 

    public Brush HoverBorderBrush 
    { 
     get 
     { 
      return (Brush)base.GetValue(HoverBorderBrushProperty); 
     } 
     set 
     { 
      base.SetValue(HoverBorderBrushProperty, value); 
     } 
    } 

    public Thickness HoverBorderThickness 
    { 
     get 
     { 
      return (Thickness)base.GetValue(HoverBorderThicknessProperty); 
     } 
     set 
     { 
      base.SetValue(HoverBorderThicknessProperty, value); 
     } 
    } 

    public Brush HoverForeground 
    { 
     get 
     { 
      return (Brush)base.GetValue(HoverForegroundProperty); 
     } 
     set 
     { 
      base.SetValue(HoverForegroundProperty, value); 
     } 
    } 

    public Brush PressedBackground 
    { 
     get 
     { 
      return (Brush)base.GetValue(PressedBackgroundProperty); 
     } 
     set 
     { 
      base.SetValue(PressedBackgroundProperty, value); 
     } 
    } 

    public Brush PressedBorderBrush 
    { 
     get 
     { 
      return (Brush)base.GetValue(PressedBorderBrushProperty); 
     } 
     set 
     { 
      base.SetValue(PressedBorderBrushProperty, value); 
     } 
    } 

    public Thickness PressedBorderThickness 
    { 
     get 
     { 
      return (Thickness)base.GetValue(PressedBorderThicknessProperty); 
     } 
     set 
     { 
      base.SetValue(PressedBorderThicknessProperty, value); 
     } 
    } 

    public Brush PressedForeground 
    { 
     get 
     { 
      return (Brush)base.GetValue(PressedForegroundProperty); 
     } 
     set 
     { 
      base.SetValue(PressedForegroundProperty, value); 
     } 
    } 
} 

Thèmes/generic.xaml

<Style TargetType="{x:Type local:GlyphButton}"> 
    <Setter Property="Width" Value="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" /> 
    <Setter Property="Foreground" Value="{Binding Path=GlyphForeground, RelativeSource={RelativeSource Self}}" /> 
    <Setter Property="Background" Value="Transparent" /> 
    <Setter Property="BorderBrush" Value="Transparent" /> 
    <Setter Property="BorderThickness" Value="1" /> 
    <Setter Property="Focusable" Value="false" /> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type local:GlyphButton}"> 
       <Border Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> 
        <ContentPresenter /> 
       </Border> 
       <ControlTemplate.Triggers> 
        <Trigger Property="IsMouseOver" Value="true"> 
         <Setter TargetName="Border" Value="{Binding HoverBackground , RelativeSource={RelativeSource TemplatedParent}}" Property="Background" /> 
         <Setter TargetName="Border" Value="{Binding HoverBorderBrush , RelativeSource={RelativeSource TemplatedParent}}" Property="BorderBrush" /> 
         <Setter TargetName="Border" Value="{Binding HoverBorderThickness , RelativeSource={RelativeSource TemplatedParent}}" Property="BorderThickness" /> 
         <Setter Value="{Binding HoverForeground , RelativeSource={RelativeSource TemplatedParent}}" Property="Foreground" /> 
        </Trigger> 
        <Trigger Property="IsPressed" Value="True"> 
         <Setter TargetName="Border" Value="{Binding PressedBackground , RelativeSource={RelativeSource TemplatedParent}}" Property="Background" /> 
         <Setter TargetName="Border" Value="{Binding PressedBorderBrush , RelativeSource={RelativeSource TemplatedParent}}" Property="BorderBrush" /> 
         <Setter TargetName="Border" Value="{Binding PressedBorderThickness , RelativeSource={RelativeSource TemplatedParent}}" Property="BorderThickness" /> 
         <Setter Value="{Binding PressedForeground , RelativeSource={RelativeSource TemplatedParent}}" Property="Foreground" /> 
        </Trigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

style pour un TabItem. Vous pouvez le placer n'importe où, dans App.xaml ou dans un dictionnaire de ressources.

style TabItem

<SolidColorBrush x:Key="FileTabTextKey" Color="#ffffff"/> 

<SolidColorBrush x:Key="ToolWindowButtonHoverActiveKey" Color="#fffcf4"/> 
<SolidColorBrush x:Key="ToolWindowButtonHoverActiveBorderKey" Color="#e5c365"/> 
<SolidColorBrush x:Key="ToolWindowButtonHoverActiveGlyphKey" Color="#000000"/> 
<SolidColorBrush x:Key="ToolWindowButtonDownKey" Color="#ffe8a6"/> 
<SolidColorBrush x:Key="ToolWindowButtonDownBorderKey" Color="#e5c365"/> 
<SolidColorBrush x:Key="ToolWindowButtonDownActiveGlyphKey" Color="#000000"/> 

<LinearGradientBrush x:Key="FileTabHotGradientKey"> 
    <GradientStop Color="#707776"/> 
    <GradientStop Color="#4b5c74"/> 
</LinearGradientBrush> 
<SolidColorBrush x:Key="FileTabHotBorderKey" Color="#9ba7b7"/> 
<SolidColorBrush x:Key="FileTabHotTextKey" Color="#ffffff"/> 
<SolidColorBrush x:Key="FileTabHotGlyphKey" Color="#ced4dd"/> 

<LinearGradientBrush x:Key="FileTabSelectedGradientKey" StartPoint="0.5,0" EndPoint="0.5,1"> 
    <GradientStop Color="#fffcf4"/> 
    <GradientStop Color="#fff3cd" Offset="0.5"/> 
    <GradientStop Color="#ffe8a6" Offset="0.5"/> 
    <GradientStop Color="#ffe8a6" Offset="1"/> 
</LinearGradientBrush> 
<SolidColorBrush x:Key="FileTabSelectedTextKey" Color="#000000"/> 
<SolidColorBrush x:Key="FileTabSelectedGlyphKey" Color="#75633d"/> 

<Style x:Key="OrangeTabItem" TargetType="{x:Type TabItem}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type TabItem}"> 
       <Border AllowDrop="true" ToolTip="{Binding Title}"> 
        <Border Name="Border" Background="Transparent" BorderBrush="Transparent" BorderThickness="1,1,1,0" CornerRadius="2,2,0,0"> 
         <DockPanel x:Name="TitlePanel" TextElement.Foreground="{StaticResource FileTabTextKey}"> 
          <controls:GlyphButton x:Name="HideButton" 
          DockPanel.Dock="Right" 
          GlyphForeground="Transparent" 
          HoverBackground="{StaticResource ToolWindowButtonHoverActiveKey}" 
          HoverBorderBrush="{StaticResource ToolWindowButtonHoverActiveBorderKey}" 
          HoverForeground="{StaticResource ToolWindowButtonHoverActiveGlyphKey}" 
          PressedBackground="{StaticResource ToolWindowButtonDownKey}" 
          PressedBorderBrush="{StaticResource ToolWindowButtonDownBorderKey}" 
          PressedForeground="{StaticResource ToolWindowButtonDownActiveGlyphKey}" 
          HoverBorderThickness="1" PressedBorderThickness="1" Margin="3,2,3,4" 
          Command="{Binding RequestCloseCommand}" 
          CommandParameter="{Binding}" 
          ToolTip="Close"> 
           <Path x:Name="CloseButtonStroke" Width="10" Height="8" Stretch="Uniform" Data="F1 M 0,0 L 2,0 5,3 8,0 10,0 6,4 10,8 8,8 5,5 2,8 0,8 4,4 0,0 Z" 
           Fill="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource Self}}" /> 
          </controls:GlyphButton> 

          <ContentPresenter x:Name="Content" HorizontalAlignment="Stretch" Margin="4,2,4,4" VerticalAlignment="Stretch" RecognizesAccessKey="true" ContentSource="Header" /> 
         </DockPanel> 
        </Border> 
       </Border> 
       <ControlTemplate.Triggers> 
        <Trigger Property="IsMouseOver" Value="true"> 
         <Setter TargetName="Border" Value="{StaticResource FileTabHotGradientKey}" Property="Background" /> 
         <Setter TargetName="Border" Value="{StaticResource FileTabHotBorderKey}" Property="BorderBrush" /> 
         <Setter TargetName="TitlePanel" Value="{StaticResource FileTabHotTextKey}" Property="TextElement.Foreground" /> 
         <Setter TargetName="HideButton" Value="{StaticResource FileTabHotGlyphKey}" Property="GlyphForeground" /> 
        </Trigger> 
        <Trigger Property="IsSelected" Value="True"> 
         <Setter Property="Panel.ZIndex" Value="1" /> 
         <Setter TargetName="Border" Value="{StaticResource FileTabSelectedGradientKey}" Property="Background" /> 
         <Setter TargetName="Border" Value="{StaticResource FileTabSelectedGradientKey}" Property="BorderBrush" /> 
         <Setter TargetName="Border" Property="BorderThickness" Value="0" /> 
         <Setter TargetName="Border" Property="Padding" Value="0,1,0,0" /> 
         <Setter TargetName="HideButton" Property="Margin" Value="3" /> 
         <Setter TargetName="TitlePanel" Value="{StaticResource FileTabSelectedTextKey}" Property="TextElement.Foreground" /> 
         <Setter TargetName="HideButton" Value="{StaticResource FileTabSelectedGlyphKey}" Property="GlyphForeground" /> 
        </Trigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Maintenant, le Main Window. Mon TabControl nécessite un fond bleu, mais vous pouvez changer les couleurs si vous le souhaitez.

<Grid Background="#FF293955"> 
    <TabControl ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource OrangeTabItem}"> 
     <TabControl.ItemTemplate> 
      <DataTemplate> 
       <TextBlock Text="{Binding Title}"/> 
      </DataTemplate> 
     </TabControl.ItemTemplate> 
     <TabControl.ContentTemplate> 
      <DataTemplate> 
       <ContentControl Content="{Binding Content}"/> 
      </DataTemplate> 
     </TabControl.ContentTemplate> 
    </TabControl> 
</Grid> 

Enfin une remarque importante: un modèle qui représente TabItem doit contenir la commande RequestCloseCommand

Exemple de deux ViewModels

public class MainViewModel 
{ 
    public MainViewModel() 
    { 
     this.Items = new ObservableCollection<TabItemViewModel> 
        { 
         new TabItemViewModel("Tab 1", OnItemRequestClose), 
         new TabItemViewModel("Tab item 2", OnItemRequestClose) 
        }; 
    } 

    public ObservableCollection<TabItemViewModel> Items { get; set; } 

    public void OnItemRequestClose(TabItemViewModel item) 
    { 
     this.Items.Remove(item); 
    } 
} 

public class TabItemViewModel 
{ 
    public TabItemViewModel(string title, Action<TabItemViewModel> onClose) 
    { 
     this.Title = title; 
     this.RequestCloseCommand = new SimpleCommand(obj => onClose(this)); 
     this.Content = "Test content " + title; 
    } 

    public string Title { get; set; } 

    public ICommand RequestCloseCommand { get; set; } 

    public object Content { get; set; } 
} 

Je pensais à l'aide RoutedUICommand mais il faudra beaucoup de temps pour réviser ce type de commandes. Donc, cette solution est la plus appropriée pour moi maintenant.

+0

@vortex. Jeez mec, celui-ci valait plus que le point + réponse que je vous ai donné. Super travail! – Berryl

+0

@vortex. avez-vous encore utilisé calibrage? J'adore entendre une opinion si oui ... – Berryl

+0

@vortex. Je vais faire une autre question à laquelle vous pouvez répondre quand j'ai une chance; Je n'ai pas beaucoup joué avec le contenu, mais il semble que votre solution nécessiterait l'instanciation d'un contrôle utilisateur dans le code. Est-ce vrai? – Berryl