Clear SelectionBackColor from text in RichTextBox [duplicate] - winforms

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
RichTextBox syntax highlighting in real time--Disabling the repaint
I'm using a RichTextBox control to find and change the SelectionBackColor property of some words. The words are not fixed so basically the text that has different BackColor varies.
I've already tried two methods of clearing the BackColor from the previous text before applying it to the new words:
Selecting all the text and setting the SelectionBackColor to the Controls BackColor.
Saving the text to string then putting it back to RichTextBox to clear it's formatting.
Although both methods work an issue arises when you have a lot of text in the control. For the first method, it becomes clearer that all text gets selected (you can notice it for a few milliseconds), which becomes annoying since this happens in the TextChanges event, so basically every letter that gets removed/added triggers this. As for the second method, it's not that obvious as the first, but since the text is removed and then insert back, the scrolling becomes a bit odd since even after using .ScrollToCaret() the scrollbar isn't exactly were it was before the SelectionBackColor clearing.
It feels like there should be a better way of clearing the existing SelectionBackColor without all these issues. Especially in this case since it has to do the cleaning in the TextChanged event.
Waiting for your thoughts. Thanks in advance.
Edit: You can see below the method I'm using for the first example I mentioned above (selecting all).
private void ClearSelection(RichTextBox rtb)
{
if (rtb.Text.Length > 0)
{
int currentIndex = rtb.SelectionStart;
rtb.SelectAll();
rtb.SelectionBackColor = Color.White;
rtb.SelectionLength = 0;
rtb.SelectionStart = currentIndex;
}
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
if (!_working)
{
ClearSelection(richTextBox1);
}
}
The _working bool is just to make sure that the method doesn't get trigged when the program is changing the colour of certain words so that it will only be trigged when it's the user changing the text.
Edit2: For those interested, the solution at Reset RTF in RichTextBox? seems to do the trick. I would avoid the one that was voted as duplicate (for some odd reason) since it produces more graphical issues.

Have you tried using double buffering? Maybe something like:
richTextBox1.SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer,true);

Related

How to detect styling applied to DataGridView via e.CellStyle.BackColor in the CellFormatting event?

If we've applied styling (e.CellStyle.BackColor say) to some rows via the CellFormatting event of a DataGridView, is it then possible to detect that styling at a later stage?
For example, currently we use a generic bloc of code to handle printing and exporting to Excel for any and all our DataGridViews. Until now the code hasn't catered for any styling.
So we want to add it in.
If we check the .DefaultCellStyle of the row or cell then our styling doesn't show up (it just shows as 0 or Black, which is completely wrong).
I assume that's because we've applied the styling via a CellFormatting event, instead of embedding it into the DefaultCellStyle.
Unfortunately I could not find a complete solution to your issue, only a work around.
Some experimenting with the CellFormatting event using the example from MSDN resulted in me seeing exactly what you were seeing - the BackColor was clearly being set but the CellStyle was not reflecting that. 1
The work around I found was to not use the DataGridViewCellFormattingEventArgs CellStyle property but to instead go straight to the grid. This has the downside that you now need to manually handle the case where you do not want to format the cell.
I've got some code below showing this - it is again just modifying the MSDN code:
void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// If the column is the Artist column, check the
// value.
if (this.dataGridView1.Columns[e.ColumnIndex].Name == "Artist")
{
if (e.Value != null)
{
// Check for the string "pink" in the cell.
string stringValue = (string)e.Value;
stringValue = stringValue.ToLower();
if ((stringValue.IndexOf("pink") > -1))
{
// With the commented line below we cannot access the new style
//e.CellStyle.BackColor = Color.Pink;
// With this line we can!
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Style.BackColor = Color.Pink;
}
else
{
// With the original MSDN code the else block to reset the
// cell style was not needed.
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Style.BackColor = dataGridView1.DefaultCellStyle.BackColor;
}
}
}
}
1. My theory is that this is similar to the confusion people have over the .Refresh() method, where the DataGridView has two very distinct views of itself, one being the rectangle painted on screen and the other being the underlying data. With the .Refresh() method you only repaint the rectangle, you do not refresh the data. I think this is like that - the CellFormatting event only formats during painting and doesn't do anything to the grid styles themselves.
A possible solution would be to add a second handler to the generic printing block of code (just before the actual printing). This handler should be attached to the CellFormatting event and only save the e.cellstyle in a temporary storage (such as a dictionary of cellstyles).
All cellstyles applied during your original cellformatting will be readable in your generic printing code without the need of adjusting specific cellformatting-events that have been tied to the datagridview.
At the end of the printing you can remove the handler again.
See also Is there a way to force a DataGridView to fire its CellFormatting event for all cells?

Using more than 144 adorners

It appears that the maximum number of adorners that work without any breakage is 144.
I have a ScrollView with a bunch of objects, and many of them come with adorners. The first 144 adorners are positioned correctly, but the rest are not. Note that it is an exceptional situation when there are so many; usually there are exactly zero adorners. Nevertheless, I'd like this to work properly even on that exceptional occasion.
Leaving aside how this arbitrary (and very low) limit makes me feel, are there any practical work-arounds for this bug?
At this time there is no known way of doing this.
Which is just as well, because I found the performance to be poor; simply subclassing my Image control that was supposed to display the adorner, and drawing the overlay in the OnRender, worked much better (and unlike WinForms, the visual can extend beyond the logical boundary of the control).
Here is the scenario under which I managed to implement a workout for this problem:
I have a number of textboxes that are linked to an Excel document.
The textboxes take a numerical value. They are set to invalidate on data errors in the xaml code. A data error occurs if the number is < 1, or null.
I placed an AdornerDecorator around the textbox (so that the red invalidation border appears correctly over the textbox).
In Excel, you can alter all the textboxes at the same time - but, as the OP found, if you manage to invalidate over 144 text boxes at once, the adorner decorator starts playing up, offsetting the position of the borders (the very thing it was designed to fix in the first place).
I tried a number of different solutions including invalidating the layout, however none of these worked for the situation I was facing.
Using Snoop, I found that if I refresh the textbox manually, the adorner then placed itself correctly. So, I decided to call an update to the layout from each individual textbox that needed the adorner. I did this by listening for OnValueUpdated on the textboxes. If the new value it was updating to happened to be an invalid value, I force an "UpdateLayout()" for the textbox (I only wanted to do this for invalid values as forcing an update impacts performance, and I don't want to do that every time the value changes).
In this way, regardless of the number of cells I wanted to change at once, the adorner decorator was always displayed in the correct position (except for the very last textbox to be evaluated which, despite my best efforts, is always ever so slightly misaligned).
This might be way late to the party here, but this seemed to solve the problem for me. I kept a list of the adorners that I had added to the adorner layer (called m_adorners), and in the root control where my adorners were contained, I attach to the LayoutUpdated event. Here's event handler:
private void OnLayoutUpdated(object sender, EventArgs e)
{
if (m_adorners.Any(a => !a.IsArrangeValid &&
a.Parent != null))
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
adornerLayer.InvalidateArrange();
}
}

give focus to textbox when control is shown

There is a custom UserControl which contains a TextBox. How do I give focus to the textbox when UserControl is shown? I've tried calling TextBox.Focus on UserControl.IsVisibleChanged and TextBox.IsVisibleChanged event, but that didn't help. What else can I try?
It seems that something causes TextBox to loose focus. The approach that I've mentioned normally works. How can I find out what causes the TextBox to loose focus? I've tried listening to TextBox.LostFocus event, but it's parameters don't contain much valuable information and I also don't see previous methods in the stack trace.
The code:
void TextBox1_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (this.TextBox1.IsVisible)
this.TextBox1.Focus();
}
As I've said before, it works if I use same code on a similar scenario in a test project, but it doesn't work in my application (the application is big and I am basically fixing bugs in it, and this one is amongst them). So I think that the problem isn't that focus is set improperly (as I've thought when I was opening this question), I think that the problem is that something else resets the focus. I am trying to find what it is here: Find out why textbox has lost focus .
I'm not exactly sure why it doesnt come back on visibility changed... however when I did TextBox1.Focus() in IsEnabledChanged it worked like a charm.
Use the UC as the bridge, when you let the UC shown, then call some function to focus the TextBox.

Showing tab with lots of ComboBox controls is slow with WinForms

I have set up a dialog with several tabs. One of these contains twenty combo boxes, each with over 100 items, added like this :
foreach (var x in collection)
{
string text = FormatItem (x);
combo.Items.Add (text);
}
so there is nothing fancy at all with the items. They are plain strings and the combo boxes get filled when the dialog is created. This happens almost instantenously.
However, when the user clicks on the tab containing all these combo boxes for the very first time, the GUI freezes for several seconds (and I am running on a really beefy machine).
I loaded the symbols for System.Windows.Forms and tried to break into the debugger while the program is stuck. What I have discovered is a stack trace with the following calls:
System.Windows.Forms.Control.CreateHandle()
System.Windows.Forms.ComboBox.CreateHandle()
System.Windows.Forms.Control.CreateControl(...) x 3
System.Windows.Forms.Control.SetVisibleCore(true)
System.Windows.Forms.TabPage.Visible.set(true)
which results in plenty of native transitions, WndProc calls, etc. I suppose this happens for every single item in every combo box. Phew.
Obviously, I cannot optimize WinForms. But maybe I can take some actions in order to avoid all this hell getting lose on my poor GUI? Any ideas?
Nota bene:
I've no event handlers attached on the combo boxes which could be called when the controls get created for real.
If I try to access the Handle property of the combo boxes just after having created and populated the form, I pay the penalty at that moment, rather than when the tab becomes visible for the first time. But having to wait several seconds when creating the form is not acceptable either. I really want to get rid of the long set up time.
The idea of applying BeginUpdate and EndUpdate does not apply here: these should be used to prevent the control from repainting when its items list gets filled. But in my case, the problem happens well after the control has been set up.
What you're saying is not consistent with anything I ever observed... :s
But have you tried using .BeginUpdate / .EndUpdate ?
Another thing you coud try is not populate the boxes until needed. Delay it until the box gets focus for example... (If you trap the dropdown event some user might be annoyed that the up/down arrow keys won't work.)
Everything I tried failed up to now to speed up the first display of the tab containing all the combo boxes. Data binding didn't help either.
I finally decided to fix the issue by doing a trick, similar to what danbystrom proposed, i.e. only populate the Items collection when the focus first arrives on a combo. This still produces a noticeable delay, the time all items get created (within a BeginUpdate and EndUpdate pair of method calls), but it is tolerable (approx. 200ms versus several seconds in my original scenario).
Instead of iteration your collections, would't setting the ComboBox.DataSource be a viable, and much faster alternative?
comboBox1.DataSource = myCollection1;
comboBox2.DataSource = myCollection2;
comboBox3.DataSource = myCollection3;
// and so on...
Here is a more complete example:
public class Entity
{
public string Title { get; set; }
public override string ToString()
{
return Title;
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
List<Entity> list = new List<Entity>
{
new Entity {Title = "Item1"},
new Entity {Title = "Item2"},
new Entity {Title = "Item3"}
};
comboBox1.DataSource = list;
}
I've just been coming up against this same problem, where populating a combobox with around 4000k items was unacceptably slow.
I was filling the combo in the OnLoad event handler of a form, however, when I shifted this code to constructor, after InitializeComponent(), there was no delay at all.
I guess doing this operation in the OnLoad was causing a redraw of the combo to fire, hence the delay? Anyway, just thought I'd add this in case it's of use to anyone else in this situation.
Lots of controls on a form can be a problem. I once had a form that dynamically created between 50-100 textbox controls. It was slow to load.
We solved that problem by using a datagrid instead. It's a control that is optimized for lots of data. I don't know what your exact requirements are, but it might work.

How do I hide the input caret in a System.Windows.Forms.TextBox?

I need to display a variable-length message and allow the text to be selectable. I have made the TextBox ReadOnly which does not allow the text to be edited, but the input caret is still shown.
The blinking input caret is confusing. How do I hide it?
You can do through a win32 call
[DllImport("user32.dll")]
static extern bool HideCaret(IntPtr hWnd);
public void HideCaret()
{
HideCaret(someTextBox.Handle);
}
When using the win32 call don't forget to hide the cursor in the textbox's GotFocus event.
Just for completeness, I needed such a functionality for using with a DevExpress WinForms TextEdit control.
They already do provide a ShowCaret and a HideCaret method, unfortunately they are protected. Therefore I created a derived class that provides the functionality. Here is the full code:
public class MyTextEdit : TextEdit
{
private bool _wantHideCaret;
public void DoHideCaret()
{
HideCaret();
_wantHideCaret = true;
}
public void DoShowCaret()
{
ShowCaret();
_wantHideCaret = false;
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
if (_wantHideCaret)
{
HideCaret();
}
}
}
To use the code, simply use the derived class instead of the original TextEdit class in your code and call DoHideCaret() anywhere, e.g. in the constructor of your form that contains the text edit control.
Maybe this is helpful to someone in the future.
If you disable the text box (set Enable=false), the text in it is still scrollable and selectable. If you don't like the visual presentation of a disabled text box (gray background usually) you can manually override the colors.
Be warned, manually overriding colors is going to make your form/control look weird on systems that do not use the default color/theme settings. Don't assume that because your control is white that everyone's control is going to be white. That's why you should always use the system colors whenever possible (defined in the System.Drawing.SystemColors enumeration) such as SystemColors.ControlLight.
I know this is an old thread but it is a useful reference.
I solved the problem with a much easier but very kludgie solution, which may depend on how much control you have over the user's access to the form. I added a textbox (any focus-able control) which I gave prime tabIndex value and then positioned it off-form so that it was not visible. This works fine on a dialog because the user can't resize. If the form is resizeable, this may not work.
As I said, a kludge - but a lot easier to set up. (BTW I found the HideCaret approach didn't work - but I didn't pursue it hard.)
AFAIK, this cannot be done. The TextBox control is a funny control because it actually has a lot of behaviour that can't be modified due to the way it taps into the operating system. This is why many of the cool custom TextBoxes are written from scratch.
I am afraid you may not be able to do what you wish to do :(

Resources