I'm currently working on a chatting program and the idea is to make it a secret one (Kind of like Facebook has the secret chat function).
My messages are sent to a listBox component and I want that every 10 or 'n' seconds the oldest message would get deleted. I
was trying to mark every message with an index but didn't quite understand how that works.
What I'm asking if maybe you guys know a function or could help me write one that does just that. I'm using Visual Studio 2015 Windows Forms, C#.
Well, when you have a ListBox, the items are all indexed since it's an object collection (an array of objects). Starting from 0 and going upwards for newer entries.
So let's say we add 3 items to our ListBox
listBox1.Items.Add("Item 1"); //Index 0
listBox1.Items.Add("Item 2"); //Index 1
listBox1.Items.Add("Item 3"); //Index 2
All you would have to do, is create a thread that runs in the background that deletes the item at index 0 (the oldest entry) each time.
new Thread(() =>
{
while(true)
{
if(listBox1.Items.Count > 0) //Can't remove any items if we don't have any.
{
Invoke(new MethodInvoker(() => listBox1.Items.RemoveAt(0))); //Remove item at index 0.
//Needs invoking since we're accessing 'listBox1' from a separate thread.
}
Thread.Sleep(10000); //Wait 10 seconds.
}
}).Start(); //Spawn our thread that runs in the background.
In C# WinForms a ListBox contains ListBoxItems which are a ObjectCollection (msdn-link)
So you can add any Object you like, the message which will be displayed comes from the DisplayMember
So for example
public class MyMessage {
public DateTime Received { get; set; }
public string Message { get; set; }
public string DisplayString
{
get { return this.ToString(); }
}
public string ToString() {
return "[" + Received.ToShortTimeString() + "] " + Message;
}
}
can be added as ListBoxItem.
Setting the DisplayMember to "DisplayString" (more here) will get you the correct output.
now you can iterate through the ListBoxItems, cast them as MyMessage and check the time when they were received.
I don't know if you thought about this but here's a way you could achieve this task.
First create an List of strings
List<string> list1 = new List<string>();
To use the List feature you will have to include collections in the form
using System.Collections;
Now comes the tricky part.
First declare a static integer variable globally i.e. outside all classes.
static int a;
Whenever you receive a message(considering your messages will be in string format) you've to add that string to list1 which you created.
list1.Add("the received message");
Now you've to declare a Timer (If you're new, check out how timers work). Windows forms already has timers, using that will be preferable.
The timer sends a Tick event after the desired time.
private void timer1_Tick(object sender, EventArgs e)
{
a = list1.Count() - 1; //Count will return the number of items in the list, you subtract 1 because the indexes start from 0
list1.RemoveAt(a);
listBox.Items.Clear();
foreach(string x in list1)
{
listBox.Items.Add(x);
}
}
What this code will do is, at every Tick event of the timer it will refresh the listbox, remove the last element from the array, and refill the listbox with the rest.
To use the timer just drag and drop it on the form. It's all GUI based and easy to figure out.
Let me know if you've doubts.
Tip: Make maximum use of try{} & catch{} blocks to avoid app crashes.
Related
Forgive me if this is simple. I come from the Embedded RToS world and have learnt c# for just for the purpose of test equipment for production. I think I have solved all the obvious issues already, just the tasks are not acting as expected.
I have a test jig with 10 serial ports and other things attached and a very basic windows forms UI with 10 list box's that need to all run simultaneously. I have cut the code down to just what is needed for the demo of the problem (no serial ports etc)
I am unable to get 10 listbox's (or textboxs) to update from 10 tasks properly. I can easily do one at a time, even 5 works, but 10 and it does not work.
I was getting exception out of range as it somehow gets passed a 10, but I only pass 0-9 so I reduced the task count to 9 for now until I understand the fundamental issue(s).
private void buttonStart_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => PTT.ProductionTest());
}
This is the task from the PTT class, it will manage and monitor the 10 tasks so the UI is still responsive
public static void ProductionTest()
{
//Somewhere to store the tasks
List<Task> TestSlotTasks = new List<Task>();
//Create the tasks for each of the test slots
for (int i = 0; i != 9; ++i)
{
TestSlotTasks.Add(Task.Factory.StartNew(() => new PTT().TestSlot(i)));
Program.ListBoxAddLine(string.Format("CALL[{0}]: task start/stop test", i), System.Drawing.Color.LawnGreen, i);
}
Task.WaitAll(TestSlotTasks.ToArray());
}
This is supposed to be a non static program so I can create multiple instances (so I can run it many times without issue, that is my intention anyway, right?)
private void TestSlot(int slot)
{
for (var z = 0; z != 10; ++z)
{
Program.ListBoxAddLine(string.Format("PTT[{0}]: task start/stop test", slot), System.Drawing.Color.LawnGreen, slot);
}
}
This is the invoke method i use to update the listBox. The list box is in a generic list something like this
//ListBoxs.Add(listBoxSlot1); //Done for each of the 10 list box's to put in a list
//This is the method that will add a line to the main text box and to the log file
public static void ListBoxAddLine(string TextToAdd, System.Drawing.Color textColor, int slot)
{
//Update the list box on the main screen
if (mainForm != null)
{
if (FormPTT.ListBoxs[slot].InvokeRequired)
{
FormPTT.ListBoxs[slot].Invoke((MethodInvoker)delegate ()
{
FormPTT.ListBoxs[slot].ForeColor = textColor;
FormPTT.ListBoxs[slot].TopIndex = FormPTT.ListBoxs[slot].Items.Add(TextToAdd);
});
}
else
{
FormPTT.ListBoxs[slot].ForeColor = textColor;
FormPTT.ListBoxs[slot].TopIndex = FormPTT.ListBoxs[slot].Items.Add(TextToAdd);
}
}
}
The output I get is pretty random if i go above 5 or 6 list box's for this example.
Using the above code, I get this
output in the list box's, ignore the other stuff
So its all ok up until slot 8, its shows the Call[7] task start/stop but nothing from the task call and 9 and 10 are ok, but I dont call it 10 times, so how is 10 getting updated? I dont pass 10? and where is 8's PTT[7]... output?
I assume I am just not understanding a few things about tasks...
In this method i'm reading a text file from my hard disk and add the items to the listView.
I also changed in the form1 designer on the listView propeties the property CheckBoxes to true.
Now when i'm running my program it's taking like 10-15 seconds to load it up all.
The form1 constructor:
LoadtoListView();
And the method LoadtoListView:
private void LoadtoListView()
{
int countit = 0;
using (StreamReader sr = new StreamReader(#"c:\listviewfile\databaseEN.txt"))
{
while (-1 < sr.Peek())
{
try
{
string name = sr.ReadLine();
string email = sr.ReadLine();
var lvi = new ListViewItem(name.Substring(name.IndexOf(":") + 1));
lvi.SubItems.Add(email.Substring(email.IndexOf(":") + 1));
listView1.Items.Add(lvi);
countit++;
}
catch (Exception) { }
}
sr.Close();
numberofforums = countit;
}
}
There are 547 items to load and 547 checkBoxes.
I tested now if i change in the designer the listView property of the CheckBoxes to false again it will load fast about 1-2 seconds.
But once i'm turning this property of the CheckBoxes to true it's tkaing more then 10-15 seconds to load.
I guess the problem is that it's taking time to draw all the CheckBoxes.
Is there any way to make it all faster ?
There exist a couple of ways you could make that faster. Actually you could make it blazingly fast, checkboxes or no checkboxes.
Your code requires a few tweaks here and there and you must think about reusability, concern separation and parameterisation.
For instance, the method name LoadToListView is already doing too much work. It loads stuff, and it also populates the listview.
There are 3 ingredients that can get you to Nirvana.
First of all
consider creating an up front materialised list of ListViewItem instances, more particularly, create a primitive array, such as this (by the way, I will also sprinkle some other good practices along the way, even though it is not the lacking of those practices which causes your delays):
public ListViewItem[] LoadItems(string filePath) {
// not hardcoding the filePath is a good idea
List<ListViewItem> accumulator = new List<ListViewItem>();
int countit = 0;
using (StreamReader sr = new StreamReader(filePath)) {
while (-1 < sr.Peek()) {
try
string name = sr.ReadLine();
string email = sr.ReadLine();
var lvi = new ListViewItem(name.Substring(name.IndexOf(":") + 1));
lvi.SubItems.Add(email.Substring(email.IndexOf(":") + 1));
// instead of adding this item to the list
// --> no more this:: listView1.Items.Add(lvi);
// just "accumulate" it
accumulator.Add(lvi);
countit++;
}
catch (Exception) { }
}
// no need to manually close the reader
// sr.Close();
// the using clause will close it for you
numberofforums = countit;
}
return accumulator.ToArray();
}
Okay.. So we've created an array of ListViewItem.
"So what?" you might think.
Well, for each and every Add invocation on your ListView, the ListView will try to react graphically (even if the GUI thread is occupied it will still try). What this reaction is is not for this answer to be concerned. What you must understand is that instead of Add you could call AddRange which takes a primitive array of ListViewItem as its parameter. That will cause just one graphical reaction for all the set of ListViewItem instances which means it will speed up your app a lot.
So here's ingredient number 2
Make another method which calls LoadItems and then calls AddRange on the ListView:
public void SomeOtherPlace() {
string filePath = #"....";
ListViewItem[] items = LoadItems(filePath);
this.listView1.Items.AddRange( items );
}
This will have already given your app the extra speed you were looking for, but even if the next step will not make your app elegant, it will surely help.
And ingredient number 3 :: Asynchrony
It would be grand if your UI didn't freeze while the LoadItems method was being called.
This "non freezing" ability can be achieved in many ways, but the most modern and coolest way is to use Task<T> and the async and await operators introduced in .NET 4.5 and C# 5.0.
If you don't have a clue about what these things are then, just enjoy the first two ingredients but don't hesitate to learn about the entities I've mentioned.
Basically what you need to do is:
make sure you can't possibly call SomeOtherPlace() twice, since what we're about to do is to make this a possibility. So if you have a button's event handler, for instance, which is calling SomeOtherPlace then we should disable that button and reenable it once we're done
we will make the SomeOtherPlace() method be an async method, which allows it to await tasks
we will run the LoadItems code on a separate thread all nicely wrapped in a Task<ListViewItem[]> and await it on the GUI thread
Let's go. The first change is this:
public void SomeOtherPlace() {
becomes
public async void SomeOtherPlace() {
Secondly, we disable the button I talked about:
public async void SomeOtherPlace() {
this.button1.Enabled = false;
...
this.button1.Enabled = true;
}
Third, we turn this line:
ListViewItem[] items = LoadItems(filePath);
into this:
ListViewItem[] items = await Task.Factory.StartNew(() => LoadItems(filePath));
Now your method should look something like this:
public async void SomeOtherPlace() {
string filePath = #"....";
ListViewItem[] items = await Task.Factory.StartNew(() => LoadItems(filePath));
this.listView1.Items.AddRange( items );
}
Hope I didn't forget anything.
Good luck and don't settle for not understanding how things work under the hood!
Use listView1.Items.AddRange instead of adding one ListViewItem at a time. This should improve your load time.
i'm trying to update/paint a listbox in real time, but i'm having some problems.
I got a button to start the process of populating the listbox button_traceroute_Click.
My problem is that the listbox is only painted/updated when the whole process (button click) has ended, i wanted the the items to be inserted(viewed) one by one. I already tried using ListBox.Update() but nothing happened.
(this is a traceroute)
private void button_traceroute_Click(object sender, EventArgs e)
{
String target;
int i = 0;
target = textBox_target.Text;
Mydata data = new Mydata();
TraceRoute traceroute = new TraceRoute();
while (i < 50 && !data.getReached() && !data.getError()) // i = hop count
{
data = traceroute.startTrace(target, data, i);
listBox_trace.Items.Add(data.getOutput());
i++;
}
}
data.getOutput() returns (string) something like: "Hop X: 165.468.354.4 -> 50 ms" or "Hop X: Timeout"
Mydata{
bool Finish flag;
bool Error flag;
int badcounter;
String output;
}
For now im populating the listbox with Strings, but the objective is to use a object.
You need to put the long running operation on it's own thread. then report the progress back to the UI intermittently.
You can see an example how to do this in another post of mine here.
Also, you can use the BeginUpdate and EndUpdate method to speed up the repainting of the listbox. When BeginUpdate is called, any pending paint to the listbox is suspended, likewise, EndUpdate resumes the painting, this can help making your listbox look as if it is fast in loading the data into it and minimizes the number of painting to it when adding the data.
Hope this helps,
Best regards,
Tom.
Try this:
data = traceroute.startTrace(target, data, i);
listBox_trace.Items.Add(data.getOutput());
Application.DoEvents();
i++;
Its not ideal - Michael G's answer is best, but this could work as a quick fix.
I'm Doing a project on FileTransfer in which i have a listview , i will get events from one of my class file for updating the percentage of the file sent so far,after receiving it i will place the percentage in my listview ,while doing that the listview got
a flickering effect how to avoid it.i used application.doevents() but it doesnt works. i have seen in torrents while updating the percent the list doesnt get flickered
how to achieve this .
void Sender_Progress(int CurrentValue, string Ip) // here im receiving Events
{
try
{
//if (CurrentValue == 1)
// UpdateTimer.Enabled = true;
//list_send.Items[CurrentValue].SubItems[4].Text = Ip.ToString();
//Application.DoEvents();
obj = new object[] {CurrentValue, Ip };
list_send.Invoke(new UpdateList(UpList), obj);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public void UpList(int Val, string ind) // here im updating the listview
{
Application.DoEvents();
int index = 0;
index = Convert.ToInt32(ind);
index = index - 1;
list_send.Items[index].SubItems[4].Text = Val.ToString();
if (Val == 100)
{
list_send.Items[index].SubItems[2].Text = "Completed.";
//UpdateTimer.Enabled = false;
}
//Application.DoEvents();
}
Firstly, you don't need the DoEvents, since you are already correctly working on two threads. Remove that. After that, I expect the problem is simply doing too much too quickly. Is it possible to batch updates, and only send an update, say, every 20? 50? times? It isn't clear what the control is, but many have multiple-update modes; for example with ListView:
theList.BeginUpdate();
try {
// make multiple updates here...
} finally {
theList.EndUpdate();
}
I would then see about passing over a list of updates, say, every 20 times (unless each takes a considerable time) [note it must be a different list per Invoke, and you need to remember to send any remaining items at the end, too].
Use worker thread - it's available from the toolbox and has two events that are invoked in the main (UI) thread.
The Progress event can be used to signal the listbox that it need to refresh or that the task was completed.
i overcome the flickering effect succesfully,im getting events frequently ,i will get an integer everytime, i will store it in a variable and compare it with next variable received by the event if it matches i wont invoke the listview,otherwise i will invoke it.now the flickering goes away. thanks all.
I'm making a simple Guess-The-Number game with a GUI. I need to wait on a loop waiting for the user to input a number in a text box and press "OK". How do I wait for an event inside a loop?
Note: I don't want message boxes. This is done in the main window, hence the need to wait for input.
EDIT: I should have explained myself better. I know that there's a loop inside the GUI. What I want is another loop inside a method. Maybe there's a better way to do this. I could code stuff inside the button's event handler, now that I think about it. Although I'd need global variables. Whataver, I'll think about it, but I hope my question is clearer now.
EDIT 2: Sorry that my question wasn't clear and the edit didn't do much help. First of all, the code is too big to be posted here. I'd probably have to post a screenshot of the GUI, so it wouldn't be of much use. Basically, I have two fields, "Max number" and "Number of allowed guesses". The user enters these two and clicks "Play". A new panel becomes available, with a text box and a "Guess" button. The user enters a guess, and the program checks to see if it's correct.
The purpose of the second infinite loop is to avoid global variables. See, each time the user clicks "Play", the game has to generate a new random number as the correct guess. If everything is done inside a method, no problem. But if the "Guess" button's event handler is called multiple times, the number has to be stored as an instance variable of the Form. Sure, it's not big deal, but I think the number should be a property of the method directing the current game, not of the Form.
I'd also have to keep track of the remaining number of guesses outside of the method. Again, it's no big deal. I just want to avoid globals if I can.
Again, I'm sorry that my question wasn't too clear. I'm kind of tired, and I didn't feel like writing too much. If this still isn't clear, then don't bother. I'll think of something.
C# automatically loops infinitely waiting for events until your form is closed. You just need to respond to the button click event.
Jason Down's suggestion is wise, create a new GuessingGame class and add it to your project. I know you're worried about "global variables" (which everyone is taught in school never to use unless you absolutely have to), but think about your design specifications for a minute.
But if the "Guess" button's event handler is called multiple times, the number has to be stored as an instance variable of the Form. Sure, it's not big deal, but I think the number should be a property of the method directing the current game, not of the Form.
As an alternative, store an instance of your GuessingGame class in the form. This is not a global variable! You said so yourself, the point of the game is keep track of the guesses and generate new numbers to guess every time "Play" is clicked. If you store an instance of the game in the form then open another form (e.g. a Help or About box), then the game's instance would not be available (thus, not global).
The GuessingGame object is going to look something like:
public class GuessingGame
{
private static Random _RNG = new Random();
private bool _GameRunning;
private bool _GameWon;
private int _Number;
private int _GuessesRemaining;
public int GuessesRemaining
{
get { return _GuessesRemaining; }
}
public bool GameEnded
{
get { return !_GameRunning; }
}
public bool GameWon
{
get { return _GameWon; }
}
public GuessingGame()
{
_GameRunning = false;
_GameWon = false;
}
public void StartNewGame(int numberOfGuesses, int max)
{
if (max <= 0)
throw new ArgumentOutOfRangeException("max", "Must be > 0");
if (max == int.MaxValue)
_Number = _RNG.Next();
else
_Number = _RNG.Next(0, max + 1);
_GuessesRemaining = numberOfGuesses;
_GameRunning = true;
}
public bool MakeGuess(int guess)
{
if (_GameRunning)
{
_GuessesRemaining--;
if (_GuessesRemaining <= 0)
{
_GameRunning = false;
_GameWon = false;
return false;
}
if (guess == _Number)
{
_GameWon = true;
return true;
}
else
{
return false;
}
}
else
{
throw new Exception("The game is not running. Call StartNewGame() before making a guess.");
}
}
}
This way, all the data related to the game is encapsulated within the class. Hooking up the events is easy in the codebehind of the form:
GuessingGame game = new GuessingGame();
private void btnPlay_Click(object sender, EventArgs e)
{
int numberOfGuesses = Convert.ToInt32(txtNumberOfGuesses.Text);
int max = Convert.ToInt32(txtMax.Text);
game.StartNewGame(numberOfGuesses, max);
}
private void btnGuess_Click(object sender, EventArgs e)
{
int guess = Convert.ToInt32(txtGuess.Text);
bool correct = game.MakeGuess(guess);
if (correct)
lblWin.Visible = true;
if (game.GameEnded)
{
// disable guess button, show loss label
}
}
You should probably look for a book to actually learn windows programming.
The very basics:
1) There is already an infinite loop deep down in the windows code somewhere. Any windows program is constantly looping and scanning for input.
2) Once input is found, this loop fires off an Event.
3) Your mission, should you choose to accept it, is to write event handlers to handle those events.
you are most likely doing it wrong as it has already been pointed out, but you can use this
Application.DoEvents();
to process events when you are on an actual loop
to do it the right way
- don't use a loop
- use an edit box for the input, then a button
- implement the button onclick event
Yes, and What if I am waiting for Speech events, it could happen anytime event when a function is running, I need to handle that without recursively call a function