Disons que j'ai TextBlock
avec du texte "Un texte" y taille de la police 10.0 .
Comment puis-je calculer le montant approprié TextBlock
largeur ?
Disons que j'ai TextBlock
avec du texte "Un texte" y taille de la police 10.0 .
Comment puis-je calculer le montant approprié TextBlock
largeur ?
Utilisez le FormattedText
classe.
J'ai créé une fonction d'aide dans mon code :
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);
}
Il renvoie des pixels indépendants de l'appareil qui peuvent être utilisés dans la mise en page WPF.
@ArunPrasad Ce serait une excellente chose à demander dans une question séparée. Cette question est clairement axée sur WPF.
Pour mémoire... Je suppose que l'opérateur essaie de déterminer par programme la largeur que prendra le textBlock après avoir été ajouté à l'arbre visuel. Une meilleure solution que le formattedText (comment gérer quelque chose comme le textWrapping ?) serait d'utiliser Measure and Arrange sur un échantillon de TextBlock, par exemple
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
Avec quelques modifications mineures, cela a fonctionné pour moi dans une application Windows 8.1 Store (Windows Runtime). N'oubliez pas de définir les informations relatives à la police pour ce bloc de texte afin que la largeur soit calculée avec précision. Une solution astucieuse qui mérite une note positive.
La solution fournie était appropriée pour .Net Framework 4.5, cependant, avec la mise à l'échelle DPI de Windows 10 et le Framework 4.6.x ajoutant divers degrés de support pour cela, le constructeur utilisé pour mesurer le texte est maintenant marqué [Obsolete]
ainsi que tous les constructeurs de cette méthode qui n'incluent pas l'attribut pixelsPerDip
paramètre.
Malheureusement, c'est un peu plus compliqué, mais cela permettra d'obtenir une plus grande précision grâce aux nouvelles capacités de mise à l'échelle.
Selon MSDN, cela représente :
La valeur de Pixels par pixel indépendant de la densité, qui est l'équivalent du facteur d'échelle. Par exemple, si le DPI d'un écran est de 120 (ou 1,25 car 120/96 = 1,25), 1,25 pixel par pixel indépendant de la densité est dessiné. Le DPI est l'unité de mesure utilisée par WPF pour être indépendant de la résolution des périphériques et des DPI.
Voici ma mise en œuvre de la réponse choisie, basée sur les conseils de la Microsoft/WPF-Samples Dépôt GitHub avec prise en compte de la mise à l'échelle du DPI.
Une configuration supplémentaire est nécessaire pour complètement supporte la mise à l'échelle du DPI à partir de Windows 10 Anniversary (sous le code), que je n'ai pas pu faire fonctionner, mais sans cela, cela fonctionne sur un seul moniteur avec la mise à l'échelle configurée (et respecte les changements d'échelle). Le document Word dans le repo ci-dessus est la source de cette information puisque mon application ne se lançait pas une fois que j'avais ajouté ces valeurs. Cet exemple de code du même dépôt a également servi de bon point de référence.
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
}
}
Selon la documentation de Microsoft/WPF-Samples, vous devez ajouter certains paramètres au manifeste de l'application pour couvrir la capacité de Windows 10 Anniversary à avoir des paramètres DPI différents par écran dans les configurations à plusieurs moniteurs. On peut supposer que sans ces paramètres, l'événement OnDpiChanged pourrait ne pas être déclenché lorsqu'une fenêtre est déplacée d'un écran à un autre avec des paramètres différents, ce qui ferait que vos mesures continueraient à reposer sur les paramètres DPI précédents. DpiScale
. L'application que j'écrivais était pour moi, seul, et je n'ai pas ce genre de configuration donc je n'avais rien à tester et quand j'ai suivi les conseils, je me suis retrouvé avec une application qui ne voulait pas démarrer à cause d'erreurs dans le manifeste, donc j'ai abandonné, mais ce serait une bonne idée de regarder ça et d'ajuster votre manifeste d'application pour le contenir :
<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>
Selon la documentation :
La combinaison de [ces] deux balises a l'effet suivant : 1) Par moniteur pour >= Windows 10 Anniversary Update 2) Système < Windows 10 Anniversary Update
J'ai trouvé quelques méthodes qui fonctionnent bien...
/// <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);
}
La somme des largeurs de glyphes ne fonctionnera pas pour la plupart des polices, car elle ne tient pas compte des éléments suivants crénage .
J'ai résolu ce problème en ajoutant un chemin de liaison à l'élément dans le code du backend :
<TextBlock x:Name="MyText" Width="{Binding Path=ActualWidth, ElementName=MyText}" />
J'ai trouvé que c'était une solution beaucoup plus propre que d'ajouter à mon code toute la surcharge des références ci-dessus comme FormattedText.
Après, j'ai été capable de faire ça :
double d_width = MyText.Width;
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
0 votes
La question a déjà été posée, et cela dépend aussi de la police.
0 votes
Vous pouvez également obtenir la largeur réelle à partir de
ActualWidth
.2 votes
L'utilisation de TextRenderer devrait également fonctionner pour WPF : stackoverflow.com/questions/721168/