2012-01-19 29 views
16

¿Hay alguna manera de especificar el recorte de texto en un TextBlock desde el lado izquierdo?TextTrimming from left

He arreglo para lograr dos de cada tres escenarios (siendo el tercero el que necesito):

  1. recorte regular

    <TextBlock 
        VerticalAlignment="Center" 
        Width="80" 
        TextTrimming="WordEllipsis" 
        Text="A very long text that requires trimming" /> 
    
    // Result: "A very long te..." 
    
  2. izquierda recorte

    <TextBlock 
        VerticalAlignment="Center" 
        Width="80" 
        FlowDirection="RightToLeft" 
        TextTrimming="WordEllipsis" 
        Text="A very long text that requires trimming." /> 
    
    // Result: "...A very long te" 
    
  3. Recorte a la izquierda donde se ve el final del texto

    // Desired result: "...uires trimming" 
    

¿Alguien sabe si esto es posible? Gracias.

+0

que lo que necesita TextTrimming = "CharacterEllipsis" en lugar de WordEllipsis. – Justin

Respuesta

3

No se puede hacer esto fuera de la caja, pero puedo pensar en dos cosas que podrían funcionar:

1) Crear una propiedad adjunta para TextBlock llama algo así como LeftTrimmingText. Luego, establecería esta propiedad en lugar de la propiedad Text. P.ej.

<TextBlock my:TextBlockHelper.LeftTrimmingText="A very long text that requires trimming." /> 

La propiedad adjunta sería calcular cuántos caracteres se podría exhibir en realidad, a continuación, establezca la propiedad Text de TextBlock en consecuencia.

2) Cree su propia clase que envuelva un TextBlock y agregue sus propias propiedades para cuidar la lógica requerida.

Creo que la primera opción es más fácil.

+0

Lamentablemente la primera opción no será fácil. Silverlight no expone una API de procesamiento/medición de texto. Lo mejor que puede hacer es detectar * cuando * se produce el recorte. Vea esta publicación en el blog: http://www.scottlogic.co.uk/blog/colin/2011/01/showing-tooltips-on-trimmed-textblock-silverlight/ – ColinE

+2

ColinE: La API de medición es el propio TextBlock. Básicamente, creas un nuevo TextBlock temporal en el código y sigues agregando caracteres hasta que el Ancho real sea mayor de lo que deseas. El TextBlock temporal no necesita representarse ni siquiera en el árbol visual. – RobSiklos

+0

@RobSiklow buen punto, eso funcionaría. – ColinE

0

No sé si es un error tipográfico, pero falta el full stop al final de su 'resultado deseado'. Asumiré que no lo quieres. Como sabe cuántos caracteres se deben mostrar, puede obtener una subcadena de toda la cadena y mostrarla. Por ejemplo,

string origText = "A very long text that requires trimming."; 

//15 because the first three characters are replaced 
const int MAXCHARACTERS = 15; 

//MAXCHARACTERS - 1 because you don't want the full stop 
string sub = origText.SubString(origText.Length-MAXCHARACTERS, MAXCHARACTERS-1); 

string finalString = "..." + sub; 
textBlock.Text = finalString; 

Si no sabe cuántos caracteres desea de forma avanzada, puede realizar un cálculo para determinarlo. En su ejemplo, un ancho de 80 da como resultado 17 caracteres, puede usar esa relación si el ancho cambia.

+5

No lo olvides: el ancho del carácter depende de la fuente – RobSiklos

+0

Sí, supongo que OP conocerá la fuente de antemano. De lo contrario, OP puede usar este método para determinar el ancho del texto en función de la fuente: http://stackoverflow.com/questions/913053/how-do-you-determine-the-width-of-the-text-in -a-wpf-treeviewitem-at-run-time – keyboardP

+1

"En su ejemplo, un ancho de 80 da como resultado 17 caracteres, puede usar esa relación si el ancho cambia". - Cada personaje puede tener (y a menudo tiene) su ancho individual. Usar una razón de una secuencia de muestra no producirá ningún resultado exacto. –

0

Va a tener que crear este efecto usted mismo. @KeyboardP da una respuesta sobre cómo hacer el recorte, sin embargo, la parte más difícil es saber cuánto recortar. Probablemente la mejor solución es tomar parte del código encontrado en la web que proporcionó recorte de texto antes de que se agregara al marco de Silverlight. Por ejemplo:

http://nerdplusart.com/texttrimming-textblock-for-silverlight

Será un poco de trabajo, sin embargo, la adaptación no debería ser demasiado difícil,

3

Si no se preocupan por las elipses, pero sólo quiere ver el final del texto en lugar del comienzo cuando se corta, puede envolver TextBlock dentro de otro contenedor y establecer su Alineación Horizontal a la Derecha. Esto lo cortará como quieras, pero sin el elipse.

<Grid> 
    <TextBlock Text="Really long text to cutoff." HorizontalAlignment="Right"/> 
</Grid> 
2

Este estilo hará el trabajo. El truco es redefinir una plantilla de control para la etiqueta. El contenido se coloca dentro de un lienzo de recorte y se alinea a la derecha del lienzo. El ancho mínimo del contenido es el ancho del lienzo por lo que el texto del contenido se alineará a la izquierda si hay suficiente espacio y se alinea a la derecha cuando se recortan.

Las elipses se activan si el ancho del contenido es mayor que el lienzo.

<Style x:Key="LeftEllipsesLabelStyle" 
     TargetType="{x:Type Label}"> 
    <Setter Property="Foreground" 
      Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" /> 
    <Setter Property="Background" 
      Value="Transparent" /> 
    <Setter Property="Padding" 
      Value="5" /> 
    <Setter Property="HorizontalContentAlignment" 
      Value="Left" /> 
    <Setter Property="VerticalContentAlignment" 
      Value="Top" /> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type Label}"> 
       <Grid > 
        <Grid.Resources> 
         <LinearGradientBrush x:Key="HeaderBackgroundOpacityMask" StartPoint="0,0" EndPoint="1,0"> 
          <GradientStop Color="Black" Offset="0"/> 
          <GradientStop Color="Black" Offset="0.5"/> 
          <GradientStop Color="Transparent" Offset="1"/> 
         </LinearGradientBrush> 
        </Grid.Resources> 

        <Canvas x:Name="Canvas" 
          ClipToBounds="True" 
          DockPanel.Dock="Top" 
          Height="{Binding ElementName=Content, Path=ActualHeight}"> 

         <Border 
          BorderBrush="{TemplateBinding BorderBrush}" 
          Canvas.Right="0" 
          Canvas.ZIndex="0" 
          BorderThickness="{TemplateBinding BorderThickness}" 
          Background="{TemplateBinding Background}" 
          Padding="{TemplateBinding Padding}" 
          MinWidth="{Binding ElementName=Canvas, Path=ActualWidth}" 
          SnapsToDevicePixels="true" 
          x:Name="Content" 
         > 
          <ContentPresenter 
           HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
           Content="{Binding RelativeSource={RelativeSource AncestorType=Label}, Path=Content}" 
           RecognizesAccessKey="True" 
           SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
           VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
          > 
           <ContentPresenter.Resources> 
            <Style TargetType="TextBlock"> 
             <Setter Property="FontSize" Value="{Binding FontSize, RelativeSource={RelativeSource AncestorType={x:Type Label}}}"/> 
             <Setter Property="FontWeight" Value="{Binding FontWeight, RelativeSource={RelativeSource AncestorType={x:Type Label}}}"/> 
             <Setter Property="FontStyle" Value="{Binding FontStyle, RelativeSource={RelativeSource AncestorType={x:Type Label}}}"/> 
             <Setter Property="FontFamily" Value="{Binding FontFamily, RelativeSource={RelativeSource AncestorType={x:Type Label}}}"/> 
            </Style> 
           </ContentPresenter.Resources> 

          </ContentPresenter> 
         </Border> 
         <Label 
          x:Name="Ellipses" 
          Canvas.Left="0" 
          Canvas.ZIndex="10" 
          FontWeight="{TemplateBinding FontWeight}" 
          FontSize="{TemplateBinding FontSize}" 
          FontFamily="{TemplateBinding FontFamily}" 
          FontStyle="{TemplateBinding FontStyle}" 
          VerticalContentAlignment="Center" 
          OpacityMask="{StaticResource HeaderBackgroundOpacityMask}" 
          Background="{TemplateBinding Background}" 
          Foreground="RoyalBlue" 
          Height="{Binding ElementName=Content, Path=ActualHeight}" 
          Content="...&#160;&#160;&#160;"> 
          <Label.Resources> 
           <Style TargetType="Label"> 
            <Style.Triggers> 
             <DataTrigger Value="true"> 
              <DataTrigger.Binding> 
               <MultiBinding Converter="{StaticResource GteConverter}"> 
                <Binding ElementName="Canvas" Path="ActualWidth"/> 
                <Binding ElementName="Content" Path="ActualWidth"/> 
               </MultiBinding> 
              </DataTrigger.Binding> 
              <Setter Property="Visibility" Value="Hidden"/> 
             </DataTrigger> 
            </Style.Triggers> 
           </Style> 
          </Label.Resources> 

         </Label> 
        </Canvas> 

       </Grid> 
       <ControlTemplate.Triggers> 
        <Trigger Property="IsEnabled" 
          Value="false"> 
         <Setter Property="Foreground" 
           Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" /> 
        </Trigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Hay un par de clases de utilidad aquí

GteConverter

<c:GteConverter x:Key="GteConverter"/> 

que es

public class RelationalValueConverter : IMultiValueConverter 
{ 
    public enum RelationsEnum 
    { 
     Gt,Lt,Gte,Lte,Eq,Neq 
    } 

    public RelationsEnum Relations { get; protected set; } 

    public RelationalValueConverter(RelationsEnum relations) 
    { 
     Relations = relations; 
    } 

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     if(values.Length!=2) 
      throw new ArgumentException(@"Must have two parameters", "values"); 

     var v0 = values[0] as IComparable; 
     var v1 = values[1] as IComparable; 

     if(v0==null || v1==null) 
      throw new ArgumentException(@"Must arguments must be IComparible", "values"); 

     var r = v0.CompareTo(v1); 

     switch (Relations) 
     { 
      case RelationsEnum.Gt: 
       return r > 0; 
       break; 
      case RelationsEnum.Lt: 
       return r < 0; 
       break; 
      case RelationsEnum.Gte: 
       return r >= 0; 
       break; 
      case RelationsEnum.Lte: 
       return r <= 0; 
       break; 
      case RelationsEnum.Eq: 
       return r == 0; 
       break; 
      case RelationsEnum.Neq: 
       return r != 0; 
       break; 
      default: 
       throw new ArgumentOutOfRangeException(); 
     } 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

y

public class GtConverter : RelationalValueConverter 
{ 
    public GtConverter() : base(RelationsEnum.Gt) { } 
} 
public class GteConverter : RelationalValueConverter 
{ 
    public GteConverter() : base(RelationsEnum.Gte) { } 
} 
public class LtConverter : RelationalValueConverter 
{ 
    public LtConverter() : base(RelationsEnum.Lt) { } 
} 
public class LteConverter : RelationalValueConverter 
{ 
    public LteConverter() : base(RelationsEnum.Lte) { } 
} 
public class EqConverter : RelationalValueConverter 
{ 
    public EqConverter() : base(RelationsEnum.Eq) { } 
} 
public class NeqConverter : RelationalValueConverter 
{ 
    public NeqConverter() : base(RelationsEnum.Neq) { } 
} 

Aquí está funcionando.

enter image description here enter image description here enter image description here

+0

Por alguna razón, la etiqueta OpacityMask no funcionaba correctamente para mí, pero logré obtener el efecto que quería usando la propiedad Background: soluciones geniales, ¡gracias! – Wolfshead

+0

Parece la solución más limpia. El código no funcionó para mí, no muestra ningún texto. –