-
Notifications
You must be signed in to change notification settings - Fork 692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Add a way to do a theoretical measure of a TextBlock #1226
Comments
You are correct that manually calling Measure or Arrange on a TextBlock (or any other element) can cause problems if done at the wrong time. The primary use, of course, is to call these in MeasureOverride/ArrangeOverride of a Panel subclass on that Panel's direct children elements. Any other use should be careful to avoid the issues you mentioned. One safe use is to call Measure() on a TextBlock which is not in the tree, so its measure/arrange dirty state doesn't matter. We agree that having an explicit mechanism to measure text would be useful, and we have come close a couple of times to adding a feature for this. There are a couple challenges which come up:
Your proposal to MeasureTheoretically is an interesting idea to allow measuring size while avoiding the two challenges above (at least without the second text parameter). It would be great if you could open a Proposal to add a text measurement API. Your MeasureTheoretically idea can be the initial proposal for that, and we can see if the community has other ideas or other scenarios for text measurement. |
How about we just add the label and edit the title to morph this into a feature proposal? |
I like this balanced attitude very much, so I've quoted it to highlight it for the benefit of everyone who may happen to read this issue.
OK, if your opinion is that the text override parameter is troublesome, then let's remove it from the proposal. It's a non-essential parameter. Re the
Without being able to see the source code of
Although that's a very good tip, it's not always practical. For example, my real scenario faced this week requires that a TextBlock be in the tree because the text needs to be displayed to the user. Thus I cannot safely call
These properties are not only copied once, but rather it requires that the two TextBlocks be synchronized/mirrored. Each time one of those properties is changed in the in-tree TextBlock, it must also be changed in the unparented TextBlock. Impractical. The In addition to
When the wrapping is parameter is As you can see, this non-instance method is simple and easy-to-use, but supports fewer options than The WinUI team could also decide whether or not to support 2 overloads of the non-instance method: One for
I wouldn't expect the non-instance method to cache the resulting
I haven't seen the source code but I guess that this issue of cached measurement info might mean that the non-instance method should only support |
Multi-Monitor Compatibility
But does a multi-monitor environment break the unparented TextBlock technique? (Admittedly my description following is possibly inaccurate or outright incorrect because I might be mixing up the behavior of WPF versus UWP/WinUI, and because I don't have the considerable time necessary to fully research the multi-monitor issue. So please just tell me if I'm incorrect.) For example, a comparison with WPF:
In order to get the correct PixelsPerDip for a given FrameworkElement in the tree, while being compatible with multi-monitor, you can do:
However, that's WPF. Is this a non-issue in UWP/WinUI even when the computer has multiple monitors? In UWP, the following is true: But that's a per-instance property. To read it, you can do:
Thus a bug would occur if you measured an unparented BUT WAIT! What about the new Windows.UI.WindowManagement.AppWindow API? It allows apps to create multiple windows in the same thread. Does AppWindow break the unparented TextBlock technique when run in a multi-monitor environment? If the answer is Yes, then again the proposed Re my other proposed method; the static/non-instance
|
MeasureTheoretically for all!What do you think of the idea of creating Currently
This means that
That would instantly work correctly with nearly all subclasses of Alternatively, if you prefer,
|
Would this MeasureTheoretically method take account of Margins + Padding, Transparent backgrounds, Null Backgrounds, Focus Rectangles with negative margins, clipped elements, etc |
@mdtauk -- I'd propose that all of those factors be handled in the exact same manner as the existing As for the other proposed method -- the simple non-instance |
The proposed MeasureTheoretically for all UIElements would be absolutely wonderful for my usage! Coming from WPF, I was not able to call UpdateLayout and check the DesiredSize for an element b/c UpdateLayout can result in a message loop and queued events could run when it was not appropriate. So, I created code to do my own (usually buggy) MeasureTheoretically for Buttons, etc. So, considering porting to WinUI3, I would LOVE to be able to avoid possible reentrancy issues here and also measure controls for layout (we built UI in code, not XAML). So please create this feature!!! |
Please correct me if there is something I don't know: The current version of WinUI seems to have a problem when apps need to measure the width and height of text. People suggest using
Windows.UI.Xaml.UIElement.Measure
in order to measure text, like this:AFAIK, the above technique is buggy because it disrupts or interferes with the measure & arrange process/system (depending on when/where you invoke
Measure
and what value you supply for theavailableSize
parameter ofMeasure
). For example, this can happen:Windows.UI.Xaml.UIElement.InvalidateMeasure
and/orInvalidateArrange
is invoked in the normal manner at the normal time.InvalidateMeasure
sets its internal "IsMeasureValid" field/flag to false (the WPF equivalent isSystem.Windows.UIElement.IsMeasureValid
).InvalidateMeasure
schedules a measure pass (an update). The measure pass is not performed immediately, rather it is scheduled to be performed later-but-soon.Windows.UI.Xaml.UIElement.Measure
is invoked in the normal manner in response to the scheduling initiated byInvalidateMeasure
, the app decides that it needs to measure text and so it invokesmyTextBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Measure
,Measure
sets the internal "IsMeasureValid" field/flag to true.Measure
is invoked in the normal manner in response to the scheduling initiated byInvalidateMeasure
, but it does nothing because "IsMeasureValid" is already true.myTextBlock.DesiredSize
(UIElement.DesiredSize
) now contains an incorrect size becauseMeasure
was invoked with sizedouble.PositiveInfinity
instead of the realavailableSize
value calculated during a normal measure pass.myTextBlock.DesiredSize
.In WPF, one way of solving this problem is to stop using
Measure
and instead use System.Windows.Media.FormattedText to measure text, butFormattedText
does not exist in UWP/WinUI. Am I unaware of an alternative or equivalent toFormattedText
? Does there exist a recommended way to measure text in UWP/WinUI?I suggest that
Windows.UI.Xaml.Controls.TextBlock
be given a new method named "MeasureTheoretically" that operates likeWindows.UI.Xaml.UIElement.Measure
except that it performs the measurement only "in theory" and does not change any properties or internal fields inTextBlock
. Thus it would not changeUIElement.DesiredSize
nor the internal "IsMeasureValid" field/flag. It could be defined like this:availableSize
parameter has same meaning as inWindows.UI.Xaml.UIElement.Measure(availableSize)
.UIElement.DesiredSize
except thatDesiredSize
is not changed.To make it even better, I suggest adding a "text" parameter as follows:
text
parameter is supplied (non-null), then it temporarily overridesTextBlock.Text
without actually changingTextBlock.Text
.text
parameter is null (NOTSystem.String.Empty
), then it usesTextBlock.Text
orTextBlock.Inlines
as normal.Alternatively, the
availableSize
parameter could be removed entirely and it would act ifavailableSize
is alwaysdouble.PositiveInfinity
. In this case, it would be useful to have a parameter that overrides the values ofWindows.UI.Xaml.FrameworkElement.Width
andHeight
, such as:elementSize.Width
always overridesWindows.UI.Xaml.FrameworkElement.Width
.double.NaN
is acceptable (means automatic Width).elementSize.Height
always overridesWindows.UI.Xaml.FrameworkElement.Height
.double.NaN
is acceptable (means automatic Height).In some cases, apps don't need to perform a full measurement, rather they just need to get the line height. This means they need to get the actual/effective value of
Windows.UI.Xaml.Controls.TextBlock.LineHeight
but they cannot read theLineHeight
property in order to get this value, because usuallyLineHeight
returns zero. The default value ofLineHeight
is zero and it means that the line height is determined automatically. Thus I suggest that TextBlock be given a get-only "ActualLineHeight" or "EffectiveLineHeight" property like this:The text was updated successfully, but these errors were encountered: