Microsoft-ui-xaml: Proposal: Set TextBox height as lines, and width as characters

Created on 31 Aug 2019  路  12Comments  路  Source: microsoft/microsoft-ui-xaml

WPF has System.Windows.Controls.TextBox.MinLines and MaxLines. I understand why these were removed in UWP and that's fine, but what makes me unhappy is the removal without a replacement. It's frustrating for developers when preexisting features are lost and not replaced for years.

I propose that UWP TextBox be given these properties to replace the lost MinLines and MaxLines:

public int? HeightAsLines { get; set; }
public int? WidthAsCharacters { get; set; }

HeightAsLines would be an optional alternative to FrameworkElement.Height. If both HeightAsLines and FrameworkElement.Height are supplied, then FrameworkElement.Height has precedence and HeightAsLines is ignored. Likewise in WPF, FrameworkElement.Height took precedence over MinLines/MaxLines.

Alternatively, if having those properties causes a conflict with FrameworkElement.Height or other problem, then it would be satisfactory to implement them as set-only methods instead of properties:

public void SetHeightAsLines(int lines);
public void SetWidthAsCharacters(int characters);

Obviously the line height in pixels is affected by TextBox.FontFamily, TextBox.FontSize, etc. When the height is set to X lines, then the TextBox height in pixels should be calculated like this:

double heightInPixels = (HeightAsLines * lineHeight) + borderThickness.Top + borderThickness.Bottom 
    + Padding.Top + Padding.Bottom;
if (HorizontalScrollBarVisibility == ScrollBarVisibility.Visible)
    heightInPixels += heightOfHorizScrollBar;

See also Windows.UI.Xaml.Controls.TextBlock.MaxLines. That makes sense for TextBlock, but the situation is a bit different for TextBox because it supports scrollbars, therefore I propose that TextBox be given a fixed height as lines, not a minimum nor maximum number of lines.

Re WidthAsCharacters: To calculate the width in pixels, the number of characters could be multipled by the average character width, but I think a simpler technique is sufficient: Use the width of the character U+2013 "EN DASH" or U+2002 "EN SPACE". Wikipedia says:

"An en is a typographic unit, half of the width of an em. [...] As its name suggests, it is also traditionally the width of an uppercase letter 'N'."
https://en.wikipedia.org/wiki/En_(typography)

feature proposal needs-winui-3 team-Framework

Most helpful comment

A comparison with HTML is useful. In most cases, WinUI is more advanced and more powerful than a GUI built in HTML (and that's how it should be), but textbox sizing is an exception to this. HTML is much more advanced than the current version of WinUI in the specific area of textbox sizing.

In WinUI currently, the options for setting the textbox width and height are:

  1. Width and height as a number of pixels.
  2. Option number 2 doesn't exist, not even for height/lines. Ouch.

In HTML, the options are much better:

  1. Width and height as a number of pixels.
  2. Width and height as a number with various relative measurement units instead of pixels.
  3. Width as a number of characters.
  4. Height as a number of lines.

HTML's relative measurement units include:

| Unit | Description |
| :--- | :--- |
| em | The current font size. For example, "2.5em" means 2.5 times the font size. |
| rem | Root em. The font size of the root element. |
| ch | The width of the character/glyph "0" in the current font. |
| ex | The x-height of the current font. Roughly the height of lowercase letters. Rarely used. |

HTML's "ch" unit is the equivalent of the new proposal in my previous message:

How about shrinking my proposal to support width-as-characters only for numeric TextBox's? For example, 5 numeric characters would mean 5 multiplied by the width of the character "0".

In addition to the relative measurement units, HTML also supports setting the textbox width as a number of characters. The following example produces a textbox with a width of 50 characters and a height of 1 line:

<input type="text" size="50"/>

The following example produces a textbox with a width equalling 25 times the width of the character "0":

<input type="text" style="width: 25ch;">

The following example demonstrates another way of making a textbox in HTML. This textbox has a width of 50 characters and a height of 4 lines:

<textarea cols="50" rows="4"></textarea>

Note that in HTML, the number of lines isn't a maximum like WPF's TextBox.MaxLines property. WPF's TextBox.MaxLines doesn't work well, and neither does its TextBox.MinLines property. Thus I proposed to name the new property HeightAsLines not MaxLines, and this is not only a name change, it's also a behavior change. New name with new behavior. My opinion is that neither MaxLines nor MinLines should be recreated in WinUI.

What about the question of whether the width/height includes or excludes the border of the textbox? Again HTML is much more advanced than WinUI in this particular area. HTML has this "box-sizing" property:

| CSS | Description |
| :--- | :--- |
| box-sizing: content-box; | Default. The width and height properties (and min/max properties) include only the content. Border and padding are excluded. |
| box-sizing: border-box; | The width and height properties (and min/max properties) include content, padding and border. |

It's certainly a tenable argument to say that the solution for textbox width should be different than my proposal, whereas it's a very untenable/unwinnable argument to claim that WinUI's Width property is fully sufficient as-is and nothing more is needed.

All 12 comments

Use the width of the character U+2013 "EN DASH" or U+2002 "EN SPACE".

Why you want bake a heuristic into the control? Besides not every font is guaranteed to have these and it probably does not make sense in scripts other than latin.

@adrientetar -- I'd be happy to think about a different way of calculating the width-as-pixels from the width-as-characters. What do you suggest? What's a better way of calculating it?

If I make a TextBox and set its Width to a hardcoded constant number of pixels, then it breaks when users change the font size in the Settings panels in our app, especially if the TextBox is for the purpose of editing an integer value. For example, imagine a TextBox that allows users to view and modify an integer value in the range 0 to 250. 250 is 3 digits thus I could set the TextBox width to 5 characters, or at least 3 characters but 5 characters is more comfortable. If I set the TextBox width to a hardcoded size of 90 pixels instead of 5 characters, then it's incompatible with the settings that allows the user to change the font size and font family.

The problem becomes bigger when you consider the height. When a TextBox should allow a longer piece of text to be entered, then it makes sense to set the TextBox height to 3 lines (for example), and ideally the TextBox height in pixels should be a multiple of a number of lines. If I set the TextBox height to a hardcoded height of 120 pixels (for example), then it usually causes the last line of text to be cut off visually, especially when the user changes the font size setting. WPF supported the ability to configure the TextBox height in terms of a number of lines, but sadly WinUI does not yet have this ability.

I think it makes sense to have a "MaxLines" for TextBox (it exists in WPF), and we could scope this proposal to just that.

But I agree with @adrientetar that MaxCharacters is problematic for many reasons.

There was another proposal (#1226) to measure text without having it on screen, which if it existed would be my suggestion for how to do this (measure offscreen and then use that width as the Width for the TextBox). Do you think that would work for you?

Could it be possible to "scan" the font that is chosen, and determine the width of the widest glyph, and then base the measurement on that?

Could it be possible to "scan" the font that is chosen, and determine the width of the widest glyph, and then base the measurement on that?

The widest glyph of all possible glyphs? Or all "likely to be used" glyphs? What about glyphs that are the result of OpenType features? What if the text that's input isn't in the font so a different fallback font is selected? It's not an easy question to answer. :)

What's a better way of calculating it?

Not calculate it? There's two kinds of measurements, fixed ones or those that grow with container size. Usually you have a UI designer who makes redlines with specified width where they're fixed. The designer can consider the space for the intended content (using the target font/size etc.) but also other considerations like how much space should this part of the UI take. For instance, a sidebar shouldn't take most of the app space. Then there's the strategy of display: does it scroll to show all text (TextBox) or does it grow in size or ellipsize to keep its size (TextBlock). These are the design decisions

If I set the TextBox width to a hardcoded size of 90 pixels instead of 5 characters,

This isn't really "character size" but the scaling factor that should be multiplied to the base design size, IMO. (Doesn't Windows have a mechanism for changing text size globally?)

@jevansaks

I think it makes sense to have a "MaxLines" for TextBox (it exists in WPF), and we could scope this proposal to just that.

This proposal can also be simpler than WPF's MinLines and MaxLines properties. I suggest these 2 simplifications:

  • Don't recreate WPF's MinLines property in WinUI.
  • Don't make MaxLines operate as a maximum. Instead simplify it to be a fixed number of lines. In this case, it would also make sense to use a different name instead of MaxLines. For example, it could be named HeightAsLines instead of MaxLines.

My experience with using WPF's MinLines and MaxLines properties is that in practice they only work well/reliably when MinLines is set to the same number of lines as MaxLines. Therefore I don't suggest bringing both properties to WinUI.

For example, have a look at GitHub's text-box at the bottom of every issue webpage. Ofcourse it's not WPF, but it does operate like a WPF TextBox that has MinLines = 4; MaxLines = 11; and it is so buggy and annoying that I avoid using it. I write my GitHub messages in a separate text editor app and then copy-paste into GitHub. Likewise in WPF, when MinLines != MaxLines, I don't think it delivers a good experience to users.

I agree with @adrientetar that MaxCharacters is problematic for many reasons.

I didn't propose "MaxCharacters", but anyway I accept your viewpoint, so here is an idea to try to address your concerns: How about shrinking my proposal to support width-as-characters only for numeric TextBox's? For example, 5 numeric characters would mean 5 multiplied by the width of the character "0".

Alternatively, I wonder if you'd like this idea better: A string property that is used to calculate the width of the TextBox. For example, if the string property is set to "000.00%", then the width of the TextBox would be: Width of the text "000.00%" + border thickness + Padding + scrollbar thickness when applicable. That is the minimum width necessary to fully display the text "000.00%" without any visual truncation. I don't mean that the string would have a syntax. TextBox would only measure the string and not otherwise interpret it. The string can be any text.

There was another proposal (#1226) to measure text without having it on screen, which if it existed would be my suggestion for how to do this (measure offscreen and then use that width as the Width for the TextBox). Do you think that would work for you?

It would certainly help and I'd love to have that ability, but I wouldn't say that proposal #1226 fully solves proposal #1255. I don't know whether the final version of #1226 will be able to include the TextBox border/frame measurement OR only text measurement. You can't simply set TextBox.Width to the measured text width because of this missing information: As an app developer using TextBox, how can you get the border/frame thickness of TextBox? The app would need to set TextBox.Width to the measured text width + border thickness + Padding.

Maybe proposal #1226 could be used in conjunction with a new read-only "BorderThickness" property in TextBox:

public Windows.UI.Xaml.Thickness BorderThickness { get; }

However, such a "BorderThickness" property might be difficult to calculate, considering that the TextBox could be configured to use a non-default ControlTemplate.

@adrientetar

Not calculate it?

Not calculating it means that the problem continues to exist without a solution.

Usually you have a UI designer who makes redlines with specified width where they're fixed.

Fixed? That's how GUI was designed in the 1990's (fixed layout), but WPF and WinUI replaced it with a much better system (flowing layout etc).

The designer can consider the space for the intended content (using the target font/size etc.) .... These are the design decisions

Actually, no. The GUI designer is unable to set TextBox.Width and TextBox.Height when he/she is unable to know which font size and font family will be used, when the end-user can change the font size and font family in the Settings area of the app. Even when an app doesn't allow end-users to change the font size, the GUI designer still doesn't know the font size with certainty because the final font size decision might not be made until after the app has been road-tested by end-users. Even after the app has finished road-testing, the GUI designer may still be unsure of the font size because the road-testing may reveal the need for important new functionality that doubles the number of onscreen GUI elements and changes the font size decision.

Even if the app's feature set is frozen, the GUI designer still doesn't know the font size because 6 months later a huge enterprise customer says: We've now purchased 2000 Microsoft Surface tablets with a display size of X by Y, smaller than the desktop computers, and the GUI doesn't fit onscreen anymore and scrollbars aren't a solution because staff members have only a couple seconds to work (because we can't leave customers waiting in a long queue of 30 customers). Therefore, can you please reduce the font size from 16 to 13 and eliminate the scrollbars?

After the font size is reduced to 13, a _different_ huge enterprise customer says: We're required by equal-access laws to support our staff members who are aged over 50 years, but many of them have difficulty reading the small text on the screen. Can you please increase the font size?

A comparison with HTML is useful. In most cases, WinUI is more advanced and more powerful than a GUI built in HTML (and that's how it should be), but textbox sizing is an exception to this. HTML is much more advanced than the current version of WinUI in the specific area of textbox sizing.

In WinUI currently, the options for setting the textbox width and height are:

  1. Width and height as a number of pixels.
  2. Option number 2 doesn't exist, not even for height/lines. Ouch.

In HTML, the options are much better:

  1. Width and height as a number of pixels.
  2. Width and height as a number with various relative measurement units instead of pixels.
  3. Width as a number of characters.
  4. Height as a number of lines.

HTML's relative measurement units include:

| Unit | Description |
| :--- | :--- |
| em | The current font size. For example, "2.5em" means 2.5 times the font size. |
| rem | Root em. The font size of the root element. |
| ch | The width of the character/glyph "0" in the current font. |
| ex | The x-height of the current font. Roughly the height of lowercase letters. Rarely used. |

HTML's "ch" unit is the equivalent of the new proposal in my previous message:

How about shrinking my proposal to support width-as-characters only for numeric TextBox's? For example, 5 numeric characters would mean 5 multiplied by the width of the character "0".

In addition to the relative measurement units, HTML also supports setting the textbox width as a number of characters. The following example produces a textbox with a width of 50 characters and a height of 1 line:

<input type="text" size="50"/>

The following example produces a textbox with a width equalling 25 times the width of the character "0":

<input type="text" style="width: 25ch;">

The following example demonstrates another way of making a textbox in HTML. This textbox has a width of 50 characters and a height of 4 lines:

<textarea cols="50" rows="4"></textarea>

Note that in HTML, the number of lines isn't a maximum like WPF's TextBox.MaxLines property. WPF's TextBox.MaxLines doesn't work well, and neither does its TextBox.MinLines property. Thus I proposed to name the new property HeightAsLines not MaxLines, and this is not only a name change, it's also a behavior change. New name with new behavior. My opinion is that neither MaxLines nor MinLines should be recreated in WinUI.

What about the question of whether the width/height includes or excludes the border of the textbox? Again HTML is much more advanced than WinUI in this particular area. HTML has this "box-sizing" property:

| CSS | Description |
| :--- | :--- |
| box-sizing: content-box; | Default. The width and height properties (and min/max properties) include only the content. Border and padding are excluded. |
| box-sizing: border-box; | The width and height properties (and min/max properties) include content, padding and border. |

It's certainly a tenable argument to say that the solution for textbox width should be different than my proposal, whereas it's a very untenable/unwinnable argument to claim that WinUI's Width property is fully sufficient as-is and nothing more is needed.

@verelpode Good proposal; being able to specify in number of lines seems like a no brainer, and we should continue discussion about using characters as width. Your contrasting with HTML was very helpful--thanks for that.

There are an accumulating number of "make TextBox great again" feature proposals and I'd like to revisit them holistically once we've made WinUI 3 open source. I'm also going to add this specific issue to a list we're tracking of capabilities that are missing in WinUI as compared to WPF. (@predavid should take note of this one for that list).

Adding the needs-winui-3 label.

@ryandemopoulos

we should continue discussion about using characters as width.

OK. It seems that 4 tenable possibilities exist:

  • The ability to set TextBox width as a float64 (double) number that TextBox multiplies by the font size, meaning the equivalent of HTML's measurement unit em.
  • The ability to set TextBox width as an integer that TextBox multiplies by the float64 width of the character "0" (zero), meaning the equivalent of HTML's measurement unit ch.
  • The ability to set TextBox width as an integer that TextBox multiplies by the float64 width of a character specified in a property of TextBox.
  • The ability to make TextBox width fully accommodate the specified string consisting of any characters. For example, if a TextBox is intended to contain a percentage with 2 fractional digits, then the string could be "000.00%". This doesn't mean a syntax, rather the string is simply measured not interpreted.

In all 4 of the above, TextBox would also add space for the border, padding, and scrollbar when applicable.

In my first message, I suggest a possibility of measuring the width of the Unicode character U+2002 "EN SPACE", but this idea was a bad idea because it lacks justification for being different than HTML's "em" standard. It would've been less bad if I said U+2003 "EM SPACE" instead of "EN SPACE" but still bad because HTML actually doesn't use either of the "em" or "en" characters, rather it defines "em" as the font size.

HTML's em unit is a valid design but horizontally it is less easily understood than the ch unit, but ch has less culture-neutrality than em, therefore the strongest and most defensible possibility seems to be the idea of making TextBox width fully accommodate the specified string consisting of any characters.

For example, if a TextBox should contain a numeric value up to a maximum value of 10000, then you could write:

TextBox tb = ...;
int maximumValue = 10000;
tb.TextMeasuredForWidth = maximumValue.ToString(System.Globalization.CultureInfo.CurrentCulture) + "  ";

If the TextBox should support 2 fractional digits, then you could write:

double maximumValue = 10000;
tb.TextMeasuredForWidth = maximumValue.ToString("F2", System.Globalization.CultureInfo.CurrentCulture) + "  ";

If it should be a percentage with 2 fractional digits, then you could write:

double maximumValue = 1.0;
tb.TextMeasuredForWidth = maximumValue.ToString("P2", System.Globalization.CultureInfo.CurrentCulture) + "  ";

If a TextBox should contain a 3-letter country code according to ISO 3166-1 alpha-3, or a 3-letter currency code according to ISO 4217, then you could write:

TextBox tb = ...;
tb.TextMeasuredForWidth = "XXM  ";

If a TextBox should contain a RGB color in hexadecimal format, then you shouldn't do this because you should use ColorPicker instead :smile: But if you do need a TextBox containing RGB, then you could write:

tb.TextMeasuredForWidth = "#AAAAAA  ";
// Or if it should support ARGB:
tb.TextMeasuredForWidth = "#AAAAAAAA  ";

So in this particular subarea (TextBox sizing), I think it's very possible to transform the situation from _"worse than HTML5"_ to _"better than HTML5"_, and nobody cares or complains or criticizes when previous versions contain deficiencies that no longer exist in the latest version. Only the latest version matters.

@ryandemopoulos

I'm also going to add this specific issue to a list we're tracking of capabilities that are missing in WinUI as compared to WPF.

Thanks. That list is important in my opinion. When companies decide between a path of producing new versions versus a path of starting a whole new system, then the choice of starting a new system tends to have serious and dangerous consequences. For example, when Nokia realized that they'd fallen far behind, they could have decided to produce new versions of their OS but instead decided to start a new OS, and this decision killed them. It was a disasterous decision that destroyed Nokia. Nokia was forced to allow Microsoft to buy out Nokia. Similarly, in the past, Apple was brought to the brink of destruction by a decision to start a new system. Apple is lucky to be alive today, and lucky that they were able to eventually make a recovery from the brink of collapse.

The same kind of decision was the decision to start WinUI instead of producing new versions of WPF. A very helpful damage-control technique would be to prioritize the feature parity with WPF and reach a point where the WinUI team can say that WinUI is better than WPF in every way that matters. This would be great for reducing the damage done by the decision to start a new system. The longer the disparity continues, the greater the amount of damage done. If you look at Google's versus Apple's versus Microsoft's smartphone marketshare, apparently the amount of damage done was very large, and this damage was incurred at the critical timepoint when Microsoft was already bleeding, thus it was damage upon damage.

It's much easier to know the answer in hindsight. The hindsight shows that it was a mistake to start WinUI, and also a damaging mistake to revert to an older programming language with less productivity, but now WinUI is so far advanced and has so many benefits that it must continue. It's far too late to cancel WinUI. So now the question is, how to extinguish these fires that have been burning for years now? A helpful fire extinguisher would be bringing WinUI to a point where the WinUI team can say that WinUI is better than WPF in every way that matters. Of course it won't undo the damage that WinUI caused, but it would help the healing.

Another helpful fire extinguisher would be to increase productivity by writing more code in C#. Although there originally existed a reason for writing WinUI in C++, unfortunately that reason didn't reach fruition, therefore isn't applicable anymore (easily said in hindsight, but not so easy to say earlier). If WinUI was written in C# from the beginning, then the better-than-WPF goal would have been achieved 3 years ago, but now it's 8 years and STILL not achieved, and nobody is able to accurately say when the goal will be reached.

Was this page helpful?
0 / 5 - 0 ratings