2010-01-10 6 views
15

L'extrait suivant:Aligner par le bas du texte dans les contrôles

<Window x:Class="Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    <Grid> 
     <StackPanel Orientation="Horizontal" 
        VerticalAlignment="Center" 
        HorizontalAlignment="Center">    
      <Label Content="Name:"/> 
      <Label Content="Itzhak Perlman" FontSize="44"/> 
     </StackPanel> 
    </Grid> 
</Window> 

Renders les suivantes:
alt text

Est-il possible, je peux configurer dans les styles des étiquettes de sorte que leurs fonds de texte devraient être aligné?
J'ai la même question avec TextBlocks aussi. NOTE: depuis que j'ai eu du mal avec ce problème pendant un moment, s'il vous plaît poster seulement certaines réponses que vous connaissez ce travail.
J'ai déjà essayé: VerticalAlignment, VerticalContentAlignment, Padding, Margin. Y a-t-il autre chose dont je ne suis pas au courant?

J'ai lu this, mais il ne parle pas d'un scénario de taille de police différente.

MISE À JOUR: Le problème est que même Padding est défini sur 0, il reste un espace indéterminé autour de la police, dans la zone ContentPresenter. cet espace varie en fonction de la taille de la police. Si je pouvais contrôler cet espace, je serais dans une meilleure situation.

Merci

+1

ah haa! Votre mise à jour clarifie le problème. –

Répondre

6

Il n'y a pas XAML seule solution, vous devez utiliser le code derrière. En outre, même avec le code-behind, il n'y a pas de solution générale pour cela, parce que si votre texte est multi-ligne? Quelle ligne de base devrait être utilisée dans ce cas? Ou s'il y a plusieurs éléments de texte dans votre modèle? Comme un en-tête et un contenu, ou plus, quelle ligne de base alors? En bref, le mieux est d'aligner le texte manuellement en utilisant les marges supérieure/inférieure. Si vous êtes prêt à faire l'hypothèse que vous avez un seul élément de texte, vous pouvez déterminer la distance en pixels de la ligne de base depuis le haut de l'élément en instanciant un objet FormattedText avec toutes les mêmes propriétés que l'élément existant élément de texte. L'objet FormattedText a une propriété doubleBaseline qui contient cette valeur. Notez que vous devez toujours entrer manuellement une marge, car l'élément peut ne pas être placé exactement en haut ou en bas de son conteneur.

Voir ce post forum MSDN: Textbox Baseline

Voici une méthode que j'ai écrit qui extrait cette valeur. Il utilise la réflexion pour obtenir les propriétés pertinentes car elles ne sont communes à aucune classe de base unique (elles sont définies séparément sur Control, TextBlock, Page, TextElement et peut-être d'autres).

public double CalculateBaseline(object textObject) 
{ 
    double r = double.NaN; 
    if (textObject == null) return r; 

    Type t = textObject.GetType(); 
    BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; 

    var fontSizeFI = t.GetProperty("FontSize", bindingFlags); 
    if (fontSizeFI == null) return r; 
    var fontFamilyFI = t.GetProperty("FontFamily", bindingFlags); 
    var fontStyleFI = t.GetProperty("FontStyle", bindingFlags); 
    var fontWeightFI = t.GetProperty("FontWeight", bindingFlags); 
    var fontStretchFI = t.GetProperty("FontStretch", bindingFlags); 

    var fontSize = (double)fontSizeFI.GetValue(textObject, null); 
    var fontFamily = (FontFamily)fontFamilyFI.GetValue(textObject, null); 
    var fontStyle = (FontStyle)fontStyleFI.GetValue(textObject, null); 
    var fontWeight = (FontWeight)fontWeightFI.GetValue(textObject, null); 
    var fontStretch = (FontStretch)fontStretchFI.GetValue(textObject, null); 

    var typeFace = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch); 

    var formattedText = new FormattedText(
     "W", 
     CultureInfo.CurrentCulture, 
     FlowDirection.LeftToRight, 
     typeFace, 
     fontSize, 
     Brushes.Black); 

    r = formattedText.Baseline; 

    return r; 
} 

EDIT: Shimmy, en réponse à votre commentaire, je ne crois pas que vous avez réellement essayé cette solution, parce que cela fonctionne.Voici un exemple:

Example Baseline Alignment

Voici le XAML:

<StackPanel> 
    <StackPanel.Resources> 
     <Style TargetType="TextBlock"> 
      <Setter Property="Margin" Value="0,40,0,0"/> 
     </Style> 
    </StackPanel.Resources> 
    <StackPanel Orientation="Horizontal"> 
     <TextBlock Name="tb1" Text="Lorem " FontSize="10"/> 
     <TextBlock Name="tbref" Text="ipsum"/> 
    </StackPanel> 
    <StackPanel Orientation="Horizontal"> 
     <TextBlock Name="tb2" Text="dolor " FontSize="20"/> 
     <TextBlock Text="sit"/> 
    </StackPanel> 
    <StackPanel Orientation="Horizontal"> 
     <TextBlock Name="tb3" Text="amet " FontSize="30"/> 
     <TextBlock Text="consectetuer"/> 
    </StackPanel> 
</StackPanel> 

Et voici le code derrière qui permet d'atteindre ce

double baseRef = CalculateBaseline(tbref); 
double base1 = CalculateBaseline(tb1) - baseRef; 
double base2 = CalculateBaseline(tb2) - baseRef; 
double base3 = CalculateBaseline(tb3) - baseRef; 
tb1.Margin = new Thickness(0, 40 - base1, 0, 0); 
tb2.Margin = new Thickness(0, 40 - base2, 0, 0); 
tb3.Margin = new Thickness(0, 40 - base3, 0, 0); 
+0

@ אביעד, et que puis-je faire avec cette valeur? qu'est-ce que le 'BaseLine'? – Shimmy

+0

La ligne de base est la distance entre le haut de l'élément de texte et la ligne sur laquelle toutes les lettres sont «assises». Si, par exemple, vous définissez votre marge 'Top' pour dire 50 moins le décalage de la ligne de base, vous serez garanti d'aligner à une ligne spécifique, peu importe la taille de la police. –

+0

C'est ce que j'ai essayé mais ça ne marche pas. Je cherche toujours la solution. Le problème est l'espace entre la marge et le «haut du texte» que vous mentionnez. J'ai mis à jour ma question. Merci pour tout votre effort אביעד. Une nouvelle? – Shimmy

0

j'ai trouvé une réponse simple basée sur Aviad « s .

J'ai créé un convertisseur qui contient la fonction d'Aviad qui accepte l'élément lui-même et renvoie l'épaisseur calculée.

Je mis en place

<Style TargetType="TextBlock"> 
    <Setter Property="Margin" 
     Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource converters:TextAlignmentMarginConverter}}" /> 
</Style> 

L'inconvénient est que cela occupe évidemment la propriété de la marge d'origine, étant donné qu'un TextBlock ne dispose pas d'un modèle que nous ne pouvons pas le mettre par TemplateBinding.

+0

C'est un bon moyen, mais un convertisseur ne vous prendra qu'une partie du chemin, pas tout à fait, parce que, par exemple, si la taille de la police change pendant l'exécution du programme, la propriété margin ' Pour être automatiquement réévalué, vous devrez le faire explicitement. En outre, le convertisseur doit prendre en compte les autres éléments du gabarit tels que le remplissage, les bordures, etc., de sorte que vous ayez encore besoin de peaufinage manuel (paramètre du convertisseur peut-être). –

+0

Comme vous pouvez le voir, je passe tout le contrôle au convertisseur, donc pas besoin de params. En tout cas, j'espère l'améliorer quand j'aurai le temps. Je veux qu'il soit basé sur la taille du parent, donc il ne doit pas être une valeur statique comme '40'. – Shimmy

5

Je aime vraiment les solutions créatives qui sont présentées ici, mais je pense que sur le long terme (jeu de mots), nous devrions utiliser ceci:

<TextBlock> 
    <Run FontSize="20">What</Run> 
    <Run FontSize="36">ever</Run> 
    <Run FontSize="12" FontWeight="Bold">FontSize</Run> 
</TextBlock> 

La seule chose qui manque de l'élément Run est liaison de données de la propriété Text mais qui pourrait être ajoutée plus tôt ou plus tard.

Une exécution ne corrigera pas l'alignement des étiquettes et de leurs boîtes de texte, mais pour de nombreuses situations simples, l'exécution fonctionnera très bien.

+0

Gardez à l'esprit que, dans de nombreux cas, les étiquettes sont préférées pour rendre les styles plus faciles à appliquer aux étiquettes sans appliquer à TextBlocks. Étant donné l'exemple d'affiches originales de «Nom: VOTRE NOM ICI», il serait plus approprié d'utiliser une étiquette pour contenir le texte «Nom:». – jpierson

22

Une autre solution assez simple:

1) Utilisez TextBlock commande au lieu des étiquettes. La raison en est que TextBlock est plus léger que Label - voir http://joshsmithonwpf.wordpress.com/2007/07/04/differences-between-label-and-textblock/

2) Utilisez LineHeight et LineStackingStrategy = BlockLineHeight pour votre style TextBlock. Cela permettra d'aligner les deux à leur ligne de base facilement.

<StackPanel Orientation="Horizontal" 
      VerticalAlignment="Center" 
      HorizontalAlignment="Center"> 
    <StackPanel.Resources> 
     <Style TargetType="TextBlock"> 
      <Setter Property="LineHeight" Value="44pt"/> 
      <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/> 
     </Style> 
    </StackPanel.Resources>    
    <TextBlock Text="Name:"/> 
    <TextBlock Text="Itzhak Perlman" FontSize="44"/> 
</StackPanel> 
+0

@Shimmy avez-vous essayé cela? C'est simple et efficace et devrait être la réponse. Fonctionne bien pour moi. – ygoe

2
<TextBlock> 
<InlineUIContainer BaselineAlignment="Baseline"><TextBlock>Small</TextBlock></InlineUIContainer> 
<InlineUIContainer BaselineAlignment="Baseline"><TextBlock FontSize="50">Big</TextBlock></InlineUIContainer> 
</TextBlock> 

Cela devrait fonctionne bien. Expérimentez avec Baseline/Bottom/Center/Top.

1

concepteur XAML prend en charge l'alignement TextBlock contrôles par la ligne de base au moment de la conception:

enter image description here

Cela affecte les marges fixes à vos commandes. Tant que les tailles de police ne changent pas au moment de l'exécution, l'alignement sera conservé.

+0

Oui, cela fonctionne. Peut-être que cette fonctionnalité n'était pas disponible au moment où la question a été posée (2010) mais je confirme que l'alignement peut être fait de cette façon dans WPF designer dans Viusal Studio 2015 (et évidemment aussi dans certaines versions antérieures comme date de cette réponse). Les deux objets ajustés * doivent contenir du texte *, donc si vous alignez par ex. Label et TextBox (qui est généralement vide), ajoutez temporairement du texte à l'intérieur. – miroxlav

Questions connexes