2010-12-27 2 views
9

Ich bin sicher, dass ich etwas offensichtlich fehlen, habe ich einen Bereich, in dem ich Text zeichnen möchte. Ich kenne seine (die Fläche) Höhe und Breite. Ich möchte wissen, wie viele Zeichen/Wörter in die Breite passen, vorzugsweise Zeichen. Zweite Frage, wenn die Zeile zu lang ist, möchte ich eine zweite Zeile zeichnen, also denke ich, dass ich auch die Höhe des Textes bekommen muss, einschließlich was immer es für die richtige vertikale Auffüllung hält?Länge der Zeichenfolge, die in einer bestimmten Breite passt

Ich würde auch lieber die Umkehrung wissen, d. H. Wie viele Zeichen ich in einer bestimmten Breite passen kann.

Ich nehme an, die Tatsache, dass WPF nicht auf Pixel beschränkt ist, wird die Antwort beeinflussen?

Letztendlich plane ich, Text um unregelmäßig geformte Bilder zu verpacken, die in den Text eingebettet sind.

Alle Zeiger in die richtige Richtung wären toll.

Dank

Antwort

15

Für WPF können Sie die FormattedText Klasse verwenden, um zu berechnen, wie viel Breite eine gegebene Textzeichenfolge verwenden wird - es hängt vom tatsächlichen Text ab.

Beispiel:

FormattedText formattedText = new FormattedText("Hello Stackoverflow", 
               System.Globalization.CultureInfo.GetCultureInfo("en-us"), 
               FlowDirection.LeftToRight, 
               new Typeface("Arial"), FontSize =14, Brushes.Black); 
double textWidth = formattedText.Width; 

Getting a sub-String für eine gegebene Breite (vereinfacht):

string text = GetSubStringForWidth("Hello Stackoverflow", 55); 
... 
public string GetSubStringForWidth(string text, double width) 
{ 
    if (width <= 0) 
     return ""; 

    int length = text.Length; 
    string testString; 

    while(true)//0 length string will always fit 
    { 
     testString = text.Substring(0, length); 
     FormattedText formattedText = new FormattedText(testString, 
                 CultureInfo.GetCultureInfo("en-us"), 
                 FlowDirection.LeftToRight, 
                 new Typeface("Arial"), 
                 FontSize = 14, 
                 Brushes.Black); 
     if(formattedText.Width <= width) 
      break; 
     else 
      length--; 
    } 
    return testString; 
} 
+0

Ausgezeichneter Start, haben wir irgendeine Idee, wie man das Gegenteil tun kann oder muss ich eine Art Dividieren anwenden und Strategie erobern? d. h. Messen "Bin ich zu lang, um in diesen Raum zu passen", dann, wenn> verfügbare Breite, Messen "Bin ich zu lang" und wenn es zu kurz ist, versuche etwas dazwischen? – Ian

+0

@Ian: Es sollte einfach sein, eine Methode zu schreiben, die die Anzahl der Zeichen zurückgibt, die zu einer bestimmten Breite passen, würde ich mit etwas wie meine Bearbeitung oben (offensichtlich vereinfacht) beginnen, und wenn Leistung ein Problem ist, gehen Sie zu etwas komplizierter - Ich habe keine Messungen gemacht. – BrokenGlass

+0

Ja. Wie widde genau ist jeder char einer unbekannten Zeichenfolge? Unmöglich, kann nur "angenommen" (gemittelt) werden. – TomTom

-1

Dies ist nicht möglich, da Zeichen unterschiedliche Länge haben, zum Beispiel W ist viel breiter als i (es sei denn, Sie eine Schriftart wie Courier New verwenden).

+1

"A fond wie Courier New" - http://en.wikipedia.org/wiki/Monospaced_font –

+0

Es ist nicht unmöglich. Die ganze Typografie hängt davon ab, dies zu können. In der Vergangenheit hatten wir Dinge wie MeasureText und MeasureString und das Measure-Ereignis für Elemente. Denken Sie darüber nach, schauen Sie sich die Webseite an, die Sie gerade lesen, und beachten Sie, wie der Text in den verfügbaren Platz passt. – Ian

+0

Ah, aber die Typografie geht nicht so wie du. Das ist wie ein Haus vom Dach zu bauen. Sie wissen nicht, wie viele Zeichen in eine Breite passen, Sie wissen, wie breit ein SPEZIFISCHER TEXT ist. Ohne den Text zu kennen, ist die Berechnung der Breite nicht möglich. Typogrpahy beginnt nicht mit einem "unbekannten zufälligen Text", der gemessen werden soll. – TomTom

3

@ BrokenGlass Antwort ist groß, aber abhängig von den Eigenschaften der Anwendung Möglicherweise erzielen Sie mit einer binären Suche eine bessere Leistung. Wenn die Mehrheit Ihrer Strings in die verfügbare Breite passt oder im Allgemeinen nur um ein oder zwei Zeichen gekürzt werden muss, ist eine lineare Suche am besten. Wenn Sie jedoch viele lange Strings haben, die stark abgeschnitten werden, funktioniert die folgende binäre Suche gut.

Beachten Sie, dass sowohl availableWidth als auch fontSize in geräteunabhängigen Einheiten (1/96stel Zoll) angegeben sind. Verwenden Sie auch den TextFormattingMode, der der Art entspricht, in der Sie Ihren Text zeichnen.

public static string TruncateTextToFitAvailableWidth(
    string text, 
    double availableWidth, 
    string fontName, 
    double fontSize) 
{ 
    if(availableWidth <= 0) 
     return string.Empty; 

    Typeface typeface = new Typeface(fontName); 

    int foundCharIndex = BinarySearch(
     text.Length, 
     availableWidth, 
     predicate: (idxValue1, value2) => 
     { 
      FormattedText ft = new FormattedText(
       text.Substring(0, idxValue1 + 1), 
       CultureInfo.CurrentCulture, 
       FlowDirection.LeftToRight, 
       typeface, 
       fontSize, 
       Brushes.Black, 
       numberSubstitution: null, 
       textFormattingMode: TextFormattingMode.Ideal); 

      return ft.WidthIncludingTrailingWhitespace.CompareTo(value2); 
     }); 

    int numChars = (foundCharIndex < 0) ? ~foundCharIndex : foundCharIndex + 1; 

    return text.Substring(0, numChars); 
} 

/** 
<summary> 
See <see cref="T:System.Array.BinarySearch"/>. This implementation is exactly the same, 
except that it is not bound to any specific type of collection. The behavior of the 
supplied predicate should match that of the T.Compare method (for example, 
<see cref="T:System.String.Compare"/>). 
</summary> 
*/  
public static int BinarySearch<T>(
    int    length, 
    T     value, 
    Func<int, T, int> predicate) // idxValue1, value2, compareResult 
{ 
    return BinarySearch(0, length, value, predicate); 
} 

public static int BinarySearch<T>(
    int    index, 
    int    length, 
    T     value, 
    Func<int, T, int> predicate) 
{ 
    int lo = index; 
    int hi = (index + length) - 1; 

    while(lo <= hi) 
    { 
     int mid = lo + ((hi - lo)/2); 

     int compareResult = predicate(mid, value); 

     if(compareResult == 0) 
      return mid; 
     else if(compareResult < 0) 
      lo = mid + 1; 
     else 
      hi = mid - 1; 
    } 

    return ~lo; 
} 
+0

Ihre Antwort ist zu kompliziert, es gibt eine viel einfachere Lösung. Sie müssen nur den Prozentsatz berechnen, um den der String für den gegebenen Platz zu klein oder zu groß ist, und die Schriftgröße mit diesem Faktor skalieren. – henon

+0

Die Änderung der Schriftgröße ist weder für das OP noch für unsere Anwendung akzeptabel. –