这两天用WPF的MultiDataBinding实现了一个能随TextBlock长度变化而自动调整字符串显示形式,中间部分用省略号(...)表示,代码如下。 internal class OutputToEllipsisDisplayConverter : IMultiValueConverter { public static readonly string EllipsisChars = /*MSG0*/"..."; public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { String path = values[0] as String; double actualWidth = (double)values[1]; double fontSize = (double)values[2]; FontFamily fontFamily = values[3] as FontFamily; return Compact(path, actualWidth, fontSize, fontFamily); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotSupportedException(); } /// <summary> /// Compact the string to show in the TextBlock. /// If the string's length is beyond the controls's ActualWidth, then it will use ellipsis in the string. /// </summary> /// <param name="text">the string content</param> /// <param name="actualWidth">the controls actual width can show the string</param> /// <returns></returns> private static string Compact(string text, double actualWidth, double fontSize, FontFamily fontFamily) { if (string.IsNullOrEmpty(text)) return text; // control is large enough to display the whole text if (MeasureTextWidth(text, fontSize, fontFamily) <= actualWidth) return text; string pre = string.Empty; string mid = text; string post = string.Empty; // split path string into <drive><directory><filename> pre = Path.GetPathRoot(text); mid = Path.GetDirectoryName(text).Substring(pre.Length); post = Path.GetFileName(text); int len = 0; int seg = mid.Length; string fit = string.Empty; // find the longest string that fits into // the control boundaries using bisection method while (seg > 1) { seg -= seg / 2; int left = len + seg; int right = mid.Length; if (left > right) continue; // build and measure a candidate string with ellipsis string tst = mid.Substring(0, left) + EllipsisChars + mid.Substring(right); // restore path with <drive> and <filename> tst = Path.Combine(Path.Combine(pre, tst), post); // candidate string fits into control boundaries, try a longer string // stop when seg <= 1 if (MeasureTextWidth(tst, fontSize, fontFamily) <= actualWidth) { len += seg; fit = tst; } } if (len == 0) // string can't fit into control { // measure "C:/.../filename.ext" fit = Path.Combine(Path.Combine(pre, EllipsisChars), post); // if still not fit then return ".../filename.ext" if (MeasureTextWidth(fit, fontSize, fontFamily) > actualWidth) fit = Path.Combine(EllipsisChars, post); } return fit; } private static double MeasureTextWidth(string text, double fontSize, FontFamily fontFamily) { FormattedText formattedText = new FormattedText( text, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(fontFamily.ToString()), fontSize, Brushes.Black ); return formattedText.WidthIncludingTrailingWhitespace; } } <DockPanel Name="panelOutput" HorizontalAlignment="Stretch"> <TextBlock x:Name="txtOutput" HorizontalAlignment="Stretch" ToolTip="{Binding Path=OutputFile}"> <TextBlock.Text> <MultiBinding Converter="{StaticResource OutputToEllipsisDisplayConverter}"> <Binding Path="OutputFile"/> <Binding Path="ActualWidth" ElementName="panelOutput"/> <Binding Path="FontSize" ElementName="txtOutput"/> <Binding Path="FontFamily" ElementName="txtOutput"/> </MultiBinding> </TextBlock.Text> </TextBlock> </DockPanel>