代码之家  ›  专栏  ›  技术社区  ›  Andrew Jackson

如何确定文本块文本是否被剪裁?

  •  36
  • Andrew Jackson  · 技术社区  · 17 年前

    以下文本块按预期包装和贴面。删除文本时将显示省略号“…”。

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

    我希望在全文文本上显示工具提示,但仅当文本被剪裁时才显示。我不知道如何可靠地确定“…”是否被显示。

    如何确定文本是否被剪裁?

    4 回复  |  直到 8 年前
        1
  •  9
  •   Alek Davis    10 年前

    我最近没有做过很多WPF,所以我不确定这是否是您要找的,但是请看这篇文章: Customizing “lookful” WPF controls – Take 2 . 这有点复杂,但似乎能解决你问的同一个问题。更新:网站似乎不见了,但你可以在 archive .

        2
  •  22
  •   Scott Chamberlain    8 年前

    因为Alek答案中的链接已断开,我发现 cached copy of the link 从回程机器。您不能下载本文中链接的代码,因此这里是代码的预组装版本。我在尝试使它工作时遇到了一两个问题,因此这段代码与本文示例中的代码稍有不同。

    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>
    
        3
  •  4
  •   Christian Regli    11 年前

    如果textBlock是ListBoxItem数据模板的一部分,则上述解决方案对我不起作用。 我建议另一个解决方案:

    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}" />
    
        4
  •  0
  •   CameO73    9 年前

    扩展Bidy的答案。这将创建一个文本块,该文本块仅在不显示所有文本时显示工具提示。工具提示将根据内容调整大小(与默认工具提示相反,默认工具提示将保留一行框,文本被剪切)。

    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用法:

        <Window ...
            xmlns:components="clr-namespace:MyComponents"
         ... >
    
        <components:CustomTextBlock Text="{Binding Details}" TextTrimming="CharacterEllipsis" />