Environment.NewLine list display - c#

I have switch display between already exist lists without any parallel or preceding process to create it. Only thing I need is display of complete separate already exist list by switching with textbox, each size of display is about 7000 lines, but each line is also the set or several strings, so it is large output about 25.0 MB size of text.
I've tried to use several different task methods, but not sure if it is proper way for this case. Anyway all this methods works good if I use it for asynchronous display with await for preceding or parallel processing, which is successfully handled by task.
But what I have to do with Environment.NewLine which freezes GUI for few seconds, I can't move form over the screen between pressing on button and display of text.
Why same display is completely handled with task if I have it with preceding process which also creates list with huge calculation process in addition to the load, without any problems. But if I want only display already exist list, I have this short time hanging, when Environment.NewLine in task is used absolutely same way.
So I don't have any list formation process attached to display, lists are already exist, and goal is somehow display it without this short GUI hanging.
private void PART()
{
if (a != 0)
{
if (b == 0)
{
textBox1.Text = String.Join(Environment.NewLine, list3);
}
else
{
textBox1.Text = String.Join(Environment.NewLine, list4);
}
}
else
{
if (b == 0)
{
textBox1.Text = String.Join(Environment.NewLine, list1);
}
else
{
textBox1.Text = String.Join(Environment.NewLine, list2);
}
}
}
Same result with:
textBox1.Lines = list1.ToArray();

Related

C# - How to show process progress percentage & print a changing string on a Console App?

I would like to know if there is a way to do two things in a Console App for C# Code.
First one, is to show the progress percentage of a called process, so that, the user will will know more or less the elapsed progress and how much he/she needs to wait.
Second one, is to print three strings which constantly change, looping each one, until the process finishes.
Those are the Strings I would like to Print, one after another, but on the Same Line of the Console App Screen:
string Please_Wait_One_Dot = "Please wait.";
string Please_Wait_Two_Dots = "Please wait..";
string Please_Wait_Three_Dots = "Please wait...";
Console.WriteLine("\n{0}", Please_Wait_One_Dot);
Console.WriteLine("\n{0}", Please_Wait_Two_Dots);
Console.WriteLine("\n{0}", Please_Wait_Three_Dots);
// This Prints those Strings but on Different Lines without any Loop.
Basically, what I want to do is to Print some Text which shows an Message but, the Number of Dots Changes from 1 to 2 and from 2 to 3 and then it Changes back to 1, Looping this Cycle until the Selected Operation by the User is Finally Done.
NOT TESTED... use as pseudo code...
Console.write("Please_Wait");
int count = 0;
do
{
if (count < 3)
{
Console.Write("."); // add a dot
count++;
}
else
{
Console.Write("\b\b\b"); // erase all dots
count = 0; //resume
}
} while(not_done)

Appending text to a line in a listbox

Is there a way to append text to the last line in a ListBox? I'd like a listing to look like this:
processing file 1... OK
processing file 2... CRC error
processing file 3... OK
When I open the file for processing, I would write "processing file x" with ListBox.Add("processing file x"). When done processing, before moving on to the next file, I would like to append the processing results.
I could wait until processing is complete, and just write the whole line at once, but it can take 10-15 seconds to process a file and it makes the UI look unresponsive.
A solution would also let me append text like (% complete) or something to make the UI more active while processing. I prefer to do this with a ListBox because of its scrolling and line selection properties, if possible.
I've not been able to find any way to do this; any ideas would be welcome.
you can directly add items to the list box
listBox1.Items.Add("new item");
and you may need to refresh it
listBox1.Refresh();
Edit :
In case you want to update the last item, you need to remove the last item and then re add it
var lastItem= listBox1.Items[listBox1.Items.Count-1];
lastItem += results;
listBox1.Items.RemoveAt(listBox1.Items.Count-1);
listBox1.Add(lastItem);
Your problem is that a GUI event is processed on the same thread as the GUI rendering, so the UI will be unresponsive until it's done.
The quick and hacky solution is to just call Application.DoEvents() after each ListBox.Add call. The form will still be jittery if you try to drag it around, but that function will cause the GUI to update/render once.
The correct way to do it is to use a BackgroundWorker, which you can start from the GUI event and will process in the background on a separate thread. If you implement its ProgressChanged function, you can call this from DoWork:
(sender as BackgroundWorker).ReportProgress(i/(float)totalFiles,msgString)
And then in ProgressChanged, do your whole:
listBox.Add(e.UserState.ToString() + ", " + e.ProgressPercentage.ToString() + "% completed.")
Simple version:
var i = listBox.Items.Count - 1; // crash bug if there is no line to append to
listBox.Items[i] = listBox.Items[i] + message;
Or, for a more complete solution (works from a non UI thread):
public static class Util
{
public static async Task RunOnUiThread(Action a)
{
await Application.Current.Dispatcher.InvokeAsync(() => { a(); });
}
}
public partial class MainWindow : Window
{
private async Task Write(string message)
{
var action = new Action(() =>
{
var i = OutputListBox.Items.Count - 1;
if (i >= 0)
{
OutputListBox.Items[i] = OutputListBox.Items[i] + message;
}
else
{
OutputListBox.Items.Add(message);
}
});
await Util.RunOnUiThread(action);
}
}

PictureBox doesn't appear

I have an if statement, inside that if statement is a foreach, for accessing each string from a string[].
The strings are some parameters for an NPC which are read from a file. The first one represents the NPC type which are two : "battle" and "teach", the last string[] for a "teach" NPC is "end", and the rest of parameters represents photo names, which I want to load in a "dialog" PictureBox.
My test file looks like this:
teach
poza1
poza2
end
So I have 2 photos to load in the dialog PictureBox. The idea is that I must pause for 5 seconds that foreach statement, otherwise the dialog PictureBox pictures will be loaded too fast, and I won't see them.
So I tried to do that, and here is how the code looks:
if (date[0].Equals("teach")) //the first line of the date[] string, date represent the text from the file
{
foreach (string parametru in date) // i think that you know what this does
{
if (parametru != "teach" && parametru != "end") // checking if the parameter isn't the first or the last line of the file
{
dialog.ImageLocation = folder + "/npc/" + score_npc + "/" + parametru + ".png"; //loading the photo
System.Threading.Thread.Sleep(5000);
}
}
//other instructions , irelevants in my opinion
}
In my attempt of debugging this, I realised that if I use a MessageBox, the function will load the both photos. Also I'm sure of the fact that the parameters will pass the if statement.
It seems so easy to fix this error, but I can't figure out how to do it.
What you're doing now just freezes the UI. Use a System.Windows.Forms.Timer instead. Drop a Timer from the toolbox onto your Form.
Then create some fields that the Timer can access, to store your pics and the current pic position:
private List<string> pics = new List<string>();
private int currentPic = 0;
Finally, load it up with the pics you want to display, and start the Timer to go through them:
pics.Clear();
pics.AddRange(date.Where(x => x != "teach" && x != "end"));
timer1.Interval = 5000;
timer1.Start();
Then you'll have to tell your Timer to display the next picture. Increase the counter, and reset it when necessary. Something like this should work. Modify as necessary.
private void timer1_Tick(object sender, EventArgs e)
{
dialog.ImageLocation = string.Format("{0}/npc/{1}/{2}.png", folder, score_npc, pics[currentPic]);
currentPic++;
if (currentPic >= pics.Count)
currentPic = 0;
// Alternatively, stop the Timer when you get to the end, if you want
// if (currentPic >= pics.Count)
// timer1.Stop();
}
You probably need to issue a PictureBox.Refresh and/or a DoEvents command for the picture box to actually get a chance to load and display the picture.
The MessageBox automatically performs a DoEvents ... which is why it is working during debugging.

Executing task onTextChanged freezes UI

I have a textbox and a listbox in my app. Also i have a text file with many players. What I want is whenever the user enters some text, look in the players file and add the matching players to the matching list which is the data source for the listbox. The problem is that it seems to be very slow and UI freezes a short time but it's quite annoying.
This is the code i have:
private void tb_playername_TextChanged(object sender, EventArgs e)
{
//This method is used to show user the options he can choose with the text he has entered
List<string> matching_players = new List<string>();
foreach (var item in all_players)
{
string player = item.f_name + " " + item.l_name;
if ((player.IndexOf(tb_playername.Text, StringComparison.OrdinalIgnoreCase) >= 0))
{
matching_players.Add("(" + item.rating + ") " + item.f_name + " " + item.l_name);
}
}
if (tb_playername.Text.Length >= 4)
{
matching_players.Sort();
matching_players.Reverse()
listbox_matchingplayers.DataSource = matching_players;
}
}
The problem is that you are doing a relatively time consuming task in the event handler. Event handlers operate on the same thread which takes care of rendering your application and handle any other visual aspects of it, so if this thread is busy, it will not be in a position to react to user input immediately, hence freezing.
The standard approach to this problem is to offload the time consuming tasks to a Background Worker. The background worker will operate in a new thread thus allowing the main thread to continue handling UI events. This example should hopefully put you on the right track when it comes to using a background worker.
EDIT: As per your question, what you could do would be to start searching only when a particular amount of characters is entered, for instance 3, this would reduce the amount of time the background worker runs. If the user keeps on typing, you could stop the current background worker if running and launch a new one.
The background worker will fire an event when finished. You could use the RunWorkerCompletedEventArgs.Result to then extract the returned list act upon it.
private async void tb_playername_TextChanged(object sender, EventArgs e)
{
var text = (sender as TextBox).Text;
// Check length of the text
if (string.IsNullOrEmpty(text) || text.Length <= 3)
return;
// Check timer to not process if user still typing, by measuring the key stoke time
...
// Filtering
List<string> matching_players = await PlayerFilter(text);
// Minimize listbox layout time
listbox_matchingplayers.SuspendLayout();
listbox_matchingplayers.DataSource = matching_players;
listbox_matchingplayers.ResumeLayout();
}
//Time consuming method
private async Task<List<string>> PlayerFilter(string text)
{
//This method is used to show user the options he can choose with the text he has entered
return matching_players;
}
For details of the user typing check wait for user to finish typing in a Text Box

BackgroundWorker strange issues

Turns out there was an issue with the GetUserByID method, then library was updated and the problem seems to have gone away, still learnt how to better access the GUI off thread.
I wrote an application using the TweetInvi library, It retrieves a users Followers and following, Also their Picture, a link to the picture and twitter ID.
It then iterates through the returned lists and displays them (all in different lists)
Now when I first started with this application I had everything run on the _Click event and ofcourse ir froze the UI until it had completed.
I have now moved the code over to a backgroundworker Thread and It's causing some quirky issues.
Sometimes it will 'choose' not to populate certain lists, other times it will.
Sometimes it will load all the lists right except for the Following you list, which filters which of your friends are following you back (with an If statement to filter out Verified accounts)
At first I read that trying to update the UI on the separate thread can cause strange errors, so I have removed any UI control changes except for the Lists it populates.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//user name retrieved from text box, rest of this method will pull various bits of data back
var username = e.Argument.ToString();
var user = User.GetUserFromScreenName(username);
Properties.Settings.Default.LastHandle = boxUsername.Text;
Properties.Settings.Default.Save();
var usersTweets = user.GetUserTimeline(Convert.ToInt32(txtTweetAmount.Text)).ToList();
foreach (var userTweet in usersTweets)
{
lstSearchTweetList.Invoke((MethodInvoker)delegate
{
var searchList = lstSearchTweetList.Items.Add(userTweet.Text);
searchList.SubItems.Add(userTweet.CreatedAt.ToString());
});
}
var show = user.GetFollowers(500).ToList();
foreach (var friend in show)
{
string screenName = "#" + friend.ScreenName;
lstFriend.BeginInvoke((MethodInvoker)delegate
{
lstFriend.Items.Add(screenName); // runs on UI thread
});
}
var friends = user.GetFriends(500);
var followers = user.GetFollowers(500);
var result2 = followers.Where(follower => friends.All(friend => follower.Name != friend.Name));
int i2 = 0;
foreach (var res2 in result2)
{
string screenName = "#" + res2.ScreenName;
lstFollowingChecker.BeginInvoke((MethodInvoker)delegate
{
lstFollowingChecker.Items.Add(screenName);
});
i2++;
// lblFollowBackAmount.Text = Convert.ToString(i2);
}
var result = friends.Where(friend => followers.All(follower => friend.Name != follower.Name));
//lblFriendCount.Text = "(" + result.Count().ToString() + ")";
int i1 = 0;
foreach (var res in result)
{
if (res.Verified != true)
{
string screenName = "#" + res.ScreenName;
lstFollowerChecker.BeginInvoke((MethodInvoker)delegate
{
lstFollowerChecker.Items.Add(screenName);
});
i1++;
// lblCheckerCount.Text = Convert.ToString(i1);
}
}
backgroundWorker1.ReportProgress(1,username);
}
The function calling RunWorkerAsync()
private void btnFind_Click(object sender, EventArgs e)
{
//start backgroundworker and clear friends and search lists
pctProgressBar.Visible = true;
lstFriend.Items.Clear();
lstSearchTweetList.Items.Clear();
lstFollowerChecker.Items.Clear();
lstFollowingChecker.Items.Clear();
lstFriend.Items.Clear();
lstSearchTweetList.Items.Clear();
if (txtTweetAmount.Text == "")
{
txtTweetAmount.Text = "20";
}
backgroundWorker1.RunWorkerAsync();
}
My problem is the strange unexplainable errors are still occurring seemingly randomly.
If this is caused by the lists being updated in the background worker thread, what use is the background worker if I cant use it to do the intensive stuff
I will also include two pictures of a friends account as it better demonstrates the issue's so something handles etc will be blanked out.
First Problem is that it sometimes populates a list multiple times, and the "Not following you back list" should be returning #Theilluminati only once
Again it returns #Theilluminati but lists it twice.
There's also an issue of if I run the below code anywhere, the background worker does not run, that is, It will pull back the picture/name/location but the background worker doesn't run and If I try do it in the actual backgroundworker thread then the lists won't populate.
var username = boxUsername.Text;
var user = User.GetUserFromScreenName(username);
//string ImageURL = user.ProfileImageUrl;
//string biggerImageURL = ImageURL.Replace("_normal", "");
//txtImageURL.Text = biggerImageURL;
//pctDisplaypicture.ImageLocation = biggerImageURL;
//txtTwitterID.Text = user.Id.ToString();
//lblFriendCount.Text = "(" + user.FollowersCount + ")";
Any help at all would be appreciated, I'm now struggling to see the use of Backgroundworker if it can't unload work from the UI thread, Sorry for the long post, Thanks for reading.
Fix Attempts
I have disabled the find button while the task is running and the same issue still occurs.
I have also tried using if(working.Cancellationpending == true) to break out of loops once the task has completed once.
I have changed the list foreach loops to the below respectively, and passed the username as a variable instead of pulling it from the control, the problems seem to have just got worse, no lists at all populate now.
lstSearchTweetList.Invoke((MethodInvoker)delegate
{
lstSearchTweetList.Items.Add(userTweet.Text).SubItems.Add(userTweet.CreatedAt.ToString());
});
backgroundWorker1.RunWorkerAsync(boxUsername.Text);
var username = e.Argument.ToString();
I have tried both answers as solutions and both still lead to same issue's with differing severity, I am still stuck with the problem that uncommenting the code to retrieve name/picture etc still blocks the backgroundworker from running. No matter where it's run from.
You may need to use the Invoke method on the list controls that you are trying to update on the background thread like so:
string screenName = "#" + friend.ScreenName;
lstFriend.Invoke((MethodInvoker)delegate {
lstFriend.Items.Add(screenName); // runs on UI thread
});
One problem you can have with multi-threading is when you try to access shared resources (Collections, Files, etc.) from multiple threads deadlocking can occur as well as race conditions. In order to do this safely a locking object would be created in this case and lock the code that is accessing the shared resource. This way the resource can only be accessed one at a time.
//defined globally
object _MyLockingObject = new object();
and within a certain method locking a list:
lock(_MyLockingObject)
{
myList.Add(item);
}
You are breaking a fundamental rule in Windows GUI programming: never access a control from a thread that is not the same thread that created the control. Bad mojo things happen when you break this rule ;)
Pass the username value via backgroundWorker1.RunWorkerAsync(boxUsername.Text);, and read it via e.Arguments as string.
You then need to use BeginInvoke to interact with the UI controls. Ideally, you should optimize this lambda to suspend the control's layout, replace the entire list of items one call, and resume the control's layout.
// execute on the UI thread
this.BeginInvoke((Action)(() =>
{
lstFriend.Items.Add("#" + friend.ScreenName);
}), null);
I would use the async Control.BeginInvoke over the sync Control.Invoke option. There does not appear to be a reason to wait on the control to render your change.

Categories