2012-06-01 8 views
0

Я использую WPF. В моем приложении есть текстовые блоки с изменяемым текстом внутри. Каждый из них имеет ширину 200 и высоту 150. Проблема в том, что у меня есть 7 текстовых блоков, подобных тем, и я хочу, чтобы они имели одинаковый размер шрифта. Текст должен быть авторизован. Я знаю, что может их автоподтвердить. Но когда у кого есть предложение внутри, а другое только два слова, размер шрифта настолько разный ... Мне нужно пересчитать размер асинхронно (например, создав какое-то событие, например OnTextChange). Текст внутри блоков изменяется динамически. Как написать функцию? Я хочу передать 3 параметра: текст, шрифт (шрифт семьи + стиль шрифта) и размер текстового блока, а также вернуть размер шрифта.Подсчет размера шрифта асинхронно

ответ

2

Лучший способ определить размер шрифта - это измерить текст любого произвольного размера, а затем умножить его на отношение его размера к размеру области.

Например, если вы измеряете текст, и он составляет половину от размера контейнера, в котором он находится, вы можете умножить его на 2, и он должен заполнить контейнер. Вы хотите выбрать минимальное соотношение ширины и высоты для использования.

В WPF класс FormattedText выполняет измерение текста.

public double GetFontSize(string text, Size availableSize, Typeface typeFace) 
{ 
    FormattedText formtxt = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeFace, 10, Brushes.Black); 

    double ratio = Math.Min(availableSize.Width/formtxt.Width, availableSize.Height/formtxt.Height); 

    return 10 * ratio; 
} 

Вы должны использовать эту функцию каждый раз, когда вы изменяете текст вашего TextBlocks, например, так:

txtBlock.FontSize = GetFontSize(txt.Text, new Size(txt.ActualWidth, txt.ActualHeight), new Typeface(txt.FontFamily, txt.FontStyle, txt.FontWeight, txt.FontStretch)); 

Edit:

Для целей практичности, вы можете быть чтобы текст мог центрировать по вертикали в этом предопределенном ограничивающем прямоугольнике. Хороший способ сделать это - обернуть свой TextBlock внутри другого объекта, такого как элемент Border. Таким образом вы можете указать, что TextBlock выровнен по центру границы, и он может автоматически настраиваться в соответствии с его содержимым.

0

Ok. Работает отлично. Но у меня другая проблема. Я написал 2 метода в MainWindow.cs:

private void fitFontSize() 
    { 
     propertiesList.Clear(); 

     TextUtils.FontProperty fontProps = new TextUtils.FontProperty(); 
     foreach (TextBlock tb in findVisualChildren<TextBlock>(statusOptionsGrid)) 
     { 
      fontProps.Text = tb.Text; 
      fontProps.Size = new Size(tb.ActualWidth, tb.ActualHeight); 
      fontProps.FontFamily = tb.FontFamily; 
      fontProps.FontStyle = tb.FontStyle; 
      fontProps.FontWeight = tb.FontWeight; 
      fontProps.FontStretch = tb.FontStretch; 
      propertiesList.Add(fontProps); 
     } 

     MessageBox.Show(TextUtils.recalculateFontSize(propertiesList) + ""); 
    } 

    public IEnumerable<T> findVisualChildren<T>(DependencyObject depObj) where T : DependencyObject 
    { 
     if (depObj != null) 
     { 
      for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) 
      { 
       DependencyObject child = VisualTreeHelper.GetChild(depObj, i); 
       if (child != null && child is T) 
       { 
        yield return (T)child; 
       } 

       foreach (T childOfChild in findVisualChildren<T>(child)) 
       { 
        yield return childOfChild; 
       } 
      } 
     } 
    } 

После этого я создал новый класс для обработки текста. Вот код:

public class TextUtils 
{ 
    public class FontProperty 
    { 
     public FontFamily FontFamily { get; set; } 
     public FontStyle FontStyle { get; set; } 
     public FontWeight FontWeight { get; set; } 
     public FontStretch FontStretch { get; set; } 
     public string Text { get; set; } 
     public Size Size { get; set; } 

     public Typeface getTypeFace() 
     { 
      return new Typeface(FontFamily, FontStyle, FontWeight, FontStretch); 
     } 
    } 

    public static double recalculateFontSize(List<FontProperty> propertiesList) 
    { 
     List<double> fontSizes = new List<double>(); 
     foreach (FontProperty fp in propertiesList) 
     { 
      fontSizes.Add(getFontSizeForControl(fp)); 
     } 

     return fontSizes.Min<double>(); 
    } 

    private static double getFontSizeForControl(FontProperty fp) 
    { 
     string text = fp.Text; 
     Size availableSize = fp.Size; 
     Typeface typeFace = fp.getTypeFace(); 

     FormattedText formtxt = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeFace, 10, Brushes.Black); 
     double ratio = Math.Min(availableSize.Width/formtxt.Width, availableSize.Height/formtxt.Height); 

     return 10 * ratio; 
    } 
} 

Код выглядит плохо, но я исправлю это позже ...

Ok. Теперь я хочу создать новый таймер, который проверяет размер шрифта каждую секунду. Когда я пытаюсь использовать метод fitFontSize() в нем, я получаю сообщение msg: «Вызывающий поток не может получить доступ к этому объекту, потому что ему принадлежит другой поток». Как это сделать, чтобы избежать подобных проблем? Я попробовал (просто попробую) создать новый поток, вызывающий этот метод. Но есть та же проблема с методом findVisualChildren <>() - он вызывается в fitFontSize(). У меня нет никаких идей, как решить мою проблему ...

Смежные вопросы