WPF为啥改变TextBlock字体Button,Label等控件的字体都变了
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF为啥改变TextBlock字体Button,Label等控件的字体都变了相关的知识,希望对你有一定的参考价值。
如题,代码如下
<Style TargetType="x:Type TextBlock">
<Setter Property="FontFamily" Value="华文彩云"/>
</Style>
模板里我确定是没有TextBlock的!!!!
我确实用Blend看过,模板里没有TextBlock
追答恩,我刚建了个demo测试了下,在Resource里加了TextBlock的style。但是我Button和Label控件的字体样式没有发生变化。Button和Label模板里用来显示内容的是ContentPresenter。也就是说明它们的Content是可以被定制的。任何控件都可以放在这里面。你看下是否这些Button和label的Content里面有放textblock.
参考技术A 因为button label等控件默认模版里显示文字的地方用的就是textblock. 所以textblock写样式时必须得加Key,然后需要用样式的textblock 显示引用样式。追问模板里没有TextBlock,这个TextBlock从何而来
如何计算已知字体大小和字符的 WPF TextBlock 宽度?
【中文标题】如何计算已知字体大小和字符的 WPF TextBlock 宽度?【英文标题】:How to calculate WPF TextBlock width for its known font size and characters? 【发布时间】:2012-03-05 02:00:38 【问题描述】:假设我有 TextBlock
的文本 "Some Text" 和 字体大小 10.0。
如何计算合适的TextBlock
宽度?
【问题讨论】:
这个问题之前已经问过了,也和字体有关。 你也可以从ActualWidth
获取实际宽度。
使用 TextRenderer 也应该适用于 WPF:***.com/questions/721168/…
【参考方案1】:
为你找到了这个:
Graphics g = control.CreateGraphics();
int width =(int)g.MeasureString(aString, control.Font).Width;
g.dispose();
【讨论】:
这种具体做法只适用于WinForms。【参考方案2】:使用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 布局的与设备无关的像素。
【讨论】:
这真的很有帮助 如何在 UWP 中做同样的事情 @ArunPrasad 在单独的问题上提出这将是一件很棒的事情。这个问题显然适用于 WPF。 如果传入的候选是空格,则返回宽度为零。我认为这可能与自动换行有关? 在我的情况下,您还需要配置这些:c# formattedText.MaxTextHeight = textBlock.MaxHeight; formattedText.MaxTextWidth = textBlock.MaxWidth; formattedText.Trimming = TextTrimming.None; // Change this value in case you do have trimming
【参考方案3】:
我找到了一些效果很好的方法...
/// <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);
【讨论】:
对大多数字体求和字形宽度不起作用,因为它不考虑kerning。【参考方案4】:我通过在后端代码中为元素添加绑定路径解决了这个问题:
<TextBlock x:Name="MyText" Width="Binding Path=ActualWidth, ElementName=MyText" />
我发现这是一个比将 FormattedText 等上述引用的所有开销添加到我的代码中更简洁的解决方案。
之后,我能够做到这一点:
double d_width = MyText.Width;
【讨论】:
你可以简单地做d_width = MyText.ActualWidth;
而不用绑定。问题是TextBlock
还没有在可视化树中。【参考方案5】:
为了记录... 我假设操作者正在尝试以编程方式确定 textBlock 在添加到可视化树后将占用的宽度。 IMO 比 formattedText 更好的解决方案(你如何处理 textWrapping 之类的东西?)将是在示例 TextBlock 上使用 Measure 和 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
【讨论】:
经过一些小的修改,这对我编写 Windows 8.1 应用商店应用程序(Windows 运行时)很有用。不要忘记设置此 TextBlock 的字体信息,以便准确计算宽度。简洁的解决方案,值得一票。 这里的关键点是就地创建这个临时文本块。所有这些对页面上现有文本块的操作都不起作用:它的 ActualWidth 仅在重绘后更新。【参考方案6】:提供的解决方案适用于 .Net Framework 4.5,但是,随着 Windows 10 DPI 缩放和 Framework 4.6.x 为其添加不同程度的支持,用于测量文本的构造函数现在标记为 [Obsolete]
,以及任何该方法上不包含 pixelsPerDip
参数的构造函数。
不幸的是,它涉及更多一点,但它会通过新的缩放功能提高准确性。
###PixelsPerDip
根据 MSDN,这表示:
Pixels Per Density Independent Pixel 值,相当于比例因子。例如,如果屏幕的 DPI 为 120(或 1.25,因为 120/96 = 1.25),则每个密度独立像素绘制 1.25 个像素。 DIP 是 WPF 使用的独立于设备分辨率和 DPI 的测量单位。
这是我根据Microsoft/WPF-Samples GitHub 存储库的指导对所选答案的实现,并具有 DPI 缩放意识。
需要一些额外的配置才能完全支持 Windows 10 周年纪念版的 DPI 缩放(代码下方),我无法开始工作,但没有它,它可以在单个显示器上工作配置了缩放(并尊重缩放更改)。上述 repo 中的 Word 文档是该信息的来源,因为一旦我添加了这些值,我的应用程序就不会启动。来自同一个仓库的This sample code 也是一个很好的参考点。
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)
DpiScale 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
其他要求
根据 Microsoft/WPF-Samples 上的文档,您需要向应用程序的清单中添加一些设置,以涵盖 Windows 10 Anniversary 在多显示器配置中为每个显示器设置不同 DPI 设置的能力。可以公平地猜测,如果没有这些设置,当窗口从一个显示器移动到另一个具有不同设置的显示器时,可能不会引发 OnDpiChanged 事件,这将使您的测量继续依赖于之前的DpiScale
。我正在编写的应用程序是为我一个人编写的,我没有那种设置,所以我没有什么可测试的,当我按照指导进行时,我最终得到了一个由于清单而无法启动的应用程序错误,所以我放弃了,但最好检查一下并调整您的应用清单以包含:
<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>
根据文档:
[这些]两个标签的组合具有以下效果: 1) >= Windows 10 周年更新的每个监视器 2) 系统
【讨论】:
【参考方案7】:我用这个:
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)
【讨论】:
以上是关于WPF为啥改变TextBlock字体Button,Label等控件的字体都变了的主要内容,如果未能解决你的问题,请参考以下文章
wpf 用button实现TextBlock里字体颜色的切换
wpf中怎么是鼠标移动到textblock中的字体上使其改变颜色
WPF 动态生成一个button 和一个textblock 怎么给textblock添加数据