From 9dfa3cfdf7e753282c01924d2a7171f359203764 Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Sat, 23 Nov 2024 13:50:40 +0800 Subject: [PATCH 01/11] Added fixes provided on the issue section --- RJTextBox.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/RJTextBox.cs b/RJTextBox.cs index d6bf27a..37cf87d 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -109,8 +109,12 @@ public override Color BackColor get { return base.BackColor; } set { - base.BackColor = value; - textBox1.BackColor = value; + try + { + base.BackColor = value; + textBox1.BackColor = value; + } + catch (ArgumentException) { return; } } } @@ -186,7 +190,7 @@ public string PlaceholderText set { placeholderText = value; - textBox1.Text = ""; + textBox1.PlaceholderText = ""; SetPlaceholder(); } } @@ -272,7 +276,7 @@ private void SetPlaceholder() if (string.IsNullOrWhiteSpace(textBox1.Text) && placeholderText != "") { isPlaceholder = true; - textBox1.Text = placeholderText; + textBox1.PlaceholderText = placeholderText; textBox1.ForeColor = placeholderColor; if (isPasswordChar) textBox1.UseSystemPasswordChar = false; @@ -283,7 +287,7 @@ private void RemovePlaceholder() if (isPlaceholder && placeholderText != "") { isPlaceholder = false; - textBox1.Text = ""; + textBox1.PlaceholderText = ""; textBox1.ForeColor = this.ForeColor; if (isPasswordChar) textBox1.UseSystemPasswordChar = true; @@ -334,6 +338,7 @@ private void UpdateControlHeight() #region -> TextBox events private void textBox1_TextChanged(object sender, EventArgs e) { + if (isPlaceholder) return; if (_TextChanged != null) _TextChanged.Invoke(sender, e); } From d21cffdae31d211a31e93ca63f1d14f8043e679c Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Sat, 23 Nov 2024 14:48:04 +0800 Subject: [PATCH 02/11] Provided isEmpty() method, reverted non-working placeholder text fix This fix aims to provide a simple condition to check if the rounded textbox either contains the placeholder value or it is actually empty, if it is, the IsEmpty() method will return true. To use it, it is as simple as rjTextBox2.IsEmpty(), this can be used as an evaluation method for database systems to avoid having the false null value issue (where the textbox does not have any input, but the database reads and adds the placeholder text inside the rounded textbox anyway). --- RJTextBox.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/RJTextBox.cs b/RJTextBox.cs index 37cf87d..9a99601 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -190,13 +190,11 @@ public string PlaceholderText set { placeholderText = value; - textBox1.PlaceholderText = ""; + textBox1.Text = ""; SetPlaceholder(); } } - - #endregion #region -> Overridden methods @@ -276,7 +274,7 @@ private void SetPlaceholder() if (string.IsNullOrWhiteSpace(textBox1.Text) && placeholderText != "") { isPlaceholder = true; - textBox1.PlaceholderText = placeholderText; + textBox1.Text = placeholderText; textBox1.ForeColor = placeholderColor; if (isPasswordChar) textBox1.UseSystemPasswordChar = false; @@ -287,7 +285,7 @@ private void RemovePlaceholder() if (isPlaceholder && placeholderText != "") { isPlaceholder = false; - textBox1.PlaceholderText = ""; + textBox1.Text = ""; textBox1.ForeColor = this.ForeColor; if (isPasswordChar) textBox1.UseSystemPasswordChar = true; @@ -321,6 +319,9 @@ private void SetTextBoxRoundedRegion() } pathTxt.Dispose(); } + + public bool IsEmpty() { return isPlaceholder || string.IsNullOrEmpty(textBox1.Text); } + private void UpdateControlHeight() { if (textBox1.Multiline == false) From d899fa0b12d7168dbf9055525a37624376b453e5 Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Sat, 23 Nov 2024 16:33:17 +0800 Subject: [PATCH 03/11] Removed IsEmpty() --- RJTextBox.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RJTextBox.cs b/RJTextBox.cs index 9a99601..6f63507 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -320,8 +320,6 @@ private void SetTextBoxRoundedRegion() pathTxt.Dispose(); } - public bool IsEmpty() { return isPlaceholder || string.IsNullOrEmpty(textBox1.Text); } - private void UpdateControlHeight() { if (textBox1.Multiline == false) @@ -366,6 +364,7 @@ private void textBox1_Enter(object sender, EventArgs e) this.Invalidate(); RemovePlaceholder(); } + private void textBox1_Leave(object sender, EventArgs e) { isFocused = false; From 2d25da5976f59c109d971a79a0f25a752e58adf7 Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Sat, 30 Nov 2024 22:50:20 +0800 Subject: [PATCH 04/11] Fixed set Texts property not working properly This change fixes a bug where, if the control receives a new text in the Texts property, it displays the text, but it is still considered a placeholder which is wrong. This is fixed by first checking if the string is not empty, if yes, remove and disable the placeholder text, and then set the Text property of the textbox to the actual value. --- RJTextBox.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/RJTextBox.cs b/RJTextBox.cs index 6f63507..5bff9d1 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -152,8 +152,11 @@ public string Texts } set { - textBox1.Text = value; - SetPlaceholder(); + if (!string.IsNullOrWhiteSpace(value)) + { + RemovePlaceholder(); + internalTextBox.Text = value; + } } } From 10e16498d3271183e3fe1e21ef3c5e869b5304fd Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Wed, 4 Dec 2024 07:44:40 +0800 Subject: [PATCH 05/11] Introduce experimental cache optimization This change makes the custom control to cache the render points necessary for drawing the control itself. When OnPaint() is called and no property that is a factor for rendering has changed (borderRadius for example), OnPaint() will not redraw the control entirely, instead, it will simply reuse the previously calculated data points and recolor the control. This change should (in theory) improve rendering performance for this control. --- RJTextBox.cs | 61 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/RJTextBox.cs b/RJTextBox.cs index 5bff9d1..6bdd8fe 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -31,6 +31,15 @@ public partial class RJTextBox : UserControl //Events public event EventHandler _TextChanged; + // Cached render point + private Rectangle rectBorderSmooth; + private Rectangle rectBorder; + private int smoothSize; + + private GraphicsPath pathBorderSmooth; + private GraphicsPath pathBorder; + GraphicsPath pathTxt; + #endregion //-> Constructor @@ -68,6 +77,7 @@ public int BorderSize if (value >= 1) { borderSize = value; + layoutChanged = true; this.Invalidate(); } } @@ -109,12 +119,12 @@ public override Color BackColor get { return base.BackColor; } set { - try - { - base.BackColor = value; - textBox1.BackColor = value; - } - catch (ArgumentException) { return; } + try + { + base.BackColor = value; + textBox1.BackColor = value; + } + catch (ArgumentException) { return; } } } @@ -169,6 +179,7 @@ public int BorderRadius if (value >= 0) { borderRadius = value; + layoutChanged = true; this.Invalidate();//Redraw control } } @@ -220,18 +231,14 @@ protected override void OnPaint(PaintEventArgs e) if (borderRadius > 1)//Rounded TextBox { //-Fields - var rectBorderSmooth = this.ClientRectangle; - var rectBorder = Rectangle.Inflate(rectBorderSmooth, -borderSize, -borderSize); - int smoothSize = borderSize > 0 ? borderSize : 1; - - using (GraphicsPath pathBorderSmooth = GetFigurePath(rectBorderSmooth, borderRadius)) - using (GraphicsPath pathBorder = GetFigurePath(rectBorder, borderRadius - borderSize)) + if (layoutChanged) { RefreshDataPoints(); } using (Pen penBorderSmooth = new Pen(this.Parent.BackColor, smoothSize)) using (Pen penBorder = new Pen(borderColor, borderSize)) { //-Drawing this.Region = new Region(pathBorderSmooth);//Set the rounded region of UserControl if (borderRadius > 15) SetTextBoxRoundedRegion();//Set the rounded region of TextBox component + if (layoutChanged) { layoutChanged = false; } graph.SmoothingMode = SmoothingMode.AntiAlias; penBorder.Alignment = System.Drawing.Drawing2D.PenAlignment.Center; if (isFocused) penBorder.Color = borderFocusColor; @@ -294,6 +301,18 @@ private void RemovePlaceholder() textBox1.UseSystemPasswordChar = true; } } + + private void RefreshDataPoints() + { + rectBorderSmooth = this.ClientRectangle; + rectBorder = Rectangle.Inflate(rectBorderSmooth, -borderSize, -borderSize); + smoothSize = borderSize > 0 ? borderSize : 1; + pathBorderSmooth?.Dispose(); + pathBorderSmooth = GetFigurePath(rectBorderSmooth, borderRadius); + pathBorder?.Dispose(); + pathBorder = GetFigurePath(rectBorder, borderRadius - borderSize); + } + private GraphicsPath GetFigurePath(Rectangle rect, int radius) { GraphicsPath path = new GraphicsPath(); @@ -309,18 +328,24 @@ private GraphicsPath GetFigurePath(Rectangle rect, int radius) } private void SetTextBoxRoundedRegion() { - GraphicsPath pathTxt; if (Multiline) { - pathTxt = GetFigurePath(textBox1.ClientRectangle, borderRadius - borderSize); - textBox1.Region = new Region(pathTxt); + if (layoutChanged) + { + pathTxt?.Dispose(); + pathTxt = GetFigurePath(internalTextBox.ClientRectangle, borderRadius - borderSize); + } + internalTextBox.Region = new Region(pathTxt); } else { - pathTxt = GetFigurePath(textBox1.ClientRectangle, borderSize * 2); - textBox1.Region = new Region(pathTxt); + if (layoutChanged) + { + pathTxt?.Dispose(); + pathTxt = GetFigurePath(internalTextBox.ClientRectangle, borderSize * 2); + } + internalTextBox.Region = new Region(pathTxt); } - pathTxt.Dispose(); } private void UpdateControlHeight() From 111ddf2057c275e0922bf3889a83b56473924287 Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Wed, 4 Dec 2024 07:46:59 +0800 Subject: [PATCH 06/11] Added missing private keyword --- RJTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RJTextBox.cs b/RJTextBox.cs index 6bdd8fe..67f38da 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -38,7 +38,7 @@ public partial class RJTextBox : UserControl private GraphicsPath pathBorderSmooth; private GraphicsPath pathBorder; - GraphicsPath pathTxt; + private GraphicsPath pathTxt; #endregion From 5c711310ce8c77635abdef6a866a19331bbf0b54 Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Wed, 4 Dec 2024 08:04:14 +0800 Subject: [PATCH 07/11] Refactored SetTextBoxRegion() This also corrects the wrong textbox name issue. --- RJTextBox.cs | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/RJTextBox.cs b/RJTextBox.cs index 67f38da..99096f4 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -165,7 +165,7 @@ public string Texts if (!string.IsNullOrWhiteSpace(value)) { RemovePlaceholder(); - internalTextBox.Text = value; + textBox1.Text = value; } } } @@ -328,23 +328,14 @@ private GraphicsPath GetFigurePath(Rectangle rect, int radius) } private void SetTextBoxRoundedRegion() { - if (Multiline) + if (layoutChanged) { - if (layoutChanged) - { - pathTxt?.Dispose(); - pathTxt = GetFigurePath(internalTextBox.ClientRectangle, borderRadius - borderSize); - } - internalTextBox.Region = new Region(pathTxt); - } - else - { - if (layoutChanged) - { - pathTxt?.Dispose(); - pathTxt = GetFigurePath(internalTextBox.ClientRectangle, borderSize * 2); - } - internalTextBox.Region = new Region(pathTxt); + pathTxt?.Dispose(); + int effectiveBorderRadius = Multiline + ? borderRadius - borderSize + : borderSize * 2; + pathTxt = GetFigurePath(textBox1.ClientRectangle, effectiveBorderRadius); + textBox1.Region = new Region(pathTxt); } } From 29262c796e0abb8a634685d269685b8fce43cf33 Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Wed, 4 Dec 2024 08:23:14 +0800 Subject: [PATCH 08/11] Avoid unnecessary Region assignment This is an extended application to the cache optimization that I made, this change makes it so that now, OnPaint() will not provide set a new Region to the control everytime, it only does so when the layout has changed. --- RJTextBox.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/RJTextBox.cs b/RJTextBox.cs index 99096f4..9fb7c1e 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -231,12 +231,15 @@ protected override void OnPaint(PaintEventArgs e) if (borderRadius > 1)//Rounded TextBox { //-Fields - if (layoutChanged) { RefreshDataPoints(); } + if (layoutChanged) + { + RefreshDataPoints(); + this.Region = new Region(pathBorderSmooth); // Set the rounded region of UserControl + } using (Pen penBorderSmooth = new Pen(this.Parent.BackColor, smoothSize)) using (Pen penBorder = new Pen(borderColor, borderSize)) { //-Drawing - this.Region = new Region(pathBorderSmooth);//Set the rounded region of UserControl if (borderRadius > 15) SetTextBoxRoundedRegion();//Set the rounded region of TextBox component if (layoutChanged) { layoutChanged = false; } graph.SmoothingMode = SmoothingMode.AntiAlias; @@ -265,7 +268,11 @@ protected override void OnPaint(PaintEventArgs e) //Draw border using (Pen penBorder = new Pen(borderColor, borderSize)) { - this.Region = new Region(this.ClientRectangle); + if (layoutChanged) + { + this.Region = new Region(this.ClientRectangle); + layoutChanged = false; + } penBorder.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset; if (isFocused) penBorder.Color = borderFocusColor; From 619698095f9136d3bfffd0f1932c44edfbb0f473 Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Wed, 4 Dec 2024 08:42:06 +0800 Subject: [PATCH 09/11] Reduce flickering This change allows the control to have less flickering, I have observed the effects of this change myself and I say it works. --- RJTextBox.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RJTextBox.cs b/RJTextBox.cs index 9fb7c1e..605f24f 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -47,6 +47,9 @@ public RJTextBox() { //Created by designer InitializeComponent(); + SetStyle(ControlStyles.AllPaintingInWmPaint, true); + SetStyle(ControlStyles.UserPaint, true); + SetStyle(ControlStyles.DoubleBuffer, true); } #region -> Properties From 059d183caa066a77d6373054b222751be944955f Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Wed, 4 Dec 2024 08:52:13 +0800 Subject: [PATCH 10/11] Fixed textbox noy resizing properly This fix ensures that when the control is stretched out, it will redraw itself properly. --- RJTextBox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RJTextBox.cs b/RJTextBox.cs index 605f24f..6fca556 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -218,6 +218,8 @@ public string PlaceholderText protected override void OnResize(EventArgs e) { base.OnResize(e); + layoutChanged = true; + Invalidate(); if (this.DesignMode) UpdateControlHeight(); } From 8821a2471d416ecd6c905aa9a2ead361e1d3b71e Mon Sep 17 00:00:00 2001 From: Unbreakable-Syntax Date: Fri, 6 Dec 2024 06:08:15 +0800 Subject: [PATCH 11/11] Added RemoveNewline property This change allows the textbox to sanitize the input by removing the newline characters. Which can be very annoying to spot and notice since it can break things, such as string evaluators and database entries. This property is used alongside the Multiline property. --- RJTextBox.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/RJTextBox.cs b/RJTextBox.cs index 6fca556..ed4f066 100644 --- a/RJTextBox.cs +++ b/RJTextBox.cs @@ -21,6 +21,7 @@ public partial class RJTextBox : UserControl private int borderSize = 2; private bool underlinedStyle = false; private bool isFocused = false; + private bool removeNewline = false; private int borderRadius = 0; private Color placeholderColor = Color.DarkGray; @@ -97,6 +98,16 @@ public bool UnderlinedStyle } } + [Category("RJ Code Advance")] + public bool RemoveNewline + { + get { return removeNewline; } + set + { + removeNewline = value; + } + } + [Category("RJ Code Advance")] public bool PasswordChar { @@ -167,8 +178,10 @@ public string Texts { if (!string.IsNullOrWhiteSpace(value)) { - RemovePlaceholder(); - textBox1.Text = value; + RemovePlaceholder(); + string val = value; + if (removeNewline) { val = val.Replace("\n", "").Replace("\r", ""); ; } + textBox1.Text = val; } } }