TransWikia.com

Xamarin.Forms - How to copy Label and size with Measure()? Trying to auto size font. (Head scratcher!)

Stack Overflow Asked by aormsby on January 24, 2021

There’s a great Xamarin.Forms sample project titled Empirical Font Size that calculates the maximum possible font size for a Label within its containing view. It takes the label in question, uses VisualElement.Measure() to get some size requests for the label based on its properties, and compares the requested sizes to the container’s size to see if the label will fit with that font size. It recalculates until it finds the best fit. It works great, but I want to modify it to do more.

Unfortunately, I’ve hit some nasty roadblocks in my modifications, and I’m not sure if they’re more C# or Xamarin related. Here’s what I’d like to do.

  • Extract the calculations into their own function for use anywhere. -> The original doesn’t do this. This bit’s actually already done, but the following goals aren’t working so it’s good to bring this up as the starting point.
  • (FOCUS HERE) Avoid using the original label data in the function. (Make a copy.) -> I’m adding some features that require me to actively change the label text data during sizing calculations, but modifying the label I’m sizing can cause issues (not always with the sizing, but in other ways). I need a way to duplicate the label for font sizing in order to protect the original data. (More on what I’ve tried below).
  • Abstract the function to allow sizing of other views. -> Right now, this only calculates max label font sizes. If possible, I’d like to support auto font sizing for other views like picker, entry, etc. Food for thought while figuring out #2.

Goal #2 is where I’m having problems. I can’t find a way to duplicate a label and then perform sizing calculations on the temporary copy. Here’s what I’ve tried and how each attempt has failed.

  • Make a shallow copy. (e.g. Label copyLabel = origLabel;) -> As expected, this doesn’t work because the new label copy is still referencing the same original label data in memory. So if I set copyLabel.Text = "hello", then origLabel.Text will also change to "hello". Not good.
  • JSON copy -> I’ve seen recommendations to use JSON converters to serialize and then deserialize a View in order to do a quick deep copy, but I got an exception when I tried this. Something about a self-referencing loop on the label’s children.(Do labels even have children??)
  • ICloneable -> I’ll be running this function in a static class, so I can’t do this. It might not matter because of the next attempt, though.
  • Make a new Label object and copy some values. -> This almost seems to work, but the catch here is the VisualElement.Measure() calculation. The new label is missing something required for the calculation to work properly, and I don’t know what that is.

For any of these to work, I’d need to copy the label, immediately perform calculations on the copy, and apply the resulting font size to the original. Then the copy disappears. Poof! Unfortunately, any copy I make results in different calculations, so this is where I’m currently stuck.

Someone mentioned elsewhere that maybe the sizing doesn’t work because the new label isn’t part of a layout and thus can’t be sized, but they didn’t explain so I don’t understand what they were getting at.

To see what works and what doesn’t, here’s a small Xamarin sample project with a modified version of the Empirical Font Size project. It’s a simple A/B test. Two labels with the same text are sized by two near identical functions – one that uses the original label and one that tries to use a copy. They should fill the top and bottom halves of the provided stack layout, but the copy doesn’t work. I’d like help making that work.

MODIFIED SAMPLE PROJECT: https://drive.google.com/file/d/1mWxE3p6u53jkIdaIEFVFnkI7BC-kfske/view?usp=sharing

One Answer

I figured out a safe, simple workaround for this issue that I call 'ghost sizing'. You can create an invisible 'ghost label' and place it into a page where a label will be sized. It's not visible, so it doesn't change the layout at all, but it is still usable in size calculations. You pass the text of the label you want to size into the ghost label, size the ghost label to the desired space, and apply the returned size value to the target label.

XAML:

<!--  for sizing other elements without breaking bindings  -->
<Label x:Name="ghostLabel" IsVisible="false" />

C# code-behind:

ghostLabel.Text = String.Copy(myLabel.Text);

...
double labelFontSize = ...// do font size calculations here using the ghostLabel object
...

myLabel.FontSize = labelFontSize;

I've made my own font sizing class for this if you're curious.

Github repo

further ghost sizing notes

Correct answer by aormsby on January 24, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP