알려진 글꼴 크기 및 문자에 대한 WPF TextBlock 너비를 계산하는 방법은 무엇입니까?
TextBlock
텍스트 "Some Text" 와 글꼴 크기 10.0 이 있다고 가정 해 보겠습니다 .
적절한 TextBlock
너비를 어떻게 계산할 수 있습니까?
FormattedText
수업을 사용하십시오 .
내 코드에서 도우미 함수를 만들었습니다.
private Size MeasureString(string candidate)
{
var formattedText = new FormattedText(
candidate,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(this.textBlock.FontFamily, this.textBlock.FontStyle, this.textBlock.FontWeight, this.textBlock.FontStretch),
this.textBlock.FontSize,
Brushes.Black,
new NumberSubstitution(),
1);
return new Size(formattedText.Width, formattedText.Height);
}
WPF 레이아웃에서 사용할 수있는 장치 독립적 픽셀을 반환합니다.
기록을 위해 ... 나는 운영자가 시각적 트리에 추가 된 후 textBlock이 차지할 너비를 프로그래밍 방식으로 결정하려고한다고 가정합니다. IMO는 formattedText (textWrapping과 같은 것을 어떻게 처리합니까?)보다 나은 솔루션은 샘플 TextBlock에서 Measure and Arrange를 사용하는 것입니다. 예 :
var textBlock = new TextBlock { Text = "abc abd adfdfd", TextWrapping = TextWrapping.Wrap };
// auto sized
textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
textBlock.Arrange(new Rect(textBlock.DesiredSize));
Debug.WriteLine(textBlock.ActualWidth); // prints 80.323333333333
Debug.WriteLine(textBlock.ActualHeight);// prints 15.96
// constrain the width to 16
textBlock.Measure(new Size(16, Double.PositiveInfinity));
textBlock.Arrange(new Rect(textBlock.DesiredSize));
Debug.WriteLine(textBlock.ActualWidth); // prints 14.58
Debug.WriteLine(textBlock.ActualHeight);// prints 111.72
제공된 솔루션은 .Net Framework 4.5에 적합했지만 Windows 10 DPI 스케일링 및 Framework 4.6.x에서 다양한 지원 수준을 추가하면 이제 텍스트를 측정하는 데 사용되는 생성자가 표시됩니다 [Obsolete]
. pixelsPerDip
매개 변수를 포함하지 마십시오 .
안타깝게도 조금 더 복잡하지만 새로운 확장 기능을 사용하면 정확도가 높아집니다.
PixelsPerDip
MSDN에 따르면 이것은 다음을 나타냅니다.
배율 인수에 해당하는 밀도 독립 픽셀 당 픽셀 값입니다. 예를 들어 화면의 DPI가 120 (또는 120/96 = 1.25이므로 1.25)이면 밀도 독립 픽셀 당 1.25 픽셀이 그려집니다. DIP는 WPF에서 장치 해상도 및 DPI와 독립적으로 사용되는 측정 단위입니다.
다음 은 DPI 확장 인식 기능이 있는 Microsoft / WPF-Samples GitHub 리포지토리의 지침에 따라 선택한 답변을 구현 한 것입니다 .
Windows 10 Anniversary (코드 아래)부터 DPI 스케일링 을 완벽하게 지원하는 데 필요한 몇 가지 추가 구성이 있습니다 . 이는 작동 할 수 없었지만이 기능이 없으면 스케일링이 구성된 단일 모니터에서 작동합니다 (스케일링 변경 사항 준수). 위의 저장소에있는 Word 문서는 해당 값을 추가하면 내 응용 프로그램이 시작되지 않기 때문에 해당 정보의 소스입니다. 동일한 저장소 의이 샘플 코드 도 좋은 참조 지점으로 사용되었습니다.
public partial class MainWindow : Window
{
private DpiScale m_dpiInfo;
private readonly object m_sync = new object();
public MainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
private Size MeasureString(string candidate)
{
DpiInfo dpiInfo;
lock (m_dpiInfo)
dpiInfo = m_dpiInfo;
if (dpiInfo == null)
throw new InvalidOperationException("Window must be loaded before calling MeasureString");
var formattedText = new FormattedText(candidate, CultureInfo.CurrentUICulture,
FlowDirection.LeftToRight,
new Typeface(this.textBlock.FontFamily,
this.textBlock.FontStyle,
this.textBlock.FontWeight,
this.textBlock.FontStretch),
this.textBlock.FontSize,
Brushes.Black,
dpiInfo.PixelsPerDip);
return new Size(formattedText.Width, formattedText.Height);
}
// ... The Rest of Your Class ...
/*
* Event Handlers to get initial DPI information and to set new DPI information
* when the window moves to a new display or DPI settings get changed
*/
private void OnLoaded(object sender, RoutedEventArgs e)
{
lock (m_sync)
m_dpiInfo = VisualTreeHelper.GetDpi(this);
}
protected override void OnDpiChanged(DpiScale oldDpiScaleInfo, DpiScale newDpiScaleInfo)
{
lock (m_sync)
m_dpiInfo = newDpiScaleInfo;
// Probably also a good place to re-draw things that need to scale
}
}
기타 요구 사항
According to the documentation at Microsoft/WPF-Samples, you need to add some settings to the application's manifest to cover Windows 10 Anniversary's ability to have different DPI settings per display in multiple-monitor configurations. It's a fair guess that without these settings, the OnDpiChanged event might not be raised when a window is moved from one display to another with different settings, which would make your measurements continue to rely on the previous DpiScale
. The application I was writing was for me, alone, and I don't have that kind of a setup so I had nothing to test with and when I followed the guidance, I ended up with an app that wouldn't start due to manifest errors, so I gave up, but it'd be a good idea to look that over and adjust your app manifest to contain:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
</windowsSettings>
</application>
According to the documentation:
The combination of [these] two tags have the following effect : 1) Per-Monitor for >= Windows 10 Anniversary Update 2) System < Windows 10 Anniversary Update
I found some methods which work fine...
/// <summary>
/// Get the required height and width of the specified text. Uses Glyph's
/// </summary>
public static Size MeasureText(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
GlyphTypeface glyphTypeface;
if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
{
return MeasureTextSize(text, fontFamily, fontStyle, fontWeight, fontStretch, fontSize);
}
double totalWidth = 0;
double height = 0;
for (int n = 0; n < text.Length; n++)
{
ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]];
double width = glyphTypeface.AdvanceWidths[glyphIndex] * fontSize;
double glyphHeight = glyphTypeface.AdvanceHeights[glyphIndex] * fontSize;
if (glyphHeight > height)
{
height = glyphHeight;
}
totalWidth += width;
}
return new Size(totalWidth, height);
}
/// <summary>
/// Get the required height and width of the specified text. Uses FortammedText
/// </summary>
public static Size MeasureTextSize(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
FormattedText ft = new FormattedText(text,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(fontFamily, fontStyle, fontWeight, fontStretch),
fontSize,
Brushes.Black);
return new Size(ft.Width, ft.Height);
}
I resolved this by adding a binding path to the element in the backend code:
<TextBlock x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />
I found this to be a much cleaner solution than adding all the overhead of the above references like FormattedText to my code.
After, I was able to do this:
double d_width = MyText.Width;
I use this one:
var typeface = new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch);
var formattedText = new FormattedText(textBlock.Text, Thread.CurrentThread.CurrentCulture, textBlock.FlowDirection, typeface, textBlock.FontSize, textBlock.Foreground);
var size = new Size(formattedText.Width, formattedText.Height)
Found this for you:
Graphics g = control.CreateGraphics();
int width =(int)g.MeasureString(aString, control.Font).Width;
g.dispose();
'Nice programing' 카테고리의 다른 글
내 AJAX 애플리케이션에서 뒤로 버튼에 대한 호출 가로 채기 (0) | 2020.10.25 |
---|---|
개체 목록에서 고유 한 필드 값을 반환하는 linq 쿼리 (0) | 2020.10.25 |
정적 라이브러리가 64 비트 용으로 빌드되었는지 확인하는 방법은 무엇입니까? (0) | 2020.10.25 |
C #에는 부작용 확인 기능이 포함되어 있습니까? (0) | 2020.10.25 |
Visual Studio의 웹 사이트 게시에서 파일 제외 (0) | 2020.10.25 |