WPFを使用しています。私のアプリには、変更可能なテキストを含むテキストブロックがいくつかあります。それぞれの幅は200、高さは150です。問題は、このようなテキストブロックが7つあり、同じフォントサイズにしたいということです。テキストは自動調整する必要があります。私はそれがそれらを自動調整できることを知っています。しかし、1つに文があり、もう1つが2語しかない場合、フォントサイズは非常に異なります...サイズを非同期的に再カウントする必要があります(たとえば、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));
編集:
実用性のために、テキストをこの事前定義された境界矩形の垂直方向の中央に配置できるようにしたい場合があります。そのための良い方法は、TextBlock を Border 要素などの別のオブジェクト内にラップすることです。このようにして、TextBlock を境界線の中央に配置するように指示でき、コンテンツに合わせて自動サイズ調整できます。
Ok。正常に動作します。しかし、私には別の問題があります。MainWindow.cs に 2 つのメソッドを記述しました。
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() メソッドを使用しようとすると、「別のスレッドが所有しているため、呼び出しスレッドはこのオブジェクトにアクセスできません。」というメッセージが表示されます。そのような問題を回避するためにこれを行うにはどうすればよいですか?このメソッドを呼び出す新しいスレッドを作成しようとしました(試してみました)。しかし、findVisualChildren<>() メソッドにも同じ問題があります。これは fitFontSize() で呼び出されます。問題を解決する方法がわかりません...