TextBox.Text Doesn't Right Justify - c#

According to my understanding, the code below should right-justify the text if the text is longer than the textbox can show, otherwise it keeps it left-justified.
The problem is that it doesn't actually do this and it's acting really odd. Short strings end up right-justified sometimes and long strings are always left-justified.
What am I doing wrong?
private void textBoxCurrentConfig_TextChanged(object sender, EventArgs e)
{
SizeF stringSize = new SizeF();
stringSize = TextRenderer.MeasureText(textBoxCurrentConfig.Text, textBoxCurrentConfig.Font);
float currentTextWidth = stringSize.Width;
float allowedTextWidth = textBoxCurrentConfig.Size.Width - 10;
if (currentTextWidth >= allowedTextWidth) // if the text we want to display is larger than the textbox can hold, right justify it to show the filename
{
textBoxCurrentConfig.TextAlign = HorizontalAlignment.Right; // right justify
}
else // otherwise we can display the entire path
{
textBoxCurrentConfig.TextAlign = HorizontalAlignment.Left; // left justify
}
textBoxCurrentConfig.Refresh();
this.Refresh();
}

As from your comments, you want to move cursor position according to the text length. You can use TextBox.Select() method for this. Check MSDN for details.
So if you want to move cursor at the the start of text, you can use
textBoxCurrentConfig.Select(0, 0);
and if you want to move cursor at the end of text, you can use
textBoxCurrentConfig.Select(textBoxCurrentConfig.Text.Length, 0);

Try to remove
this.Refresh();
It's may cause the page to refresh and return the text box to the original align

Related

C# UWP - Wrong caret size when there is no text

I have a RichEditBox and want to change the font size of the selection. When I don't select anything, and then change the font size, the caret size doesn't get bigger, until I start typing some characters (spaces don't work). When I delete all of those characters, the caret shrinks again, until I type some characters. Is there any possibility to change the caret size immediately, like in programs like Word?
ITextSelection selection = Editor.Document.Selection;
if(selection != null) {
float fontSize;
try {
fontSize = (float) Convert.ToDouble(fontSizeBox.Text);
} catch(FormatException) {
fontSize = 11;
}
selection.CharacterFormat.Size = fontSize;
}
Editor.Focus(FocusState.Programmatic);
You can do this by a trick using PlaceholderText
<RichEditBox PlaceholderText="Input your text" FontSize="52"/>
Update:
Well, I added some screenshot and did the test.
The caret did shrink a little when enter a new line. And it get bigger when RichEditBox changed from palceholder text to your own text.
This is really interesting.
I think this maybe a bug created by Microsoft.
Default:
The first line
Enter a new line

How to create a smooth animated text marquee?

I know that there are lot of different threads about horizontal text animation/text scrolling, but unfortunately none of them give smooth scrolling with repeatable text. I have tried double/thickness animation using various WPF controls containing text. Also tried animating with visual brush which gives me by far the most elegant scrolling compared to other approaches (for e.g. playing with Canvas.Left property etc.) but that too goes blur the text, if the text length or the animation speed is too high.
I'm over to a pure DirectX C# implementation using SharpDX library. Should also mention that I'm a beginner with DirectX programming. Here is the code:
public void RunMethod()
{
// Make window active and hide mouse cursor.
window.PointerCursor = null;
window.Activate();
var str = "This is an example of a moving TextLayout object with no snapped pixel boundaries.";
// Infinite loop to prevent the application from exiting.
while (true)
{
// Dispatch all pending events in the queue.
window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessAllIfPresent);
// Quit if the users presses Escape key.
if (window.GetAsyncKeyState(VirtualKey.Escape) == CoreVirtualKeyStates.Down)
{
return;
}
// Set the Direct2D drawing target.
d2dContext.Target = d2dTarget;
// Clear the target.
d2dContext.BeginDraw();
d2dContext.Clear(Color.CornflowerBlue);
//float layoutXOffset = 0;
float layoutXOffset = layoutX;
// Create the DirectWrite factory objet.
SharpDX.DirectWrite.Factory fontFactory = new SharpDX.DirectWrite.Factory();
// Create a TextFormat object that will use the Segoe UI font with a size of 24 DIPs.
textFormat = new TextFormat(fontFactory, "Verdana", 100.0f);
textLayout2 = new TextLayout(fontFactory, str, textFormat, 2000.0f, 100.0f);
// Draw moving text without pixel snapping, thus giving a smoother movement.
// d2dContext.FillRectangle(new RectangleF(layoutXOffset, 1000, 1000, 100), backgroundBrush);
d2dContext.DrawTextLayout(new Vector2(layoutXOffset, 0), textLayout2, textBrush, DrawTextOptions.NoSnap);
d2dContext.EndDraw();
//var character = str.Substring(0, 1);
//str = str.Remove(0, 1);
//str += character;
layoutX -= 3.0f;
if (layoutX <= -1000)
{
layoutX = 0;
}
// Present the current buffer to the screen.
swapChain.Present(1, PresentFlags.None);
}
}
Basically it creates an endless loop and subtracts the horizontal offset. Here are the challenges: I need repeatable text similar to HTML marquee without any gaps, Would probably need to extend it to multiple monitors.
Please suggest.
I don't know neither how to use DirectX nor sharpdx, but if you want you can consider this solution
I had a similar problem a while ago, but with the text inside a combobox. After a bounty i got what i was looking for. I'm posting the relevant piece of code as an example, but you can check the complete answer here
Basically, whenever you have a textblock/textbox that contain a string that cannot be displayed completely, cause the length exceed the textblock/box lenght you can use this kind of approach. You can define a custom usercontrol derived from the base you need (e.g. SlidingComboBox : Combobox) and define an animation for you storyboard like the following
_animation = new DoubleAnimation()
{
From = 0,
RepeatBehavior = SlideForever ? RepeatBehavior.Forever : new RepeatBehavior(1), //repeat only if slide-forever is true
AutoReverse = SlideForever
};
In my example i wanted this behaviour to be active only when the mouse was on the combobox, so in my custom OnMouse enter i had this piece of code
if (_parent.ActualWidth < textBlock.ActualWidth)
{
_animation.Duration = TimeSpan.FromMilliseconds(((int)textBlock.Text?.Length * 100));
_animation.To = _parent.ActualWidth - textBlock.ActualWidth;
_storyBoard.Begin(textBlock);
}
Where _parent represent the container of the selected item. After a check on the text lenght vs combobox lenght i start the animation and end it at the end of the text to be displayed
Note that in the question i mentioned there are also other soltions. I'm posting the one that worked for me

RichTextBox formatting and restoring cursor and scrollbar position

I have RichTextBox in which I format text, I have there multiple text selections and formats.
Because of that, after the formatting is done, the scrollbars positions of RichTextBox isn't the same.
How am I able to save and restore scrollbar position same (easy) way as I can save cursor position?
protected override void OnTextChanged(EventArgs e)
{
// Save cursor position
int cursor_position = this.SelectionStart;
// Format text
Highlight();
// Restore position
this.SelectionLength = 0;
this.SelectionStart = cursor_position;
}
I have seen many posts here that were solving this by handling scroll messages.
I have managed this simplier way, so if anyone have the same problem, you can use this way. It is not perfect (if there is a half of line displayed at the top, it will be scrolled), but is enough I think :).
protected override void OnTextChanged(EventArgs e)
{
// Get first and last displayed character
int start = this.GetCharIndexFromPosition(new Point(0, 0));
int end = this.GetCharIndexFromPosition(new Point(this.ClientSize.Width, this.ClientSize.Height));
// Save cursor position
int cursor_position = this.SelectionStart;
int cursor_lenght = this.SelectionLength;
// Your formatting
Highlight();
// Scroll to the last character and then to the first + line width
this.SelectionLength = 0;
this.SelectionStart = end;
this.ScrollToCaret();
this.SelectionStart = start + this.Lines[this.GetLineFromCharIndex(start)].Length+1;
this.ScrollToCaret();
// Finally, set cursor to original position
this.SelectionStart = cursor_position;
}

AutoSize text in a fixed-sized label to avoid text jumping to second line

I have a label with a fixed size, approximately 100 pixel width and 20 pixel height.
When I place a long string into the label, the text wraps to the second line but I cannot see the second line because the size of the label is fixed.
Instead of wrapping to the second line, I want the fontsize to shrink so that the string is displayed on a single line in the label. Does anyone know of a simple way to do this?
EDIT:
The below code is working for me (most of the time). I didn't want to do anything recursive. There are times when the text still spills over to another line. I assume because I can't truly use the entire width of the label. How do I compensate for that?
private void Label_TextChanged(object sender, EventArgs e)
{
Label label = sender as Label;
if (label != null && label.Text.Length != 0)
{
SizeF size = new SizeF();
using (Graphics g = label.CreateGraphics())
{
size = g.MeasureString(label.Text, label.Font);
}
Single x = (label.Width) / size.Width;
Single y = (label.Height) / size.Height;
Single scaler = x > y ? y : x;
using (Font font = label.Font)
{
label.Font = new Font(font.Name, font.SizeInPoints * scaler);
}
}
}
This is easy to do. Use Graphics.MeasureString(...) to determine the width required for your string, then progressively make the font smaller and smaller until the width required for the string is equal to or less than the width of your label.
You can use the System.Windows.Forms.Label.TextChanged-event and check the length of the string.
private void Label_TextChanged(object sender, EventArgs e){
System.Windows.Forms.Label label = sender as label;
if(label != null){
//check text-length and if necessary resize it
}
}
See here for TextChanged-event.
There is the FontHeight-property, which might do your trick. See here for a reference.
And if everything fails, derive from System.Windows.Forms.Label and create your own custom label.

Autoscale Font in a TextBox Control so that its as big as possible and still fits in text area bounds

I need a TextBox or some type of Multi-Line Label control which will automatically adjust the font-size to make it as large as possible and yet have the entire message fit inside the bounds of the text area.
I wanted to see if anyone had implemented a user control like this before developing my own.
Example application: have a TextBox which will be half of the area on a windows form. When a message comes in which is will be approximately 100-500 characters it will put all the text in the control and set the font as large as possible. An implementation which uses Mono Supported .NET libraries would be a plus.
If know one has implemented a control already... If someone knows how to test if a given text completely fits inside the text area that would be useful for if I roll my own control.
Edit: I ended up writing an extension to RichTextBox. I will post my code shortly once i've verified that all the kinks are worked out.
I had to solve the same basic problem. The iterative solutions above were very slow. So, I modified it with the following. Same idea. Just uses calculated ratios instead of iterative. Probably, not quite as precise. But, much faster.
For my one-off need, I just threw an event handler on the label holding my text.
private void PromptLabel_TextChanged(object sender, System.EventArgs e)
{
if (PromptLabel.Text.Length == 0)
{
return;
}
float height = PromptLabel.Height * 0.99f;
float width = PromptLabel.Width * 0.99f;
PromptLabel.SuspendLayout();
Font tryFont = PromptLabel.Font;
Size tempSize = TextRenderer.MeasureText(PromptLabel.Text, tryFont);
float heightRatio = height / tempSize.Height;
float widthRatio = width / tempSize.Width;
tryFont = new Font(tryFont.FontFamily, tryFont.Size * Math.Min(widthRatio, heightRatio), tryFont.Style);
PromptLabel.Font = tryFont;
PromptLabel.ResumeLayout();
}
I haven't seen an existing control to do this, but you can do it the hard way by using a RichTextBox and the TextRenderer's MeasureText method and repeatedly resizing the font. It's inefficient, but it works.
This function is an event handler for the 'TextChanged' event on a RichTextBox.
An issue I've noticed:
When typing, the text box will scroll to the current caret even if scrollbars are disabled. This can result in the top line or left side getting chopped off until you move back up or left with the arrow keys. The size calculation is correct assuming you can get the top line to display at the top of the text box. I included some scrolling code that helps sometimes (but not always).
This code assumes word wrap is disabled. It may need modification if word wrap is enabled.
The code:
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint wMsg, int wParam, uint lParam);
private static uint EM_LINEINDEX = 0xbb;
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
// If there's no text, return
if (richTextBox1.TextLength == 0) return;
// Get height and width, we'll be using these repeatedly
int height = richTextBox1.Height;
int width = richTextBox1.Width;
// Suspend layout while we mess with stuff
richTextBox1.SuspendLayout();
Font tryFont = richTextBox1.Font;
Size tempSize = TextRenderer.MeasureText( richTextBox1.Text, richTextBox1.Font);
// Make sure it isn't too small first
while (tempSize.Height < height || tempSize.Width < width)
{
tryFont = new Font(tryFont.FontFamily, tryFont.Size + 0.1f, tryFont.Style);
tempSize = TextRenderer.MeasureText(richTextBox1.Text, tryFont);
}
// Now make sure it isn't too big
while (tempSize.Height > height || tempSize.Width > width)
{
tryFont = new Font(tryFont.FontFamily, tryFont.Size - 0.1f, tryFont.Style);
tempSize = TextRenderer.MeasureText(richTextBox1.Text, tryFont);
}
// Swap the font
richTextBox1.Font = tryFont;
// Resume layout
richTextBox1.ResumeLayout();
// Scroll to top (hopefully)
richTextBox1.ScrollToCaret();
SendMessage(richTextBox1.Handle, EM_LINEINDEX, -1, 0);
}
The solution i came up with was to write a control which extends the standard RichTextBox control.
Use the extended control in the same way you would a regular RichTextBox control with the following enhancements:
Call the ScaleFontToFit() method after resizing or text changes.
The Horizontal Alignment field can be used to center align the text.
The Font attributes set in the designer will be used for the entire region. It is not possible to mix fonts as they will changed once the ScaleFontToFit method is called.
This control combines several techniques to determine if the text still fits within it's bounds. If the text area is multiline, it detects if scrollbars are visible. I found a clever way to detect whether or not the scrollbars are visible without requiring any winapi calls using a clever technique I found on one of Patrick Smacchia's posts.. When multiline isn't true, vertical scrollbars never appear so you need to use a different technique which relies on rendering the text using a the Graphics object. The Graphic rendering technique isn't suitable for Multiline boxes because you would have to account for word wrapping.
Here are a few snippets which shows how it works (link to source code is provided below). This code could easily be used to extend other controls.
/// <summary>
/// Sets the font size so the text is as large as possible while still fitting in the text
/// area with out any scrollbars.
/// </summary>
public void ScaleFontToFit()
{
int fontSize = 10;
const int incrementDelta = 5; // amount to increase font by each loop iter.
const int decrementDelta = 1; // amount to decrease to fine tune.
this.SuspendLayout();
// First we set the font size to the minimum. We assume at the minimum size no scrollbars will be visible.
SetFontSize(MinimumFontSize);
// Next, we increment font size until it doesn't fit (or max font size is reached).
for (fontSize = MinFontSize; fontSize < MaxFontSize; fontSize += incrementDelta)
{
SetFontSize(fontSize);
if (!DoesTextFit())
{
//Console.WriteLine("Text Doesn't fit at fontsize = " + fontSize);
break;
}
}
// Finally, we keep decreasing the font size until it fits again.
for (; fontSize > MinFontSize && !DoesTextFit(); fontSize -= decrementDelta)
{
SetFontSize(fontSize);
}
this.ResumeLayout();
}
#region Private Methods
private bool VScrollVisible
{
get
{
Rectangle clientRectangle = this.ClientRectangle;
Size size = this.Size;
return (size.Width - clientRectangle.Width) >= SystemInformation.VerticalScrollBarWidth;
}
}
/**
* returns true when the Text no longer fits in the bounds of this control without scrollbars.
*/
private bool DoesTextFit()
{
if (VScrollVisible)
{
//Console.WriteLine("#1 Vscroll is visible");
return false;
}
// Special logic to handle the single line case... When multiline is false, we cannot rely on scrollbars so alternate methods.
if (this.Multiline == false)
{
Graphics graphics = this.CreateGraphics();
Size stringSize = graphics.MeasureString(this.Text, this.SelectionFont).ToSize();
//Console.WriteLine("String Width/Height: " + stringSize.Width + " " + stringSize.Height + "form... " + this.Width + " " + this.Height);
if (stringSize.Width > this.Width)
{
//Console.WriteLine("#2 Text Width is too big");
return false;
}
if (stringSize.Height > this.Height)
{
//Console.WriteLine("#3 Text Height is too big");
return false;
}
if (this.Lines.Length > 1)
{
//Console.WriteLine("#4 " + this.Lines[0] + " (2): " + this.Lines[1]); // I believe this condition could be removed.
return false;
}
}
return true;
}
private void SetFontSize(int pFontSize)
{
SetFontSize((float)pFontSize);
}
private void SetFontSize(float pFontSize)
{
this.SelectAll();
this.SelectionFont = new Font(this.SelectionFont.FontFamily, pFontSize, this.SelectionFont.Style);
this.SelectionAlignment = HorizontalAlignment;
this.Select(0, 0);
}
#endregion
ScaleFontToFit could be optimized to improve performance but I kept it simple so it'd be easy to understand.
Download the latest source code here. I am still actively working on the project which I developed this control for so it's likely i'll be adding a few other features and enhancements in the near future. So, check the site for the latest code.
My goal is to make this control work on Mac using the Mono framework.
I had a similar requirement for a text box in a panel on a windows form hosted window. (I injected the panel onto the existing form). When the size of the panel changes (in my case) the text would resize to fit the box. Code
parentObject.SizeChanged += (sender, args) =>
{
if (textBox1.Text.Length > 0)
{
int maxSize = 100;
// Make a Graphics object to measure the text.
using (Graphics gr = textBox1.CreateGraphics())
{
for (int i = 1; i <= maxSize; i++)
{
using (var test_font = new Font(textBox1.Font.FontFamily, i))
{
// See how much space the text would
// need, specifying a maximum width.
SizeF text_size =
TextRenderer.MeasureText(
textBox1.Text,
test_font,
new Size(textBox1.Width, int.MaxValue),
TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);
try
{
if (text_size.Height > textBox1.Height)
{
maxSize = i - 1;
break;
}
}
catch (System.ComponentModel.Win32Exception)
{
// this sometimes throws a "failure to create window handle" error.
// This might happen if the TextBox is invisible and/or
// too small to display a toolbar.
// do whatever here, add/delete, whatever, maybe set to default font size?
maxSize = (int) textBox1.Font.Size;
}
}
}
}
// Use that font size.
textBox1.Font = new Font(textBox1.Font.FontFamily, maxSize);
}
};

Categories