2009-06-25 35 views
31

El siguiente textblock se ajusta y recorta como se esperaba. La elipsis "..." se muestra cuando se recorta el texto.¿Cómo puedo determinar si mi texto TextBlock está siendo recortado?

<TextBlock 
    MaxWidth="60" 
    MaxHeight="60" 
    Text="This is some long text which I would like to wrap." 
    TextWrapping="Wrap" 
    TextTrimming="CharacterEllipsis" /> 

Me gustaría mostrar una información sobre herramientas sobre el texto con el texto completo, pero solo si el texto está recortado. No estoy seguro de cómo determinar de manera confiable si se muestra el "..." o no.

¿Cómo puedo determinar si el texto está siendo recortado o no?

+4

pregunta declarado Perfectamente - agradable. :) –

Respuesta

9

No he hecho mucho WPF últimamente, así que no estoy seguro de si esto es lo que estás buscando, pero echa un vistazo a este artículo: Customizing “lookful” WPF controls – Take 2. Es un poco complejo, pero parece abordar la misma pregunta que estás haciendo. ACTUALIZACIÓN: El sitio web parece haber desaparecido, pero puede encontrar el artículo en el archive.

+0

El algoritmo mencionado en el artículo anterior es exactamente lo que estaba buscando, ¡gracias! –

+2

El enlace está muerto :( – l33t

+2

@ l33t También necesitaba el enlace, [aquí hay una copia de la página] (http://web.archive.org/web/20130316081653/http://tranxcoder.wordpress.com/ 2008/10/12/customizing-lookful-wpf-controls-take-2 /) guardado en caché por Wayback Machine –

18

Como el enlace en la respuesta de Alek está caído, encontré un cached copy of the link de la máquina de retorno. No puede descargar el código vinculado en el artículo, por lo que aquí hay una versión premontada del código. Hubo uno o dos problemas con los que me encontré tratando de hacerlo funcionar, por lo que este código es ligeramente diferente del código en los ejemplos del artículo.

using System; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Media; 

namespace TextBlockService 
{ 
    //Based on the project from http://web.archive.org/web/20130316081653/http://tranxcoder.wordpress.com/2008/10/12/customizing-lookful-wpf-controls-take-2/ 
    public static class TextBlockService 
    { 
     static TextBlockService() 
     { 
      // Register for the SizeChanged event on all TextBlocks, even if the event was handled. 
      EventManager.RegisterClassHandler(
       typeof(TextBlock), 
       FrameworkElement.SizeChangedEvent, 
       new SizeChangedEventHandler(OnTextBlockSizeChanged), 
       true); 
     } 


     private static readonly DependencyPropertyKey IsTextTrimmedKey = DependencyProperty.RegisterAttachedReadOnly("IsTextTrimmed", 
      typeof(bool), 
      typeof(TextBlockService), 
      new PropertyMetadata(false)); 

     public static readonly DependencyProperty IsTextTrimmedProperty = IsTextTrimmedKey.DependencyProperty; 

     [AttachedPropertyBrowsableForType(typeof(TextBlock))] 
     public static Boolean GetIsTextTrimmed(TextBlock target) 
     { 
      return (Boolean)target.GetValue(IsTextTrimmedProperty); 
     } 


     public static readonly DependencyProperty AutomaticToolTipEnabledProperty = DependencyProperty.RegisterAttached(
      "AutomaticToolTipEnabled", 
      typeof(bool), 
      typeof(TextBlockService), 
      new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits)); 

     [AttachedPropertyBrowsableForType(typeof(DependencyObject))] 
     public static Boolean GetAutomaticToolTipEnabled(DependencyObject element) 
     { 
      if (null == element) 
      { 
       throw new ArgumentNullException("element"); 
      } 
      return (bool)element.GetValue(AutomaticToolTipEnabledProperty); 
     } 

     public static void SetAutomaticToolTipEnabled(DependencyObject element, bool value) 
     { 
      if (null == element) 
      { 
       throw new ArgumentNullException("element"); 
      } 
      element.SetValue(AutomaticToolTipEnabledProperty, value); 
     } 

     private static void OnTextBlockSizeChanged(object sender, SizeChangedEventArgs e) 
     { 
      TriggerTextRecalculation(sender); 
     } 

     private static void TriggerTextRecalculation(object sender) 
     { 
      var textBlock = sender as TextBlock; 
      if (null == textBlock) 
      { 
       return; 
      } 

      if (TextTrimming.None == textBlock.TextTrimming) 
      { 
       textBlock.SetValue(IsTextTrimmedKey, false); 
      } 
      else 
      { 
       //If this function is called before databinding has finished the tooltip will never show. 
       //This invoke defers the calculation of the text trimming till after all current pending databinding 
       //has completed. 
       var isTextTrimmed = textBlock.Dispatcher.Invoke(() => CalculateIsTextTrimmed(textBlock), DispatcherPriority.DataBind); 
       textBlock.SetValue(IsTextTrimmedKey, isTextTrimmed); 
      } 
     } 

     private static bool CalculateIsTextTrimmed(TextBlock textBlock) 
     { 
      if (!textBlock.IsArrangeValid) 
      { 
       return GetIsTextTrimmed(textBlock); 
      } 

      Typeface typeface = new Typeface(
       textBlock.FontFamily, 
       textBlock.FontStyle, 
       textBlock.FontWeight, 
       textBlock.FontStretch); 

      // FormattedText is used to measure the whole width of the text held up by TextBlock container 
      FormattedText formattedText = new FormattedText(
       textBlock.Text, 
       System.Threading.Thread.CurrentThread.CurrentCulture, 
       textBlock.FlowDirection, 
       typeface, 
       textBlock.FontSize, 
       textBlock.Foreground); 

      formattedText.MaxTextWidth = textBlock.ActualWidth; 

      // When the maximum text width of the FormattedText instance is set to the actual 
      // width of the textBlock, if the textBlock is being trimmed to fit then the formatted 
      // text will report a larger height than the textBlock. Should work whether the 
      // textBlock is single or multi-line. 
      // The "formattedText.MinWidth > formattedText.MaxTextWidth" check detects if any 
      // single line is too long to fit within the text area, this can only happen if there is a 
      // long span of text with no spaces. 
      return (formattedText.Height > textBlock.ActualHeight || formattedText.MinWidth > formattedText.MaxTextWidth); 
     } 

    } 
} 
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:tbs="clr-namespace:TextBlockService"> 
    <!-- 
    Rather than forcing *all* TextBlocks to adopt TextBlockService styles, 
    using x:Key allows a more friendly opt-in model. 
    --> 

    <Style TargetType="TextBlock" x:Key="TextBlockService"> 
     <Style.Triggers> 
      <MultiTrigger> 
       <MultiTrigger.Conditions> 
        <Condition Property="tbs:TextBlockService.AutomaticToolTipEnabled" Value="True" /> 
        <Condition Property="tbs:TextBlockService.IsTextTrimmed" Value="True"/> 
       </MultiTrigger.Conditions> 

       <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Text}" /> 
      </MultiTrigger> 
     </Style.Triggers> 
    </Style> 
</ResourceDictionary> 
+0

Mejoras usando la máquina Wayback de esa manera. Sigo olvidando que esa cosa está ahí afuera. – MarqueIV

+0

Ya es 'Opt-In' porque tiene que habilitarlo con 'AutomaticToolTipEnabled'. (En respuesta al comentario del código 'En lugar de forzar * todos * TextBlocks a adoptar estilos TextBlockService, usando x: Key permite un modelo de aceptación más amigable. ') –

+0

@KellyElton No quería poner desencadenantes innecesarios en cada TextBlock en la ventana.Puede hacer que se aplique a cada cuadro de texto si encuentra que no causa ningún gasto adicional. La aplicación para la que se escribió fue una aplicación de entrada de datos con una tabla con celdas editables, que se agregó a unos cientos de cuadros de texto. –

2

La solución anterior no funcionó para mí si el TextBlock es parte de un ListBoxItem DataTemplate. propongo otra solución:

public class MyTextBlock : System.Windows.Controls.TextBlock 
{ 

    protected override void OnToolTipOpening(WinControls.ToolTipEventArgs e) 
    { 
     if (TextTrimming != TextTrimming.None) 
     { 
      e.Handled = !IsTextTrimmed(); 
     } 
    } 

    private bool IsTextTrimmed() 
    { 
     Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); 
     return ActualWidth < DesiredSize.Width; 
    } 
} 

XAML:

<MyTextBlock Text="{Binding Text}" TextTrimming="CharacterEllipsis" ToolTip="{Binding Text}" /> 
+0

Esta es una buena solución limpia. Sin embargo, no funcionó para mí: mi TextBlock está siendo recortado (veo las elipsis), pero mi DesiredSize.Width es igual a ActualWidth. – thehelix

0

Ampliando la respuesta de Bidy. Esto creará un TextBlock que solo muestra la información sobre herramientas cuando no se muestra todo el texto. La información sobre herramientas se redimensionará a los contenidos (a diferencia de la información sobre herramientas predeterminada que seguirá siendo un cuadro de una línea con el texto cortado).

using System; 
using System.Windows; 
using System.Windows.Controls; 

namespace Optimizers.App4Sales.LogViewer.Components 
{ 
    public class CustomTextBlock : TextBlock 
    { 
     protected override void OnInitialized(EventArgs e) 
     { 
      // we want a tooltip that resizes to the contents -- a textblock with TextWrapping.Wrap will do that 
      var toolTipTextBlock = new TextBlock(); 
      toolTipTextBlock.TextWrapping = TextWrapping.Wrap; 
      // bind the tooltip text to the current textblock Text binding 
      var binding = GetBindingExpression(TextProperty); 
      if (binding != null) 
      { 
       toolTipTextBlock.SetBinding(TextProperty, binding.ParentBinding); 
      } 

      var toolTipPanel = new StackPanel(); 
      toolTipPanel.Children.Add(toolTipTextBlock); 
      ToolTip = toolTipPanel; 

      base.OnInitialized(e); 
     } 

     protected override void OnToolTipOpening(ToolTipEventArgs e) 
     { 
      if (TextTrimming != TextTrimming.None) 
      { 
       e.Handled = !IsTextTrimmed(); 
      } 
     } 

     private bool IsTextTrimmed() 
     { 
      Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); 
      return ActualWidth < DesiredSize.Width; 
     } 
    } 
} 

uso de XAML:

<Window ... 
     xmlns:components="clr-namespace:MyComponents" 
    ... > 

    <components:CustomTextBlock Text="{Binding Details}" TextTrimming="CharacterEllipsis" /> 
Cuestiones relacionadas