2009-06-25 23 views
31

Der folgende Textblock wird wie erwartet umgebrochen und zugeschnitten. Die Elipsis "..." wird angezeigt, wenn der Text abgeschnitten wird.Wie kann ich feststellen, ob mein TextBlock-Text abgeschnitten wird?

Ich möchte einen Tooltip über den Text mit dem vollständigen Text anzeigen, aber nur, wenn der Text getrimmt ist. Ich bin mir nicht sicher, wie ich zuverlässig feststellen kann, ob das "..." angezeigt wird oder nicht.

Wie kann ich feststellen, ob der Text getrimmt wird oder nicht?

+4

Perfekt angegeben Frage - nice one. :) –

Antwort

9

Ich habe in letzter Zeit nicht viel WPF gemacht, also bin ich mir nicht sicher, ob Sie das suchen, aber schauen Sie sich diesen Artikel an: Customizing “lookful” WPF controls – Take 2. Es ist ein bisschen komplex, aber es scheint die gleiche Frage zu stellen, die Sie stellen. UPDATE: Die Website scheint weg zu sein, aber Sie können den Artikel in der archive finden.

+0

Der oben erwähnte Algorithmus ist genau das, wonach ich gesucht habe - danke! –

+2

Der Link ist tot :( – l33t

+2

@ l33t Ich brauchte auch den Link, [hier ist eine Kopie der Seite] (http://web.archive.org/web/20130316081653/http://tranxcoder.wordpress.com/ 2008/10/12/customing-lookful-wpf-controls-take-2 /) von der Wayback-Maschine zwischengespeichert –

18

Weil die Verbindung in Aleks Antwort ist unten, fand ich eine cached copy of the link von der Wayback-Maschine. Sie können den im Artikel verlinkten Code nicht herunterladen, daher ist hier eine vorinstallierte Version des Codes enthalten. Es gab ein oder zwei Probleme, mit denen ich beim Versuch, es zum Laufen zu bringen, gelaufen bin, also ist dieser Code etwas anders als der Code in den Beispielen im Artikel.

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

Good ups, die die Wayback Maschine so verwenden. Ich vergesse immer wieder, dass das Ding da draußen ist. – MarqueIV

+0

Es ist bereits 'Opt-In', weil Sie es mit 'AutomaticToolTipEnabled' aktivieren müssen. (Als Antwort auf den Code-Kommentar 'Anstatt * alle * TextBlocks TextBlockService-Stile zu erzwingen, mit x: Key ermöglicht eine freundlichere Opt-in-Modell. ') –

+0

@KellyElton Ich wollte nicht unnötige Trigger auf jeder setzen TextBlock im Fenster.Sie können es einfach auf jedes Textfeld anwenden, wenn Sie feststellen, dass es keinen Overhead verursacht. Die App für die es geschrieben wurde, war eine Dateneingabe-App mit einer Tabelle mit editierbaren Zellen, die sich zu einigen hundert Textfeldern addierte. –

2

Die Lösung oben für mich nicht funktioniert, wenn der Textblock Teil eines ListBoxItem Datatemplate ist. Ich schlage vor, eine andere Lösung:

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

Dies ist eine schöne, saubere Lösung. Allerdings funktionierte es nicht für mich: mein TextBlock wird getrimmt (ich sehe die Ellipse), aber meine DesiredSize.Width entspricht ActualWidth. – thehelix

0

auf bidy Antwort erweitern. Dadurch wird ein TextBlock erstellt, der nur den Tooltip anzeigt, wenn nicht der gesamte Text angezeigt wird. Die QuickInfo wird auf den Inhalt angepasst (im Gegensatz zur Standard-QuickInfo, die eine einzeilige Box mit abgeschnittenem Text bleibt).

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; 
     } 
    } 
} 

XAML Nutzung:

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

    <components:CustomTextBlock Text="{Binding Details}" TextTrimming="CharacterEllipsis" /> 
Verwandte Themen