TextBox Scrollbar. Scrolling to end of TextBox within Thread - c#

I've tried all the answers suggest in Stack Overflow to get my scrollbar to move to the bottom as text is being updated, but I have a feeling its not working because it's within a thread. My code is below ...
foreach(HtmlAgilityPack.HtmlNode paginationUser in paginationUsers) {
String userUrl = paginationUser.GetAttributeValue("id","");
this.Invoke((MethodInvoker)delegate {
txtLog.AppendText("...... Added " + userUrl + Environment.NewLine);
txtLog.Select(txtLog.Text.Length, 0);
txtLog.ScrollToCaret();
});
}
Is it the thread thats causing the code not to work? And what's a better solution?

Try to add this code :
TextBox.SelectionStart = txtLogEntries.Text.Length;
TextBox.ScrollToCaret();
at onTextChanged TextBox event .

I feel using this code is better:
TextBox.AppendText("your text")
it will automatically scroll to the end of the newly appended text & the auto scrolling animation seems more smoother compared to TextBox.ScrollToCaret() method
you can put this code at TextChanged TextBox event

I am having the same problem with WPF, using a thread to write to the textbox. It works fine until I add the ScrollToEnd.
I have no solution, just some remarks.
You are not locking the control. You should if you are filling it from a tread.
If I use Invoke it does work (but the UI becomes unresponsive). I use BeginInvoke, which is smoother but then it totally locks up if I use ScrollToEnd.
It seems to be some kind of triggering issue, one event causing the other.
Try feeding text slowly and see what happens, the worker thread may be flooding the textbox, giving it a really hard time, not allowing the main thread to do its thing.

Related

How do I avoid having my Rich Text Box, "scroll bar," freeze up?

My issue is in the .NET framework using C# to create a simple form application that contains a rich text box (RTB) control.
Briefly the issue I am experiencing is that when trying to clear the contents (.Text) of the RTB, the scroll bar doesn't go away. I would like to know if there is anything inherently wrong with the way I am using the RTB. I apologize, but the site will not allow me to post images yet. So if there is a misunderstanding regarding what "doesn't go away" means, please ask!
So first, I write data to the box using the following code snippet:
// append the new message
this.rtb_receive_0.Text += message;
this.rtb_receive_0.SelectionStart = this.rtb_receive_0.Text.Length;
this.rtb_receive_0.ScrollToCaret();
Later on, I clear the RTB contents (RTB.Text) with the following code:
this.rtb_receive_0.Text = String.Empty;
this.rtb_receive_0.Refresh();
In the above code I have attempted to fix my problem with the, "Refresh," method. However it does not seem to be doing the job.
When I clear the RTB contents, the scroll bar does not go away... I noticed that if I grab another window and drag it over the top of the application, that the frozen scroll bar disappears. Also, I can minimize the application, then maximize it again and the bar will disappear. There has to be a way to prevent this frozen scroll bar from happening in the first place though.
Per the answer, here was the fix to stop the bar from freezing up:
this.rtb_receive_0.Text = String.Empty;
this.rtb_receive_0.Clear();
this.rtb_receive_0.ScrollBars = RichTextBoxScrollBars.None;
this.rtb_receive_0.ScrollBars = RichTextBoxScrollBars.Vertical;
this.rtb_receive_0.Refresh();
Have you tried simply just programatically setting the Scrollbars property on the RTB?
myRichTextBox.ScrollBars = RichTextBoxScrollBars.None;
Edit: I think I misinterpreted what you needed. Searching around, I found this similar post on another forum: http://www.vbforums.com/showthread.php?793671-RESOLVED-RichTextBox-Visual-Bug
This user is setting the value of an RTB based on a selection in a list view. When a new value is set and does not require a scrollbar it doesn't re-draw and still shows the bar.
It seems like adding myRichTextBox.Clear(); myRichTextBox.Refresh(); should help. In this case that user is also programatically setting the ScrollBars property as well.
Also, are you able to determine how many lines of text can fit in the RichTextBox before a scrollbar is needed? I suppose that might vary based on system settings on the machine, but you might just be able to programatically check myrtb.Scrollbars = (myrtb.Lines.Length > X) ? Vertical : None; (excuse the psuedo code syntax)
What helped for me was just calling the refresh() method twice. Very ugly, but it does the job.
Hmm, after more thorough testing this ugly fix proved to be not so much of a fix afterall. It helps, but still some glitches.
refresh();
update();
seems like a better solution.
I was having this same problem. I solved it by calling the Invalidate() method which forces the control to repaint.
Me.RichTextBox.Clear()
'Call Invalidate in order to force the RichTextBox to repaint. I do this so that any
'Visible Scroll bars are removed after clearing the Text
Me.RichTextBox.Invalidate()
I tried with Refresh(); Update(); Invalidate();,but it didn't worked for me.
I solved this problem using following three lines :-
RitchTextBox.Clear(); //Clearing text in RichTextBox
RitchTextBox.ScrollBars = RichTextBoxScrollBars.None; //Remove scroll
RitchTextBox.ScrollBars = RichTextBoxScrollBars.Vertical; //Again add scroll
Try those above three lines. It will work 100%.

Application hanging on Control.Invoke except when debugging

I have my main form kicking off some background work using Delegate.BeginInvoke and within those delegates I am adding some rows to be displayed on a DataGridView on my main form. I have a backing dataset and a BindingSource attached to that, which I use as the source for my DataGridView.
Whenever I add a row, I do this:
ResultsDataTable.AddResultsRow(row);
RefreshDataGridView();
Where RefreshDataGridView() looks like this:
private void RefreshDataGridView()
{
if(InvokeRequired)
{
//I have tried dgvResults.Invoke() as well
dgvResults.BeginInvoke(new Action(() => RefreshDataGridView()));
}
else
{
dgvResults.Refresh(); //this is where it hangs
dgvResults.FirstDisplayedScrollingRowIndex = dgvResults.Rows.Count - 1;
}
}
It works well, when I add a new row it displays instantly and scrolls (despite my scrollbar not being drawn correctly but I can live with that) as expected, but only when I run the app through the debugger. When I start it without debugging, the application hangs whenever a row is added and it actually needs to scroll.
I've built the application in debug mode and run it without debugging, then let it get to the point where it hangs and attached the debugger to the process to see where it is happening (see comment in code above).
I know this is happening because my main thread is waiting for something but I have no clue what it is waiting for or how to find out.
Does anyone have any ideas?
Update: I started it without debugging then attached the debugger again, and found that the main thread is getting stuck updating a control, but I can't figure out which one.
Update 2: I got rid of the refresh and now it doesn't hang when adding the new row, but I can't resize my form at all without it hanging.
Update 3: It seemed to be hanging while trying to update the scrollbars of the data grid, so I encapsulated it in a panel and gave that scrollbars instead. With a bit of hacking to get the data grid to dynamically size itself based on the data it contains, it's a bit glitchy but no more deadlocks.
I had the same issue. You mentioned that your DataGridView has a bindingsource attached to it. If you are doing something to your source, then you are affecting the DataGridView. You will need to place that line of code that is modifying the source inside the BeginInvoke statement. Once I did that, the issue is gone.

How to clear the text inside a TextBox when it is being passed down in a function?

Here is what my code looks like:
private void exportToExcelButton_Click(object sender, EventArgs e)
{
txtBox.Clear();
txtBox.AppendText("Beginning Export...");
ExportExcel(txtBox);
txtBox.AppendText("Export complete...");
}
The problem I am having is that whenever the button is clicked (to execute the function above), only part of the current text in the TextBox (System.Windows.Forms.TextBox) is cleared, and replaced with the first line: "Beginning Export ...".
However once the function ExportExcel(txtBox) is done executing, then the entire text is replaced by the new one generated in ExportExcel(txtBox).
Inside ExportExcel(txtBox); I have several txtBox.AppendText() statements explaining to the user the actions being made.
I have tried clearing the text with txtBox.Text = String.Empty; and txtBox.Text = "";and neither have worked.
Let me know if anything needs to be clarified, thanks.
Looks like you're blocking the GUI thread, preventing the text box from redrawing itself. This is one reason why you shouldn't perform long-running tasks on the GUI thread. Use a background thread instead. That way you leave the GUI thread free to perform important operations like drawing, responding to mouse clicks, etc.
Have you tried the textBox.Refresh , before calling txtBox.AppendText("Beginning Export...").
The method invalidates the control.
On the other hand, if you use a background thread, then you should update the UI only by overriding the Progress Changed event. Background threads are not meant for updating user interfaces. Try searching for Worker threads and UI threads. They correlate to MFC, but the concept is the same.
Also keep in mind the cross thread calls.
I agree with dvnrrs. However if you are unable to do this, try calling txtBox.Refresh();after adding each line of text.
There is another method called Application.DoEvents(); that has a similar behavior, but its use is not recommended since it sort of short-circuits the normal application flow and can cause your application to fail unexpectedly or do strange things.

Unreliable GUI update in multithreaded C# program

I have a program that runs a series of methods in other threads within one window and let's the user know what's going on using a status bar. The status bar updates are in the main thread which set's the status bar and then refreshes the GUI. There are several blocks of code in series each looking something like this:
Thread do1Thread = new Thread(Class.Method);
do1Thread.Start();
// inform user
this.status.Text = "Doing stuff 1...";
// update GUI
Utility.RefreshGUI();
// join thread
do1Thread.Join();
Sometimes the status bar does indeed update but often is stays on the first status until the end when it displays the last status. Occasionally is sticks on "Ready." which is the default.
Note that two of the blocks take a few seconds so there should be time for it to update. Also, the program is written in C# (Mono) using GTK# for the GUI.
How can I ensure that that the GUI updates to reflect the change?
The problem is that the Join() call blocks the UI thread which blocks all window messages.
Can you use a BackgroundWorker and execute whatever code you have after the Join in the RunWorkerCompleted call?
You need to dispatch Update message to UI thread, call invoke instead of direct property
this.status.BeginInvoke((Action)(() => this.status.Text = "Something happen"));
The best way I have found to update a control in a primary thread is to set a delegate for updating and invoke that from other threads.
You have to use observe and observable pattern.
EDITED:
It is really better divide logic and view parts of code.
Here is an example in real world how to use. Pattern
Could you check whether you are using a StatusStrip control?
If so, your code looks like setting directly the Text of Stautus Strip Control
this.status.Text = "Doing stuff 1...";
So it wont reflect in the Status Strip as Text. You have to place a toolstriplabel and need to set its text in this case.
Please check the post here

Paragraph.BringIntoView() works only when focused

I am working on a text editor that is based on RichEditBox. I have implemented functionality "Go to line" which eventually resolves to
TextPointer.Paragraph.BringIntoView();
Along with this I also set the caret position.
What I found out is that BringIntoView only works when I click on the RichEditBox first (focus it). Otherwise it seems to get ignored. I can see that the caret position has been adjusted by the code around BringIntoView though.
Does anybody know what is the reason/nature of that problem? How can I overcome it?
Found a workaround for this, not sure if it will work in a pure WPF environment, in my case I'm running WPF inside a mainly Windows Forms solution using WPF UserControls where needed.
Instead of invoking BringIntoFocus() immediately, defer it to a later moment by adding it to a queue that gets handled by a timer. For example:
System.Windows.Forms.Timer DeferredActionTimer = new System.Windows.Forms.Timer() { Interval = 200 };
Queue<Action> DeferredActions = new Queue<Action>();
void DeferredActionTimer_Tick(object sender, EventArgs e) {
while(DeferredActions.Count > 0) {
Action act = DeferredActions.Dequeue();
act();
}
}
In your forms constructor, or in the OnLoad event add:
DeferredActionTimer.Tick += new EventHandler(DeferredActionTimer_Tick);
DeferredActionTimer.Enabled = true;
Finally, instead of calling TextPointer.Paragraph.BringIntoView(); directly, call it like this:
DeferredActions.Enqueue(() => TextPointer.Paragraph.BringIntoView());
Note that the Windows Forms timer kicks events off in the main thread (via the message pump loop). If you have to use another timer you need a bit of extra code. I'd recommend you to use System.Timers.Timer rather than the System.Threading.Timer (it's a little more thread-safe). You would also have to wrap the action in a Dispatcher.Invoke structure. In my case, the WinForms timer works like a charm.
Can't you just give the RichTextBox(?) focus first then, using Keyboard.Focus(richTextBox) or richTextBox.Focus()?

Categories