2013-12-12 14 views
6

Mi butto al sodo: C'è un modo per dire al WPF TextBlock di misurarsi in modo tale che la sua dimensione non cambia quando i suoi FontWeight cambiamenti?Come posso avere un TextBlock WPF con le stesse dimensioni per tutti i pesi dei font?

Ho un TextBlock che cambia font basati dinamicamente su uno stile. Il TextBlock è all'interno di un RadioButton quindi è grassetto quando selezionato, Normale altrimenti:

<Style x:Key="BoldWhenChecked" TargetType="RadioButton"> 
    <Style.Triggers> 
     <Trigger Property="IsChecked" Value="True"> 
      <Setter Property="TextElement.FontWeight" Value="Bold" /> 
     </Trigger> 
    </Style.Triggers> 
</Style 

e qui ci sono i pulsanti di opzione stessi:

<StackPanel Orientation="Horizontal"> 
    <RadioButton Style="{StaticResource BoldWhenChecked}"> 
     <TextBlock Text="Item 1" /> 
    </RadioButton> 
    <RadioButton Style="{StaticResource BoldWhenChecked}"> 
     <TextBlock Text="Item 2" /> 
    </RadioButton> 
    <RadioButton Style="{StaticResource BoldWhenChecked}"> 
     <TextBlock Text="Item 3" /> 
    </RadioButton> 
    etc... 
</StackPanel> 

Purtroppo, dal momento che non sto usando un font a larghezza fissa la larghezza di TextBlock cambia quando cambia il peso del font e l'intero pannello dei pulsanti di opzione si sposta di conseguenza, il che è visivamente fastidioso.

+0

Potete mostrare l'xaml per i 'RadioButtons'? – clcto

+0

Stai usando un modello di controllo per i RadioButton? –

risposta

4

ho creato un work-around per questo con l'aggiunta di una nascosta TextBlock nel contenuto del RadioButton con il suo FontStyle esplicitamente impostato Bold:

<RadioButton Style="{StaticResource BoldWhenChecked}"> 
    <Grid> 
     <TextBlock Text="Item 1" /> 
     <TextBlock Text="Item 1" FontStyle="Bold" Visibility="Hidden" /> 
    </Grid> 
</RadioButton> 

In questo modo quando si seleziona la RadioButton e il visibile TextBlock è reso grassetto, la larghezza non cambia perché il TextBlock nascosto ha già dimensionato la griglia in modo appropriato.

0

Personalmente mi piacerebbe provare a organizzare i miei layout quindi meglio che non erano dipendenti dalla dimensione dei pulsanti di opzione. Detto questo, se insisti assolutamente a farlo in questo modo, dovrai impostare la larghezza di ciascun TextBlock in base alle sue dimensioni in grassetto e in un modo che lo farà aggiornare se hai bisogno di cambiare il testo e/o famiglia di caratteri ecc. Per fare ciò è necessario associare la proprietà Width a un convertitore che accetta tutti gli altri valori.

Inizia con uno stile che imposta la larghezza della casella di testo:

<Style TargetType="{x:Type TextBlock}"> 
     <Setter Property="Width"> 
      <Setter.Value> 
       <MultiBinding Converter="{StaticResource TextToWidthConverter}"> 
        <Binding Path="Text" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/> 
        <Binding Path="FontFamily" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/> 
        <Binding Path="FontStyle" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/> 
        <Binding Path="FontStretch" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/> 
        <Binding Path="FontSize" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/> 
       </MultiBinding> 
      </Setter.Value> 
     </Setter> 
    </Style> 

Ora aggiungere il codice per il convertitore valore stesso (notare che io sto usando code posted by Clarke Kent in another StackOverflow answer per misurare il testo):

public class TextToWidthConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     var text = values[0] as String; 
     var fontFamily = values[1] as FontFamily; 
     var fontStyle = (FontStyle)values[2]; 
     var fontStretch = (FontStretch)values[3]; 
     var fontSize = (Double)values[4]; 
     var size = MeasureText(text, fontFamily, fontStyle, FontWeights.Bold, fontStretch, fontSize); 
     return size.Width; 
    } 

    public object[] ConvertBack(object values, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 

    /// <summary> 
    /// Get the required height and width of the specified text. Uses Glyph's 
    /// </summary> 
    public static Size MeasureText(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize) 
    { 
     Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch); 
     GlyphTypeface glyphTypeface; 

     if (!typeface.TryGetGlyphTypeface(out glyphTypeface)) 
     { 
      return MeasureTextSize(text, fontFamily, fontStyle, fontWeight, fontStretch, fontSize); 
     } 

     double totalWidth = 0; 
     double height = 0; 

     for (int n = 0; n < text.Length; n++) 
     { 
      ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]]; 

      double width = glyphTypeface.AdvanceWidths[glyphIndex] * fontSize; 

      double glyphHeight = glyphTypeface.AdvanceHeights[glyphIndex] * fontSize; 

      if (glyphHeight > height) 
      { 
       height = glyphHeight; 
      } 

      totalWidth += width; 
     } 

     return new Size(totalWidth, height); 
    } 

    /// <summary> 
    /// Get the required height and width of the specified text. Uses FortammedText 
    /// </summary> 
    public static Size MeasureTextSize(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize) 
    { 
     FormattedText ft = new FormattedText(text, 
              CultureInfo.CurrentCulture, 
              FlowDirection.LeftToRight, 
              new Typeface(fontFamily, fontStyle, fontWeight, fontStretch), 
              fontSize, 
              Brushes.Black); 
     return new Size(ft.Width, ft.Height); 
    } 
} 

Ma di nuovo, in una vera app proverei a risolverlo correttamente inserendoli in una griglia o qualcosa del genere.

+0

"... inserendoli in una griglia o qualcosa del genere." Come funzionerebbe? Sarebbe esattamente quello che sto cercando, ma non ho trovato un modo per farlo. – kcnygaard

0

Da your comment here:

"... mettendoli in una griglia o qualcosa del genere." Come funzionerebbe? Sarebbe esattamente quello che sto cercando, ma non ho trovato un modo per farlo.

So che questa è una vecchia domanda. E anche se in realtà non lo dici nella domanda, né la domanda include un buono, Minimal, Complete, Verifiable example che illustra pienamente il tuo scenario, sospetto che tu abbia (o piuttosto, abbia) bisogno di sapere dove è il gruppo di pulsanti di opzione non modificare le dimensioni come cambia lo stile del carattere, ma do ridimensionare in qualche modo in modo dinamico altrimenti. A tale scopo, penso che il tuo lavoro con lo TextBlock nascosto non sia male.

Ma direi che, se è possibile controllare il layout della tutto il gruppo di pulsanti di opzione dall'esterno, quindi utilizzando uno degli elementi di rete per organizzare i pulsanti di opzione stessi sarebbe meglio, come suggerito da Mark nel suo risposta.

La chiave è che gli oggetti della griglia possono distribuire le larghezze e le altezze in modo uniforme su colonne e righe, e quindi possono disporre i pulsanti di opzione indipendentemente dalle dimensioni nominali desiderate.

Ad esempio:

<Window x:Class="TestSO20556328BoldRadioButtons.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 

    <Window.Resources> 
    <p:Style x:Key="BoldWhenChecked" TargetType="RadioButton"> 
     <p:Style.Triggers> 
     <Trigger Property="IsChecked" Value="True"> 
      <Setter Property="TextElement.FontWeight" Value="Bold" /> 
     </Trigger> 
     </p:Style.Triggers> 
    </p:Style> 
    </Window.Resources> 

    <StackPanel> 
    <UniformGrid Columns="3"> 
     <RadioButton Style="{StaticResource BoldWhenChecked}"> 
     <TextBlock Text="Item 1" /> 
     </RadioButton> 
     <RadioButton Style="{StaticResource BoldWhenChecked}"> 
     <TextBlock Text="Item 2" /> 
     </RadioButton> 
     <RadioButton Style="{StaticResource BoldWhenChecked}"> 
     <TextBlock Text="Item 3" /> 
     </RadioButton> 
    </UniformGrid> 

    <Grid> 
     <Grid.ColumnDefinitions> 
     <ColumnDefinition/> 
     <ColumnDefinition/> 
     <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <RadioButton Style="{StaticResource BoldWhenChecked}"> 
     <TextBlock Text="Item 1" /> 
     </RadioButton> 
     <RadioButton Style="{StaticResource BoldWhenChecked}" Grid.Column="1"> 
     <TextBlock Text="Item 2" /> 
     </RadioButton> 
     <RadioButton Style="{StaticResource BoldWhenChecked}" Grid.Column="2"> 
     <TextBlock Text="Item 3" /> 
     </RadioButton> 
    </Grid> 
    </StackPanel> 
</Window> 

solo per mostrare un paio di diversi modi per affrontare il problema.

Si noti che quanto sopra può funzionare anche se in qualche modo si sta tentando di dimensionare il gruppo di pulsanti di opzione in modo dinamico in qualche modo. Ma senza ulteriori specificità nella domanda, non sarei in grado di dire quale approccio specifico raggiungerebbe questo obiettivo.

Problemi correlati