i´m a beginner in C#,
a start a new Thread
pollprintthread = new System.Threading.Thread(PollPrint);
pollprintthread.Start();
in this Thread i call a function with a Datagridview
void PollPrint()
{
(some code)
printausfueren();
(some code)
}
public void printausfueren()
{
(some Code)
object[] row = { sqlReader[0], sqlReader[1], sqlReader[3], sqlReader[2], sqlReader[4], sqlReader[5], sqlReader[6], sqlReader[7] };
dataGridViewPrint.Rows.Add(row);
(some Code)
}
but i can´t use printausfuheren().invoke, i found any tutorials but only for Static Methods
Please Help :)
You can find a good explanation here: https://stackoverflow.com/a/12179408/67038
However, I would recommend that you wrap your call to dataGridViewPrint.Rows.Add in a method and call that method instead.
You will want to do this because there may be other actions that you wish to take on the UI when you add those rows.
For example, if you are adding a lot of rows, you are going to want to call BeginEdit/EndEdit on the grid, so that it does not try to repaint itself for every row that you add:
public void printausfueren()
{
//(some Code)
object[] rows = { sqlReader[0], sqlReader[1], sqlReader[3], sqlReader[2], sqlReader[4], sqlReader[5], sqlReader[6], sqlReader[7] };
dataGridViewPrint.Invoke(new Action(() => LoadGrid(rows)));
//(some Code)
}
private void LoadGrid(object[] rows)
{
// only need BeginEdit/EndEdit if you are adding many rows at a time
dataGridViewPrint.BeginEdit(false);
dataGridViewPrint.Rows.Add(rows);
dataGridViewPrint.EndEdit();
}
You can easily use anonymous methods to do complex invokes, such as:
yourForm.Invoke( (MethodInvoker) delegate() { dataGridViewPrint.Rows.Add(row); } );
This will auto-magically capture the row instance etc.
Related
I'm filling a list with a function but I want to display how many elements are in list dynamically.
With my code, I have a NullReferenceException error.
My code is :
private async void FillListWithElements()
{
var list = await Task.Run(() => Functions.GetElements(param));
DefineCurrentAction($"Element(s) in list : {list.Count}");
Task.WaitAll();
[...]
}
Functions.GetElements is a static sync function in a library. (Pretty sure I should change something here)
DefineCurrentAction display text in a label to inform the user.
How can I display how much elements are in my list while the function GetElements is performing ?
A better way to couch this question would be to ask
"How can I get GetElements to report progress back to the caller?"
As such, say you changed the declaration of GetElements to
List<Something> GetElements(Some params, Action<int> progress)
{
//...
for(;;)
{
//do something
progress?.Invoke(theCount)
}
}
Now, when you call GetElements, you can supply a function (delegate) to receive the progress:
GetElements(params, count => Console.WriteLine(count))
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 a datagridview with some data calculated in a different class and thread, using a delegate. Unfortunately I'm having trouble with a variety of different errors, depending on the approach I try.
The code I am trying to execute in the form thread looks like this:
public partial class AcquireForm : Form
//
// ...
//
// update the grid with results
public delegate void delUpdateResultsGrid(int Index, Dictionary<string, double> scoreCard);
public void UpdateResultsGrid(int Index, Dictionary<string, double> scoreCard)
{
if (!this.InvokeRequired)
{
//
// Some code to sort the data from scoreCard goes here
//
DataGridViewRow myRow = dataGridViewResults.Rows[Index];
DataGridViewCell myCell = myRow.Cells[1];
myCell.Value = 1; // placeholder - the updated value goes here
}
}
else
{
this.BeginInvoke(new delUpdateResultsGrid(UpdateResultsGrid), new object[] { Index, scoreCard});
}
}
Now, I need to get this method to run from my other thread and class. I have tried:
public class myOtherClass
//
// ...
//
private void myOtherClassMethod(int myIndex)
{
// ...
AcquireForm.delUpdateResultsGrid updatedelegate = new AcquireForm.delUpdateResultsGrid(AcquireForm.UpdateResultsGrid);
updatedelegate(myIndex, myScoreCard);
}
Unfortunately this gives an "Object reference is required for the non-static field, method, or property AcquireForm.UpdateResultsGrid(int, System.Collections.Generic.Dictionary)" error. I seem to be unable to reference the UpdateResultsGrid method at all...
I have noticed that
public class myOtherClass
//
// ...
//
private void myOtherClassMethod(int myIndex)
{
// ...
AcquireForm acquireForm = new AcquireForm();
acquireForm.UpdateResultsGrid(myIndex,myScoreCard);
}
does not throw any errors when compiling, but it tries to create a new form and that is something I do not want to do. I don't want to create a new instance of AcquireForm, I want to reference the pre-existing one, if that's possible.
I have also tried making the UpdateResultsGrid method static, but this throws up problems with several things incuding the use of "this.(anything)".
I've also tried moving the majority of the UpdateResultsGrid method into myOtherClassMethod, leaving behind in the AcquireForm class just the delegate. Again, this does not work because many of the references to UI objects break (there aren't any dataGridViews in scope).
I'm starting to run out of ideas here. Unfortunately I'm rather new to C# (as you can probably tell), and I'm editing someone else's code rather than writing my own entirely from scratch. If anyone could offer some advice on this problem it'd be most appreciated.
Make sure your objects are communicating with each other: Your myOtherClass is going to have to know about the AcquireForm object - you can't just create a new one (as you've discovered). You'll need to pass the AcquireForm object into the myOtherClass object (myOtherObject.SetForm(myAcquireForm, for example) and reference it when you need to.
In case you're having issues with invoking this might be of help - how I invoke a "next" button click:
BeginInvoke(new Action(()=>button_next_Click(null,null)));
Moreover, it sounds like maybe this should not be separate classes and you should be utilising a BackgroundWorkder instead.
So, the documentation that I've found online so far regarding the Invoke property doesn't seem to be particularly good, which is actually kind of annoying, believe it or not. I understand what Invoke does - you can't safely access the GUI directly, so an invoke does it in a safe way. That's fine, but I don't understand the variables that go into the method. If I wanted to, for instance, remove text from a listbox, how would I do that? I get about this far before I get a bit lost.
private void DoStuff(string TextIWouldBeRemoving)
{
if (listboxname.InvokeRequired)
{
listboxname.Invoke(SomeMysteriousParamaters, new object[] { TextIWouldBeRemoving )};
}
}
The first parameter is the method you want to safely invoke, the second parameter is an object array of the arguments to that method
So you would write:
private void DoStuff(string TextIWouldBeRemoving)
{
if (listboxname.InvokeRequired)
{
listboxname.Invoke(DoStuff, new object[] { TextIWouldBeRemoving )};
}
else
{
// Actually remove the text here!
}
}
Invoke is all about threading.
You need to do an invoke whenever you have created a separate thread in your code, and you need to update the User Interface elements from withing the code, that is executing in that newly create thread.
You can use a BeginInvoke, instead of a synchronous Invoke method. This article has a good example:
http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx
private void button1_Click(object sender, EventArgs e)
{
if (listBox1.InvokeRequired)
{
Action<string> d = DoAnything;
listBox1.Invoke(d, new object[] { "Item 1" });
}
else
DoAnything("Item 1");
}
void DoAnything(string itemText)
{
listBox1.Items.Remove(itemText);
}
I've got a C# program that talks to an instrument (spectrum analyzer) over a network. I need to be able to change a large number of parameters in the instrument and read them back into my program. I want to use backgroundworker to do the actual talking to the instrument so that UI performance doesn't suffer.
The way this works is - 1) send command to the instrument with new parameter value, 2) read parameter back from the instrument so I can see what actually happened (for example, I try to set the center frequency above the max that the instrument will handle and it tells me what it will actually handle), and 3) update a program variable with the actual value received from the instrument.
Because there are quite a few parameters to be updated I'd like to use a generic routine. The part I can't seem to get my brain around is updating the variable in my code with what comes back from the instrument via backgroundworker. If I used a separate RunWorkerCompleted event for each parameter I could hardwire the update directly to the variable. I'd like to come up with a way of using a single routine that's capable of updating any of the variables. All I can come up with is passing a reference number (different for each parameter) and using a switch statement in the RunWorkerCompleted handler to direct the result. There has to be a better way.
I think what I would do is pass a list of parameters, values, and delegates to the BackgroundWorker. That way you can write the assign-back code "synchronously" but have execution deferred until the values are actually retrieved.
Start with a "request" class that looks something like this:
class ParameterUpdate
{
public ParameterUpdate(string name, string value, Action<string> callback)
{
this.Name = name;
this.Value = value;
this.Callback = callback;
}
public string Name { get; private set; }
public string Value { get; set; }
public Action<string> Callback { get; private set; }
}
Then write your async code to use this:
private void bwUpdateParameters_DoWork(object sender, DoWorkEventArgs e)
{
var updates = (IEnumerable<ParameterUpdate>)e.Argument;
foreach (var update in updates)
{
WriteDeviceParameter(update.Name, update.Value);
update.Value = ReadDeviceParameter(update.Name);
}
e.Result = updates;
}
private void bwUpdateParameters_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
var updates = (IEnumerable<ParameterUpdate>)e.Argument;
foreach (var update in updates)
{
if (update.Callback != null)
{
update.Callback(update.Value);
}
}
}
Here's how you would kick off the update. Let's say you've got a bunch of member fields that you want to update with the actual values of the parameters that were used:
// Members of the Form/Control class
private string bandwidth;
private string inputAttenuation;
private string averaging;
// Later on, in your "update" method
var updates = new List<ParameterUpdate>
{
new ParameterUpdate("Bandwidth", "3000", v => bandwidth = v),
new ParameterUpdate("InputAttenuation", "10", v => inputAttenuation = v),
new ParameterUpdate("Averaging", "Logarithmic", v => averaging = v)
};
bwUpdateParameters.RunWorkerAsync(updates);
That's all you have to do. All of the actual work is done in the background, but you're writing simple variable-assignment statements as if they were in the foreground. The code is short, simple, and completely thread-safe because the actual assignments are executed in the RunWorkerCompleted event.
If you need to do more than this, such as update controls in addition to variables, it's very simple, you can put anything you want for the callback, i.e.:
new ParameterUpdate("Bandwidth", "3000", v =>
{
bandwidth = v;
txtBandwidth.Text = v;
})
Again, this will work because it's not actually getting executed until the work is completed.
[Edit - look back at update history to see previous answer. Talk about not being able to see the wood for the trees]
Is there any reason that, rather than passing a reference number to the Background Worker, you can't pass the ID of the label that should be updated with any value passed back?
So the UI adds an item in the work queue containing:
Variable to change
Attempted change
UI ID
and the BackgroundWorker triggers an event with EventArgs containing
Attempted change
Actual value after attempt
UI ID
Error Message (null if successful)
which is all the information you need to update your UI without a switch or multiple event args and without your Background Worker ever being aware of UI detail.
How about something like this?
[TestFixture]
public class BGWorkerTest
{
string output1;
string output2;
[Test]
public void DoTest()
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, args) =>
{
output1 = DoThing1();
output2 = DoThing2();
};
backgroundWorker.RunWorkerAsync();
//Wait for BG to finish
Thread.Sleep(3000);
Assert.AreEqual("Thing1",output1);
Assert.AreEqual("Thing2",output2);
}
public string DoThing1()
{
Thread.Sleep(1000);
return "Thing1";
}
public string DoThing2()
{
Thread.Sleep(1000);
return "Thing2";
}
}