2010-11-20 22 views
2

J'ai modifié le modèle GroupBox standard car je voulais le personnaliser. En dehors des autres personnalisations, je voulais que l'en-tête de GroupBox soit aligné horizontalement dans le Centre au lieu de Gauche ou Droite. L'alignement de l'en-tête n'est pas un problème cependant, le vrai problème est le masque d'opacité défini pour les contrôles de bordure. Le masque d'opacité définit l'espace transparent derrière l'en-tête de la zone de groupe où les bordures ne sont pas dessinées. Je n'ai pas réussi à comprendre comment placer l'espace/espace transparent derrière l'en-tête de groupe lorsque je place l'en-tête au centre.Personnalisation de l'en-tête WPF GroupBox

Voici comment mon XAML ressemble à: (S'il vous plaît accédez à la section commençant par « Border.OpacityMask » qui fixe l'écart transparent dans la bordure autour de l'en-tête)

<ControlTemplate x:Key="GroupBoxControlTemplate1" TargetType="{x:Type GroupBox}"> 
<Grid SnapsToDevicePixels="True"> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="6"/> 
     <ColumnDefinition Width="Auto"/> 
     <ColumnDefinition Width="*"/> 
     <ColumnDefinition Width="6"/> 
    </Grid.ColumnDefinitions> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition Height="*"/> 
     <RowDefinition Height="6"/> 
    </Grid.RowDefinitions> 

    <Border Background="{TemplateBinding Background}" BorderBrush="Transparent" 
    BorderThickness="{TemplateBinding BorderThickness}" 
    CornerRadius="4" Grid.Column="1 " Grid.ColumnSpan="4" 
    Grid.Row="1" Grid.RowSpan="3" HorizontalAlignment="Stretch"/> 

    <Border x:Name="Header" Grid.Column="2" Grid.RowSpan="2" HorizontalAlignment="Left" 
     Padding="3,1,3,0" VerticalAlignment="Stretch"> 
     <Border.Effect> 
      <DropShadowEffect BlurRadius="10" Direction="334"/> 
     </Border.Effect> 
     <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
     Content="{TemplateBinding Header}" 
     ContentSource="Header" 
     ContentStringFormat="{TemplateBinding HeaderStringFormat}" 
     ContentTemplate="{TemplateBinding HeaderTemplate}" 
     RecognizesAccessKey="True" Height="Auto" 
     VerticalAlignment="Center" 
     HorizontalAlignment="Center" 
     OpacityMask="#FF3844BD" Margin="0,1,0,0"> 
     </ContentPresenter> 
    </Border> 

    <ContentPresenter Margin="{TemplateBinding Padding}" 
     SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
     Content="{TemplateBinding Content}" 
     ContentStringFormat="{TemplateBinding ContentStringFormat}" 
     ContentTemplate="{TemplateBinding ContentTemplate}" 
     Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2"/> 
    <Border BorderBrush="White" BorderThickness="{TemplateBinding BorderThickness}" 
     CornerRadius="4" Grid.ColumnSpan="3" Grid.Row="1" Grid.RowSpan="3" RenderTransformOrigin="0.5,0.5" Margin="0"> 
     <Border.OpacityMask> 
      <MultiBinding ConverterParameter="7" UpdateSourceTrigger="Default"> 
       <MultiBinding.Converter> 
        <BorderGapMaskConverter/> 
       </MultiBinding.Converter> 
       <Binding Path="ActualWidth" ElementName="Header"/> 
       <Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}"/> 
       <Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}"/> 
      </MultiBinding> 
     </Border.OpacityMask> 
     <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4"> 
      <Border BorderBrush="White" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4"/> 
     </Border> 
    </Border> 
</Grid> 

Un grand merci pour votre aide à l'avance.

-Wajahat

+0

Je suis tombé sur une question similaire ici (http://stackoverflow.com/questions/2104013/wpf-groupbox- header-postion-alignment) où la réponse suggérée n'est pas ce que je veux. La solution suggérée ici était simplement de retourner les bordures de sorte que l'espace vide dans la bordure soit déplacé vers la droite. Je veux que l'en-tête soit au centre et non à droite. Merci. –

Répondre

6

je devais faire quelque chose de similaire il y a quelque temps, je voulais créer un GroupBox avec deux têtes (une à gauche et une à droite). J'ai juste utilisé Reflector pour obtenir le code pour BorderGapMaskConverter, et l'ai modifié pour créer mon propre convertisseur. Vous pourriez probablement faire la même chose ici.


EDIT: J'ai modifié mon convertisseur pour le faire fonctionner pour un en-tête centré.

Voici le ControlTemplate

<ControlTemplate TargetType="{x:Type GroupBox}"> 
    <Grid SnapsToDevicePixels="true"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="6"/> 
      <ColumnDefinition Width="*"/> 
      <ColumnDefinition Width="Auto"/> 
      <ColumnDefinition Width="*" /> 
      <ColumnDefinition Width="6"/> 
     </Grid.ColumnDefinitions> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="*"/> 
      <RowDefinition Height="6"/> 
     </Grid.RowDefinitions> 
     <Border CornerRadius="4" 
      Grid.Row="1" 
      Grid.RowSpan="3" 
      Grid.Column="0" 
      Grid.ColumnSpan="5" 
      BorderThickness="{TemplateBinding BorderThickness}" 
      BorderBrush="Transparent" 
      Background="{TemplateBinding Background}"/> 
     <Border x:Name="Header" 
      Padding="3,1,3,0" 
      Grid.Row="0" 
      Grid.RowSpan="2" 
      Grid.Column="2"> 
      <ContentPresenter ContentSource="Header" 
          RecognizesAccessKey="True" 
          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> 
     </Border> 
     <ContentPresenter Grid.Row="2" 
         Grid.Column="1" 
         Grid.ColumnSpan="3" 
         Margin="{TemplateBinding Padding}" 
         SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> 
     <Border CornerRadius="4" 
      Grid.Row="1" 
      Grid.RowSpan="3" 
      Grid.ColumnSpan="5" 
      BorderThickness="{TemplateBinding BorderThickness}" 
      BorderBrush="White"> 
      <Border.OpacityMask> 
       <MultiBinding Converter="{StaticResource CenterBorderGapMaskConverter}"> 
        <Binding ElementName="Header" 
         Path="ActualWidth"/> 
        <Binding RelativeSource="{RelativeSource Self}" 
         Path="ActualWidth"/> 
        <Binding RelativeSource="{RelativeSource Self}" 
         Path="ActualHeight"/> 
       </MultiBinding> 
      </Border.OpacityMask> 

      <Border BorderThickness="{TemplateBinding BorderThickness}" 
       BorderBrush="{TemplateBinding BorderBrush}" 
       CornerRadius="3"> 
       <Border BorderThickness="{TemplateBinding BorderThickness}" 
        BorderBrush="White" 
        CornerRadius="2"/> 
      </Border> 
     </Border> 
    </Grid> 
</ControlTemplate> 

Et voici le convertisseur:

class CenterBorderGapMaskConverter : IMultiValueConverter 
{ 
    // Methods 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     Type type = typeof(double); 
     if (values == null 
      || values.Length != 3 
      || values[0] == null 
      || values[1] == null 
      || values[2] == null 
      || !type.IsAssignableFrom(values[0].GetType()) 
      || !type.IsAssignableFrom(values[1].GetType()) 
      || !type.IsAssignableFrom(values[2].GetType())) 
     { 
      return DependencyProperty.UnsetValue; 
     } 

     double pixels = (double)values[0]; 
     double width = (double)values[1]; 
     double height = (double)values[2]; 
     if ((width == 0.0) || (height == 0.0)) 
     { 
      return null; 
     } 
     Grid visual = new Grid(); 
     visual.Width = width; 
     visual.Height = height; 
     ColumnDefinition colDefinition1 = new ColumnDefinition(); 
     ColumnDefinition colDefinition2 = new ColumnDefinition(); 
     ColumnDefinition colDefinition3 = new ColumnDefinition(); 
     colDefinition1.Width = new GridLength(1.0, GridUnitType.Star); 
     colDefinition2.Width = new GridLength(pixels); 
     colDefinition3.Width = new GridLength(1.0, GridUnitType.Star); 
     visual.ColumnDefinitions.Add(colDefinition1); 
     visual.ColumnDefinitions.Add(colDefinition2); 
     visual.ColumnDefinitions.Add(colDefinition3); 
     RowDefinition rowDefinition1 = new RowDefinition(); 
     RowDefinition rowDefinition2 = new RowDefinition(); 
     rowDefinition1.Height = new GridLength(height/2.0); 
     rowDefinition2.Height = new GridLength(1.0, GridUnitType.Star); 
     visual.RowDefinitions.Add(rowDefinition1); 
     visual.RowDefinitions.Add(rowDefinition2); 
     Rectangle rectangle1 = new Rectangle(); 
     Rectangle rectangle2 = new Rectangle(); 
     Rectangle rectangle3 = new Rectangle(); 
     rectangle1.Fill = Brushes.Black; 
     rectangle2.Fill = Brushes.Black; 
     rectangle3.Fill = Brushes.Black; 
     Grid.SetRowSpan(rectangle1, 2); 
     Grid.SetRow(rectangle1, 0); 
     Grid.SetColumn(rectangle1, 0); 
     Grid.SetRow(rectangle2, 1); 
     Grid.SetColumn(rectangle2, 1); 
     Grid.SetRowSpan(rectangle3, 2); 
     Grid.SetRow(rectangle3, 0); 
     Grid.SetColumn(rectangle3, 2); 
     visual.Children.Add(rectangle1); 
     visual.Children.Add(rectangle2); 
     visual.Children.Add(rectangle3); 
     return new VisualBrush(visual); 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     return new object[] { Binding.DoNothing }; 
    } 
} 
+0

Un grand merci Thomas, votre suggestion fonctionne comme un charme. Cependant, je suis déçu de WPF, car il y a toujours une courbe d'apprentissage abrupte quand on fait ce genre de personnalisations qui ne sont pas simples et encombrantes. –

+0

Oui, WPF dur est un peu difficile quand on commence ... Mais quand on commence à mieux le comprendre, on peut faire des choses qu'on n'aurait jamais pu faire dans WinForms. Quoi qu'il en soit, il est généralement beaucoup plus facile de réaliser ce que vous voulez que dans ce cas précis. Vous avez rarement besoin de créer ce type de convertisseur ... –

+0

+1, faisait juste la même chose et quand j'ai atteint le 'BorderGapMaskConverter' je pensais aïe ... :) Beau travail! –