I need to add an element to a Winform ListView control from other thread, so I am using a delegate, this way:
private delegate void AddMessageLogCallback(string message);
public void AddMessageLog(string message)
{
if (InvokeRequired)
Invoke(new AddMessageLogCallback(AddMessageLog), message);
else
{
lstLogs.Items.Add(message).EnsureVisible();
}
}
The problem is that the Invoke does nothing, not even throws an exception.
I have used this kind of delegates before and never had problems. What different is at this time?
Your code works as desired with the test code below, so the problem should be something else.
private void button1_Click(object sender, EventArgs e)
{
AddMessageLog("local message");
}
private async void button2_Click(object sender, EventArgs e)
{
await Task.Run(() => AddMessageLog("async message"));
}
Btw, I would mention that there is no need to define a new AddMessageLogCallback delegate and to call the AddMessageLog recursively. So a more simple (and maybe cleaner) solution:
public void AddMessageLog(string message)
{
Action addLog = () => lstLogs.Items.Add(message).EnsureVisible();
if (InvokeRequired)
Invoke(addLog);
else
addLog();
}
Related
so I am making a small WPF app.
I am new to C# and Multithreading, I want to run certain methods in sequence but because one of the methods is Async it does not run in sequence.
private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
if (!OpenFile()) return; // opens a file dialog and ensure format is correct
await Task.Run(() =>
{
// some heavy task which I run here so that I dont freeze the UI
});
}
private void TheFunctionIwantToRunInSeqeuence(object sender, RoutedEventArgs e)
{
LoadButton_Click(sender, e);
SaveCareerInfoButton_Click(sender, e); // I want this line to wait for load to finish
LoadButton_Click(sender, e);
ImportCareerInfoButton_Click(sender, e); // I want this line to wait for the second load to finish
}
Await these calls as well, refactor your code a bit.. extract handler's content to a separate method and don't pass senders and args between handlers
private Task Load()
{
if (!OpenFile()) return;
return Task.Run(() =>
{
// some heavy task which I run here so that I dont freeze the UI
});
}
private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
await Load();
}
private async void TheFunctionIwantToRunInSeqeuence(object sender, RoutedEventArgs e)
{
await Load();
// refactor your code to not pass sender, e.. SaveCareer();
SaveCareerInfoButton_Click(sender, e);
await Load();
// refactor your code to not pass sender, e.. ImportCareer();
ImportCareerInfoButton_Click(sender, e);
}
You can use await to wait an async function to finish and make function TheFunctionIwantToRunInSeqeuence to async Task return type:
private async Task TheFunctionIwantToRunInSeqeuence(object sender, RoutedEventArgs e)
{
await LoadButton_Click(sender, e);
SaveCareerInfoButton_Click(sender, e); // I want this line to wait for load to finish
await LoadButton_Click(sender, e);
ImportCareerInfoButton_Click(sender, e); // I want this line to wait for the second load to finish
}
I want to run a method inside serialport_DataReceived event.
public void Draw(byte[] data);
private void myPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
this.Invoke(new EventHandler(DrawingAudioData(data)));
}
This is not work. It gives an error that say "Method name expected". What can i do?
Try
public delegate void Draw(byte[] data);
private void myPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
this.Invoke(new Draw(DrawingAudioData), data);
}
Seems to me that the DrawingAudioData passed to Invoke does not have EventHandler signature. Also you should pass the method Name to the delegate constructor.
The DrawingAudioData method should have the signature that matches the Draw delegate:
public void DrawingAudioData(byte[] data) {
More information about Event Handler here.
More information about the Delegate and Invoke method here.
I am trying to figure out how to make it that when my timer ticks, it performs a bidder00_TextChanged, or something like that.
Is this even possible to do? and if it isn't, is there any other way to do it?
I tried to search Google for it but i didn't get any results, if you find anything that i missed please post it here.
I don't really have any code but here it is:
private void bidder00_TextChanged(object sender, EventArgs e)
{
if (bidder00.Text == addbidder1.Text)
{
bidBtn1.PerformClick();
}
}
That is my TextChanged Event
My timer doesn't have any code because it is going to perform the bidder00_TextChanged Event.
You could create a method Perform() and call it from within your event handlers :
private void timer1_Tick(object sender, EventArgs e)
{
Perform();
}
private void bidder00_TextChanged(object sender, EventArgs e)
{
Perform();
}
private void Perform()
{
if (bidder00.Text == addbidder1.Text)
{
bidBtn1.PerformClick();
}
}
I assume you have coupled your actual logic with your click event which is not a good idea. Separate the code out into a separate function and have both parts of the application call the same code e.g.
private void SubmitBid()
{
// code you want to execute
}
private void OnSubmitBid()
{
// confirm whether we can actually submit the bid
if (bidder00.Text == addbidder1.Text)
{
SubmitBid();
}
}
private void Timer1_OnTick(object sender, EventArgs e)
{
// trigger code from timer
OnSubmitBid();
}
private void bidder00_TextChanged(object sender, EventArgs e)
{
// trigger code from text change
OnSubmitBid();
}
private void btnBid_Click(object sender, EventArgs e)
{
// trigger code from button press
OnSubmitBid();
}
Notice all the UI controls trigger the same code. There is an extra call in there for the text control validation (i.e. OnSubmitBid()) - if this wasn't required then you would just call SubmitBid directly.
I'm just creating a ContextMenu..
At this line, I don't know what I shall put in the third param (or better: how I have to form it -syntaxly-):
(contextMenuStrip.Items[0] as System.Windows.Forms.ToolStripMenuItem).DropDownItems.Add(contextUnterMenuStrip.Items.Add(exe),null, HERE);
on 'HERE' I have to set an EventHandler onClick
By Example I got this Method:
public void DoSomething()
{
//...
}
How could I call this Method? (Over the Eventhandler?) or do I have to make a Method like:
private void button_Click(object sender, RoutedEventArgs e)
{
//...
}
Don't "call" the method but take its address. Which means omitting the ()
private void menuItem1_Click(object sender, EventArgs e)
{
//...
}
// your code, I think it misses a few ')'
... (contextMenuStrip.Items[0] as System.Windows.Forms.ToolStripMenuItem)
.DropDownItems.Add(contextUnterMenuStrip.Items
.Add(exe),null, menuItem1_Click);
As you can see here, the callback has to have the following prototype:
public delegate void EventHandler( Object sender, EventArgs e )
So your method DoSomething has to look like:
private void DoSomething(object sender, EventArgs e)
{
//...
}
You can create an anonymous event handler using the Linq libraries and call your method that way. This can be a nice and quick way of doing something (especially if it's just a test project). But if you start using it extensively, it might become difficult to read it.
An example of this would be:
var menuItem1 = new MenuItem();
menuItem1.Click += (sender, e) => DoSomething();
Refer here for further information on using Linq: http://msdn.microsoft.com/library/bb308959.aspx
I have a DirectoryMonitor class which works on another thread.
It has the following events declared:
public class DirectoryMonitor
{
public event EventHandler<MonitorEventArgs> CreatedNewBook;
public event EventHandler ScanStarted;
....
}
public class MonitorEventArgs : EventArgs
{
public Book Book { get; set; }
}
There is a form using that monitor, and upon receiving the events, it should update the display.
Now, this works:
void DirectoryMonitor_ScanStarted(object sender, EventArgs e)
{
if (InvokeRequired)
{
Invoke(new EventHandler(this.DirectoryMonitor_ScanStarted));
}
else {...}
}
But this throws TargetParameterCountException:
void DirectoryMonitor_CreatedNewBook(object sender, MonitorEventArgs e)
{
if (InvokeRequired)
{
Invoke(new EventHandler<MonitorEventArgs>(this.DirectoryMonitor_CreatedNewBook));
}
else {...}
}
What am I missing?
The Invoke method excepts to receive a System.Delegate instance which can be invoked without passing any additional parameters. The delegate created by using DirectoryMonitor_ScanStarted requires 2 parameters and hence you get the exception when it's used.
You need to create a new delegate which wraps the call and arguments together.
MethodInvoker del = () => this.DirectoryMonitor_ScanStarted(sender,e);
Invoke(del);
You're missing the parameters:-
void DirectoryMonitor_ScanStarted(object sender, MonitorEventArgs e)
{
if (InvokeRequired)
{
Invoke(new EventHandler<MonitorEventArgs>(DirectoryMonitor_ScanStarted), sender, e);
}
else {...}
}
For reasons not clear to me (probably due to COM legacy) it's permissible to omit parameters when using a generic event, but not when using a user defined EventArg type.