Can't add strings to listbox - c#

Hi i'm trying to collect output of this library into listbox.
Here's part of code from the test project, with i've tried to modify:
public partial class Form1 : Form
{
D.Net.Clipboard.ClipboardManager Manager;
public Form1()
{
InitializeComponent();
Manager = new D.Net.Clipboard.ClipboardManager(this);
Manager.Type = D.Net.Clipboard.ClipboardManager.CheckType.Text;
Manager.OnNewTextFound += (sender, eventArg) =>
{
button1.Text = eventArg; //just testing, working correctly
listBox1.Items.Add(eventArg); //does not show neither result nor any error
MessageBox.Show(String.Format("New text found in clipboard : {0}", eventArg));
};
}
private void button1_Click(object sender, EventArgs e)
{
listBox1.Items.Add("test"); //working correctly
}
}
Problem is when i trying to add item into the list it does nothing, and further lines of code (in this function) don't run at all.
I tried to fix it thru some custom classes and different expressions but nothing worked for me (yes, I'm a noob). Also tried to do it with textBox, result is the same, but text on buttons changes as it should.
Looks like completely lame problem, but i've spent almost 5 hours by googling, reading microsoft documentation, SO, and closest i can get is this as i can see stuff suggested there already implemented.

The OnNewTextFound event is firing on a separate thread from the UI, so your attempt to update the UI is failing. An exception is thrown in the other thread, aborting the rest of that method, but your UI thread keeps executing.
You'll have to call Invoke() in order to execute the code back on the UI thread:
listBox1.Invoke(new MethodInvoker(delegate { listBox1.Items.Add(eventArg); }));

You are adding EventArgs to the Items list for the ListBox. Is there an eventArgs.[someString] that you can add?

You cannot add items to a listbox during the construction of the form. You need to move the code into the Load event.

Related

Accessing WinForms Controls from a serial port in a class

First, the apology: I'm new to posting questions on this site, so I apologize for formatting or information errors.I have seen many answers to taking data from a serial port dropped on a form and using it to populate text boxes, graphs, etc. on the main form, using "Invoke" because the serial port is running in a different thread.
I am trying to "generalize" some comm stuff we use all the time in to a class (yes, the old VB6 programmer is trying to grow up :-) and I'm having issues. I can do some things if I force a form name in the main program.cs and use the same namespace for the class, but this sorta defeats the purpose. I've also tried adding an event on the "received" even of the serial port in the class to raise an event on the main form. The event tries to get raised but a cross thread exception occurs.
The code at this point is quite large, so I'll try to "outline" it. In simplistic form, assuming I have a for called "Form1" which contains a text box called textbox1 and a class called "SerialThing":
Form1:
SerialThing mySerialThing ;
Form1_Load:
mySerialThing = new SerialThing();
DisplayData()
Textbox1.Text = "You Got Data!";
SerialThing:
Static SerialPort myDevice;
Init()
myDevice = new SerialPort;
myDevice.DataReceived += new SerialDataReceivedEventHandler(devicePort_DataReceived);
devicePort_DataReceived()
this.Invoke(new EventHandler(DisplayData));
The above will work if the serial port is placed on the main form, but not if created inside the class.
Again, sorry if too complex, or too simplistic. I am looking for an "easy" way to do this, but keep the class "generalized" (ideally not have to have the workspace names match, etc).
-Vin
There are many, many ways to do this. I'll present the classic approach using a custom event, delegates, and Invoke(), as I think it's important to understand that process. Once you've got this down, you can jump to some of the newer approaches.
First, in your SerialThing() class, you declare a Custom event to pass out data when it is received:
class SerialThing
{
public delegate void DataReceivedDelegate(string data);
public event DataReceivedDelegate DataReceived;
static SerialPort myDevice;
public SerialThing()
{
myDevice = new SerialPort();
myDevice.DataReceived += new SerialDataReceivedEventHandler(myDevice_DataReceived);
}
void myDevice_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// ... grab the data and place into a string called "data" ...
string data = "";
// raise our custom event:
if (DataReceived != null)
{
DataReceived(data);
}
}
}
Now, over in Form1, you subscribe to that custom event when you create the instance of SerialThing. Additionally, when that event is received, you marshal the call from the secondary thread to the main thread using InvokeRequired, Invoke, and a delegate:
public partial class Form1 : Form
{
SerialThing mySerialThing;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
mySerialThing = new SerialThing();
mySerialThing.DataReceived += new SerialThing.DataReceivedDelegate(mySerialThing_DataReceived);
}
private delegate void DataReceivedDelegate(string data);
void mySerialThing_DataReceived(string data)
{
if (this.InvokeRequired)
{
this.Invoke(new DataReceivedDelegate(mySerialThing_DataReceived), new Object[] { data });
}
else
{
textBox1.Text = data;
}
}
}
EDIT: In response to your comments below...
Think of a delegate as simply a "pointer to a method". When you execute the delegate, the associated method gets run.
The InvokeRequired() portion determines if the code is running in a different thread than the one that created the control. In this case, the control is the Form itself (this). If true is returned, then the event was received in a different thread. We then proceed to this.Invoke() line inside the true portion of the If block. Again this refers to the Form. Thus the Form is requesting to Invoke ("run") the passed delegate on the thread that created it (the main UI thread). We create a instance of the delegate that actually points to the same method we are already in resulting in a recursive call. The second parameter is simply an array of Object used to pass the parameters along with the delegate.
When Invoke() is run we end up re-entering the method because of the recursive call. At this point, however, the InvokeRequired() check will return false as we are now running in the main UI thread. Therefore we drop down into the false portion of the If statement where we update the TextBox. In this pattern it is safe to update an GUI controls in the else block of the If statement.
Note that a recursive call isn't necessary here. This is simply a style choice. We could have instead used a second "helper" function that the delegate pointed to, and Invoked that instead. The recursive approach reduces the number of methods required.
This is perhaps the most verbose approach to solving this type of problem. I like it, though, as it shows the flow of events and data, and movement between the threads.
We could shorten all the Form code to just this, using anonymous delegates:
private void Form1_Load(object sender, EventArgs e)
{
mySerialThing = new SerialThing();
mySerialThing.DataReceived += delegate (string data)
{
this.Invoke((MethodInvoker)(delegate() { textBox1.Text = data; }));
};
}
I don't know about you, but as a former VB6 programmer myself, that just looks weird when you first see that type of thing.
I've also used components that I know have things running in different
threads, yet the "form code" has never had to use the delegate stuff,
so maybe there's something that can be buried into the class?
Yes, it's possible to bake some "magic" into a class so that it raises events already on the main UI thread, thus not requiring any Invoke() calls. One way to do this is thru using a SynchronizationContext.
Another possibility for approaching this type of problem would be to use a BackgroundWorker() control which has events such as ProgressChanged() and RunWorkerCompleted() that are raised in the main UI thread for you (they do the necessary invoking type stuff under the hood for you).

WPF threads and tasks on textchanged event

Using Visual Studio 2012 ulti, C# .NET WPF.
Using Tasks in my code on winforms used to be simple.
All I would do is create a delegate, create a function for my code, create a task and the event would be a simple button. Easy stuff. Problem I have is the following...
Create a thread as per-usual But the event will be on text changed.
The problem im having is thinking about the logic, if I simply change the event I cant see this working as the user could type faster than the code could run ( in this case an sql query select statement). There for it would try to run many tasks which I don't even think would work.
Basically User enters text box that used for searching an account by name or number.
In this textbox I would like to thread the entire process.
The only solution I can think of is as the text changes if there is a thread still running stop that thread and create the new one, but not sure if thats a clean way of doing it as its a sql stored procedure ill be calling.
So any body got a solution to this?
If you need any more info just ask. Ill also provide some code that currently works to give you an understanding if needed...
Set Invoke method up:
private void SetDataGrid(bool AutoGenerateColumns, Object DataSource, String DataMember, DataGridViewAutoSizeColumnsMode Mode)
{
if (this.ParetoGrid.InvokeRequired)
{
this.ParetoGrid.Invoke(new Action<bool, Object, String, DataGridViewAutoSizeColumnsMode>(SetDataGrid),
AutoGenerateColumns, DataSource, DataMember, Mode);
}
else
{
this.ParetoGrid.AutoGenerateColumns = AutoGenerateColumns;
this.ParetoGrid.DataSource = DataSource;
this.ParetoGrid.DataMember = DataMember;
ParetoGrid.AutoResizeColumns(Mode);
}
}
Call invoke method in another method:
Private void GetSomething()
{
//sql code get data
SetDataGrid(true, dataSet1, "Pareto", DataGridViewAutoSizeColumnsMode.AllCells);
}
Then simply start task on event:
private void myButton_Click(Object sender, EventArgs e)
{
Task t = new Task(() => getSomething());
t.Start();
}
As you can see simple stuff, but simple changing event seems to mess the whole logic up.
I'd recomend moving this logic from the task into a Timer callback, then have your OnTextChanged handler actually reset the timer each time it's fired (only have the timer fire once of course). By making the timer elapse after .5-1 sec, or something like that, you'll wait until all of their text has been entered before actually calling your logic. But the user's experience will still be quite responsive.
Example:
private System.Threading.Timer keyEntryTimer = new Timer(Logic,null,-1,-1);
public void HandleEvent(objet sender, EventArgs args)
{
keyEntryTimer.Change(500,-1);
}
public void Logic(objet state)
{
//Your task logic would go here to read from the text etc...
//You'll have to handle any UI updates either by firing off a task once the DB results return or using a dispatcher
}
You can simply cache all accounts when they enter this search mode. Then you can search through cached accounts when they enter text into the textbox. Doing a query in a different thread for every keypress is very heavy on the app.
I would probably do a combination of the other suggested answers and try to delay the firing of the SQL statement using a timer or some similar method, but if the user delayed long enough for the SQL to fire then try to just filter the returned results from that point forward (only if the search string is becoming more restrictive obviously). This could save you expensive SQL round trips and give you a working cache after the first hit (which should be smaller than if you tried to cache everything).
Hope this helps.

Cross-thread: Invoke before the dialog shown

Probably I don't understand correctly this topic. Here is an issue...
C# Windows Application (.NET 2.0). The MainForm has a button "Query". When the user pushes it, the following should happen:
private void btnQuery_Click(object sender, EventArgs e)
{
querier = new Querier();
OutputForm outputForm = new OutputForm();
querier.ProcessAll(outputForm.OutputReceived);
outputForm.ShowDialog();
}
Querier is the worker. It creates a background thread and runs it to do stuff. The OutputForm is a simple form with txtOutput multiline text-box that should display the output of the working thread.
To allow the working thread send its output, querier.ProcessAll() method receives a callback handler. This is its implementation:
public void OutputReceived(string message)
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate() { this.OutputReceived(message); });
else if (!string.IsNullOrEmpty(message))
txtOutput.AppendText(message + Environment.NewLine);
}
So basically the working thread runs and sends output using the OutputReceived() method, which uses Invoke(), because the working thread can't access the txtOutput field directly.
Note that outputForm.ShowDialog() is called AFTER querier.ProcessAll(). That's because ShowDialog() is blocking.
But here is the problem. If the working thread sends any output BEFORE the dialog is actually shown, I get the exception about cross-thread operation! When I debug it, I see that for some reason this.InvokeRequired() in the OutputReceived() method returns "false"! That's why the working thread tries to access txtOutput directly and crashes.
The problem is clearly about the race condition between the thread and ShowDialog(). If I add Thread.Sleep() in the beginning of the working thread, the dialog shows up and then everything works fine.
Can you explain such behavior?
The best thing to do here would be to ensure that the long running task doesn't actually start until the other form is first displayed. This isn't all that hard thanks to the Shown event in Form.
private void btnQuery_Click(object sender, EventArgs e)
{
querier = new Querier();
OutputForm outputForm = new OutputForm();
outputForm.Shown += delegate { querier.ProcessAll(outputForm.OutputReceived); };
outputForm.ShowDialog();
}
Sorry, I found the answer!
There is one special case when InvokeRequired() will return "false". It's when the control's handle hasn't been created yet. In this case it is forbidden to call Invoke() - so the InvokeRequired() tries to protect you, sort of.
Now I call CreateHandle() method in the CTOR of OutputForm. In this way the handle is created even before the dialog is shown, so InvokeRequired() works as expected.

Windows Forms GUI hangs when calling OpenFileDialog.ShowDialog()

my project a three tier architecture project talking to a WCF service in the backend. When the backend is able to fetch data from the service, it notifies the business layer using publish-subscribe, which in return notifies the GUI layer.
I have added an OpenFileDialog to my UI design using Visual Studios designer. A button event handler calls the ShowDialog message. However, once I click the button, the whole UI hangs.
Having googled around a bit, I found out that using delegates is the preferred way to handle tasks like this. However, with nor without delegate the problem persists.
Currently my code looks like this:
private void bOpen_Click(object sender, EventArgs e)
{
Func<Image> del = delegate
{
OpenFileDialog d = new OpenFileDialog();
if (d.ShowDialog() == DialogResult.OK)
{
return Image.FromFile(d.FileName);
}
return null;
};
Invoke(del);
}
I'm coming from the Java world, so I'm not really familiar with the intricacies of C# UI programming.
Anything I'm missing here?
openFileDialog1->ShowHelp = true;
I put this line in my code then the problem was solved.
I seem to have solved the problem adding the [STAThread] Attribute to the main method. I was told to do so once I ran the program in a debugger - which I hadn't done before because I ran the service from Visual Studio and the client regularly from Windows.
[STAThread]
public static void Main(string[] args)
{
GUI gui = new GUI();
gui.ShowDialog();
}
Can anybody explain what exactly is going on though
This tends to be an environmental problem, when you use OpenFileDialog a lot of shell extensions get loaded into your process. A misbehaving one can easily screw up your program. There are a lot of bad ones out there.
Debugging this is difficult, you need an unmanaged debugger since these shell extensions are unmanaged code. You might be able to tell something from the call stack when you break in after the deadlock. Windows debugging symbols required, enable the Microsoft symbol server. But the most effective approach is to use SysInternals' AutoRuns utility. Start by disabling all of the shell extensions that were not produced by Microsoft. Then start re-enabling the ones you cannot live without one by one.
And, as you found out, these shell extension expect to run on an STA thread and fail miserably when they don't get it. The UI thread of a program must always be STA, also to support the clipboard and drag-and-drop and various kinds of controls like WebBrowser. Normally always taken care of automatically by the [STAThread] attribute on the Main() method, put there by the project template. And the Application.Run() call, required to implement the STA contract. Deadlock when you don't.
I believe the "delegate" prefered way actually refers to using a separate thread.
I'm gonna give you an example using BackgroundWorker.
It would look like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
m_Worker.DoWork += new DoWorkEventHandler(m_Worker_DoWork);
m_Worker.ProgressChanged += new ProgressChangedEventHandler(m_Worker_ProgressChanged);
m_Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_Worker_RunWorkerCompleted);
}
void m_Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Usually, used to update a progress bar
}
void m_Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Usually, used to add some code to notify the user that the job is done.
}
void m_Worker_DoWork(object sender, DoWorkEventArgs e)
{
//e.Argument.ToString() contains the path to the file
//Do what you want with the file returned.
}
private void bOpen_Click(object sender, EventArgs e)
{
OpenFileDialog d = new OpenFileDialog();
if (d.ShowDialog() == DialogResult.OK)
{
m_Worker.RunWorkerAsync(d.FileName);
}
}
BackgroundWorker m_Worker = new BackgroundWorker();
}
Now, as for the reason your UI "hangs", it's because by default, your operation runs on the UI thread, so if you run something heavy the UI won't respond.
I also met this problem. And I tried all the solution here and none can solve it. Then I change the target framework from .Net Framework 4.7 to 4.6.2, the problem solved...
I think my problem is different, as none of the above solutions worked for me.
I wrote temporary code to set the OpenFileDialog.FileName property to something not null or empty string (it was empty string when the hang up occured), and I restarted my computer. When I started up Visual Studio again, and ran it, it worked again without hanging up.

Label text not updated

I have a Windows Form with a status bar which shows the current state of application.
I have a class named AppState with update the Label in the status bar and in dispose it changes the state back to "Ready".
In code when I do an operation like:
using (AppState state = new AppState("Processing..."))
{
//Do some work that take some seconds
}
But the label remains the same. I am not getting any exceptions. The label text is updated but on UI it keeps on showing previous value. Am I missing anything here?
santosc you are right, thats the only thing I am doing. Here is the AppState code
public class AppState : IDisposable
{
static string Default = "Ready";
public AppState(string status)
{
Form.StatusLabel.Text = status;
}
public void Dispose()
{
Form.StatusLabel.Text = Default;
}
}
It's always the same thing...
If you want to start something that takes a while, don't do it within your GUI thread or your GUI will freeze (no updates of label, no resizing, no moving, no whatever).
Filling your code on thousand places with Application.DoEvents() is also a bad practice.
If you have some long running task (long means > 1 sec) you should probably use a BackgroundWorker. Maybe it's a little bit harder at the beginning, but you will love it if your program gets more complex. Due to the fact, that this has already being discussed several time, here is a link with some sample code.
Now that you know the right tool (BackgroundWorker) to solve your problem, you should get it to work (or ask another question about your new specific problem).
Looks like you want to put Application.DoEvents() after setting the StatusLabel text field value. This tells Windows Forms to process the Windows event queue for your form, causing changes to be repainted.
in order to be "thread safe" use Invoke, and test with the InvokeRequired in the form like:
// code outside the myForm:-----------------------
if (myForm.InvokeRequired)
myForm.Invoke(new ChangeLabelEventHandler(ChangeLabel), "teeeest");
else
myForm.ChangeLabel("teeeest");
// code in the myForm:-----------------------------
public delegate void ChangeLabelEventHandler(string newText);
private void ChangeLabel(string newLabelText)
{
this.label1.Text = newLabelText;
}
I'm new to C# stuff, but why can't you just do something like:
private void updateStatusBar(string status)
{
if (StatusLabel.InvokeRequired)
{
StatusLabel.Invoke((MethodInvoker)(() =>
{
StatusLabel.Text = status;
}));
}
else
{
StatusLabel.Text = status;
}
}
When you want to update the status?
Maybe multiple threads could solve your problem.
The easiest way is using a BackgroundWorker.
The reason is that the UI is only able to redraw when the UI thread has nothing else to do. And you are blocking it with your calculation.
use Label.Refresh(); it saves a lot of time.This should work for u

Categories

Resources