C# delegate method with return value - c#

I am making a C# program which is runnning 2 threads. The main UI thread, and a seperate network thread i make myself.
I have figured out, that if i need to change something in the UI from the network thread, i will need to call a deligate method, which works just fine:
// Declared as a global variable
public delegate void ListBoxFirst();
//Then call this from the network thread
listBox1.BeginInvoke(new ListBoxFirst(InitListBox));
//Which points to this
public void InitListBox() {
listBox1.SelectedIndex = 0;
}
Now i need to be able to read a UI value (a listbox.selectedindex) from the network thread, but it shows the error "cannot implicitly convert type 'system.iasyncresult' to 'int'" if i try it the same way (of course with "int" instead of "void", and a "int a = " before the listbox1.begininvoke). I have googled a lot but im pretty new to C# so i get really lost.
Any help would be much appriciated

I figured it out:
public int ReadListBoxIndex()
{
int count = 0;
listBox1.Invoke(new MethodInvoker(delegate
{
count = listBox1.SelectedIndex;
}));
return count;
}
Called with a regular
int count = ReadListBoxIndex();

You need to call EndInvoke() to get the result.
Some code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
listBox1.Items.AddRange(new[] { "AS","Ram"});
}
protected override void OnLoad(EventArgs e)
{
listBox1.BeginInvoke(new Action(GetResult));
}
private void GetResult()
{
if (InvokeRequired)
{
Invoke(new Action(GetResult));
}
listBox1.SelectedIndex = 0;
}
}

This works also, where tn is the treenode you want added to cross thread and tnret is the treenode that is returned.
_treeView.Invoke(new Action(() => tnret = tn.Nodes.Add( name, name )));

Related

passing value from another class to textbox in form?

I have a class and a form. the class is intended to do some processes when event is raised and return back the values to the form to display only. I kind of have problem passing values back to form. For instance, I have this code in class print:
public class PrintClass : Form1
{
public void printEventHandler(object sender, EventArgs e)
{
string text = "Process Completed";
append_Tbox(text);
}
}
and the method in form1 to display the text:
public void append_Tbox(string s)
{
TboxPrint.AppendText(s);
}
However, nothing is displayed. I believe there is something wrong, but I can't figure it out.
What is the fastest way to pass values from the class to form?
First off, your processing class shouldn't extend Form1. This is giving you the illusion that you can access the methods of your existing form, but it's not doing what you think it is. You're creating an entirely new form when you do this, and just not showing it. That form has it's own set of all instance fields, so you're not accessing the controls of your main form. Even if this would work (and it won't) it's not a well designed solution.
The proper way to do this is actually much easier. You just need to have your other class return a value from it's method:
public class PrintClass
{
public string DoWork()
{
Thread.Sleep(2000);//placeholder for real work.
return "Process Completed";
}
}
Now your main form can just call that method and append the return value to a textbox.
Once you do this you'll have an entirely separate issue. If you do the work in the UI thread you'll be blocking that UI thread while the work takes place, preventing the form from being repainted, or any other events from being handled. You need to do the work in a background thread and then marshal back to the UI thread to update the UI with the results. There are a number of ways of doing this, but if you have C# 5.0 using await is by far the easiest:
public class Form1 : Form
{
private void SomeEventHandler(object sender, EventArgs args)
{
string result = await Task.Run(()=>new PrintClass().DoWork());
TboxPrint.AppendText(result);
}
}
If you need a C# 4.0 solution you can use ContinueWith, which is more or less what the above will be translated to, but it's not quite as clean of syntax.
public class Form1 : Form
{
private void SomeEventHandler(object sender, EventArgs args)
{
Task.Factory.StartNew(()=>new PrintClass().DoWork())
.ContinueWith(t => TboxPrint.AppendText(t.Result)
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext());
}
}
I have create delegate in Main Form
public delegate void LoginDelegate(string s);
public partial class AirLineReservationMDI : Form
{
LoginDelegate loginAirLineDelegate;
}
loginAirLineDelegate = new LoginDelegate(DisableToolStripMenuItems);
public void DisableToolStripMenuItems(string s)
{
this.viewToolStripMenuItem.Visible = true;
this.bookingToolStripMenuItem.Visible = true;
this.existingUserToolStripMenuItem.Visible = false;
this.newUserToolStripMenuItem.Visible = false;
this.toolStripStatusUserID.Text = "USerID :- "+s;
this.LoginUserId = s;
}
in Another Class, (I have passed delagete object to this class )
I fired the Delegate
logDelegate(textBoxUserName.Text);
I used Action<T> delegate to solve the problem. here is the code and it works fine.
class PrintClass
{
public Action<string> DisplayDelegate;
public void printEventHandler(object sender, EventArgs e)
{
string text = "Event Handled, and text value is passed";
var copy = DisplayDelegate;
if (copy != null)
{
copy(text);
}
}
}
and in `Form1.cs' :
private void Form1_Load(object sender, EventArgs e)
{
PrintClass p = new PrintClass();
BtnPrint.Click += p.printEventHandler;
//subscrite the displayevent method to action delegate
p.DisplayDelegate += DisplayEvent;
}
public void DisplayEvent(string s)
{
Invoke(new Action(() => TboxPrint.AppendText(s)));
}
so the text 'Event Handled, and text value is passed' is displayed on the textbox.
I m not sure if it is the efficient way.
Thanks guys.

Async the content in textbox

I want to display the content asynchronously in textbox? Do anyone know the bug of my code listed below? I want to implement the text in textbox will be updated per second with new value? also i want to ask why checking the InvokeRequired each time before calling the invoke method for the controller?
private void Counting(int Num)
{
int i = 0;
string counter = null;
while (i < Num)
{
Thread.Sleep(1000);
counter = string.Format(" {0}", i);
tbxStatus.BeginInvoke(new UpdateStatusDelegate(UpdateStatus), new string[] { counter });
}
}
public void UpdateStatus(string data)
{
tbxStatus.Text += data;
}
public delegate void UpdateStatusDelegate(string data);
public delegate void CountDelegate(int num);
private void btnStart_Click(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new CountDelegate(Counting), new object[] { 5 });
}
else
Counting(5);
}
Thanks
Your InvokeRequired test should be inside the delegate:
public void UpdateStatus(string data)
{
if (this.tbxStatus.InvokeRequired)
{
UpdateStatusDelegate d = new UpdateStatusDelegate(UpdateStatus);
this.Invoke(d, new object[] { data });
}
else
{
this.tbxStatus.Text = data;
}
}
You can also make this a lot easier and cleaner looking if you use the following extension methods:
public static TResult SafeInvoke(this T isi, Func call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) {
IAsyncResult result = isi.BeginInvoke(call, new object[] { isi });
object endResult = isi.EndInvoke(result); return (TResult)endResult;
}
else
return call(isi);
}
public static void SafeInvoke(this T isi, Action call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) isi.BeginInvoke(call, new object[] { isi });
else
call(isi);
}
So if I want to call a method that I have defined in my UI thread (like a method defined in your form instance) you can do so with the following code (with no need to create any delegates or anything):
formInstance.SafeInvoke(f => f.myFormMethod("parameter1","parameter2"));
in your case, you could do the following:
formInstance.SafeInvoke(f => f.UpdateStatus(myCounterInt.toString));
or something like that.
I've written about using this on my blog but don't give me the credit as I was writing about this CodeProject article
I have recently come to a similar problem.
I am writing a networked game and I wanted to append text to a text box I have, however I would get an error saying I was trying to change a windows form control from a different thread, thus causing a not safe thread. This is because I am using ASyncCallback on my server side.
I had this previously: (The console method is a simple method that gets a string and appends it to the textbox).
console("message");
And to solve the problem I had to change it to this, solving the problem for now:
this.Invoke((MethodInvoker)delegate { console("message"); });
Hopefully this helps others that come across this problem. It probably isn't the best approach possible but it did the trick for me.
Regards.

C# cross-thread call problem

I'm writing a form app in c# and I need to be able to change the contents of a Rich Text Box from any thread, I tried using a delegate and InvokeRequired, but the delegate I made still gives me a cross-thread call error, and InvokeRequired crashes the form, without giving an error.
Function I need to be able to execute from any thread:
public static void updateSub(int what)
{
subDisplay.subBox.Text = tb[what];
}
The delegate I tried to use:
public delegate void UpdateDelegateVoid(int what);
static public UpdateDelegateVoid uSub = new UpdateDelegateVoid(updateSub);
uSub(0);
My InvokeRequired code:
public static void updateSub(int what)
{
if (subDisplay.subBox.InvokeRequired)
{
subDisplay.subBox.Invoke(new MethodInvoker(finish));
}
else
{
subDisplay.subBox.Text = tb[what];
}
}
I'm not really sure why the code above isn't working. Thanks!
Strictly speaking, when you check InvokeRequired and find it's true, you should marshall the call to the same method. I'm not sure it fixes your specific problem (I'd need to see more exception details and code) but this is what I mean:
public static void updateSub(int what)
{
if (subDisplay.subBox.InvokeRequired)
{
subDisplay.subBox.Invoke(new Action<int>(updateSub), what);
}
else
{
subDisplay.subBox.Text = tb[what];
}
}
If you're getting "weird behaviour", then check that the form is actually created on the main application thread. In WinForms this isn't forced (as it is in WPF) so it's just possible that the thread that the form was created on isn't actually the root thread of the app.
I mostly use this, and it works perfectly. For the exact same purpose are what you are intending.
public void UpdateSub(string message)
{
subDisplay.subBox.Invoke((Action)delegate {
subDisplay.subBox.Text = message;
});
}
Hope it help's your or someone else with it!
Try this - where you call the same method if an invoke is required.
public void UpdateSub(string message)
{
if (!subDisplay.subBox.InvokeRequired)
{
subDisplay.subBox.Text = message;
}
else
{
var d = new UpdateFormText(UpdateSub);
Invoke(d, new object[] { message });
}
}
Where UpdateFormText is the delegate
public delegate void UpdateFormText(string message);

WPF - Updating Label Content During Processing

Well I've tried several methods of getting this to work, background worker, Dispatcher.Invoke, threading within the called class and nothing seems, to work. The best solution so far is an Extension method which calls the invoke of the control. Also I've tried avoid passing the data for the label through my event classes and simply invoking within my processing code, however this made no difference.
In regards to the background component, I kept getting exceptions saying the background worker was busy, so I instantiated the class several times, however the label only visibly changed once the entire operation had been complete.
I've removed my previous code, here's everything that is relevant, as it seems the issue is difficult to resolve.
Method Being Called
private void TestUris()
{
string text = new TextRange(rtxturis.Document.ContentStart, rtxturis.Document.ContentEnd).Text;
string[] lines = Regex.Split(text.Remove(text.Length - 2), "\r\n");
foreach (string uri in lines)
{
SafeUpdateStatusText(uri);
bool result;
string modUri;
if (!uri.Contains("http://"))
{
modUri = uri;
result = StoreData.LinkUriExists(new Uri("http://" + modUri));
}
else
{
modUri = uri.Substring(7);
result = StoreData.LinkUriExists(new Uri(uri));
}
if (!result)
{
Yahoo yahoo = new Yahoo();
yahoo.Status.Sending += (StatusChange);
uint yahooResult = 0;
yahooResult = yahoo.ReturnLinkCount(modUri);
if (yahooResult > 1000 )
{ results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 1000, "Will be processed", true)); }
else
{ results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, (int)yahooResult, "Insufficient backlinks", false)); }
}
else
{
results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 0, "Previously been processed", false));
}
}
foreach (var record in results)
{
dgvresults.Items.Add(record);
}
EnableStartButton();
}
Yahoo Class
public class Yahoo
{
/// <summary>
/// Returns the amount of links each Uri has.
/// </summary>
public uint ReturnLinkCount(string uri)
{
string html;
Status.Update(uri, false); //this is where the status is called
try
{
html = client.DownloadString(string.Format("http://siteexplorer.search.yahoo.com/search?p=http%3A%2F%2F{0}&fr=sfp&bwm=i", uri));
}
catch (WebException ex)
{
ProcessError(ex.ToString());
return 0;
}
return (LinkNumber(html));
}
Status Classes
public class StatusEventArgs : EventArgs
{
private string _message;
private bool _isidle;
public StatusEventArgs(string message, bool isidle)
{
this._message = message;
this._isidle = isidle;
}
public bool IsIdle
{
get { return _isidle; }
}
public string Message
{
get { return _message; }
}
}
public class Status
{
public Status()
{
}
// Declaring an event, with a custom event arguments class
public event EventHandler<StatusEventArgs> Sending;
// Some method to fire the event.
public void Update(string message, bool isIdle)
{
StatusEventArgs msg = new StatusEventArgs(message, isIdle);
OnUpdate(msg);
}
// The method that invokes the event.
protected virtual void OnUpdate(StatusEventArgs e)
{
EventHandler<StatusEventArgs> handler = Sending;
if (handler != null)
{
handler(this, e);
}
}
}
Method That Changes the labels Content
private void StatusChange(object sender, StatusEventArgs e)
{
if(!e.IsIdle)
{
lblstatus.Content = e.Message;
lblstatus.Foreground = StatusColors.Green;
lblstatus.Refresh();
}
else
{
lblstatus.Content = e.Message;
lblstatus.Foreground = StatusColors.Grey;
lblstatus.Refresh();
}
}
The Refresh static method called:
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render , EmptyDelegate);
}
Another EDIT: Staring at my code for a bit longer, I've realised, that the foreach loop will execute really quickly, the operation which takes the time, is
yahooResult = yahoo.ReturnLinkCount(modUri);
Therefore I've declared the status class (which handles the event and invokes the label etc) and subscibed to it. I've gotten better results, although it still feels random, sometimes I see a couple of label updates, and sometimes one even though the exact same URI's are passed, so weird.
I hope there is sth. helpful...
private void button1_Click(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem(o =>
{
int result = 0;
for (int i = 0; i < 9999999; i++)
{
result++;
Dispatcher.BeginInvoke(new Action(() =>
{
this.label1.Content = result;
}));
Thread.Sleep(1);
}
});
}
SOLVED IT YES WOOHOOOOOOOO 3 days of testing, testing, testing.
I decided to start a new project just with the extension method above and a simply for loop to test UI update functionality. I started testing different DispatchPrioraties (tested them all).
Weirdly, I found the highest priorities were the worse, for example using Send didn't update the label at all, Render updated it twice on average. This was the weird behavior I was experiencing as I tried different priorities. I discovered Background:
The enumeration value is 4. Operations are processed after all other non-idle operations are completed.
Now this sounded exactly what I didn't want, as obviously the label should update during processing, hence why I never tried it. I'm guessing that once one of my method has been completed, before the next it called, the UI is updated. I'm find of guessing, but it 100% consistently updates correctly on two separate operations.
Thanks all.
Well this is going to sound stupid but you could just reference the forms namespace, and then you can do this
using System.Windows.Forms;
mylabel = "Start";
Application.doEvents();
myLabel = "update"
Application.doEvents();
now the problem using this would be you are using wpf but you can still reference forms and use this namespace. The other issue is what ever is in the que would execute directly to the ui. However this is the most simplistic way of doing label updates i could think of.
would it be easier/better to add the status info as a property on this object, and have it just fire property change notifications?
that way the label text (or whatever) could be bound to the property instead of having the async work try to update a label?
or add a method like this to update status if you have to update it?
void SafeUpdateStatusText(string text)
{
// update status text on event thread if necessary
Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate
{
lblstatus.Content = text;
}, null);
}
otherwise, i don't think we have enough details to help yet....
I hope this helps:
private delegate void UpdateLabelDelegate(DependencyProperty dp, object value);
public void UpdateLabelContent(Label label, string newContent)
{
Dispatcher.Invoke(new UpdateLabelDelegate(label.SetValue), DispatcherPriority.Background, ContentProperty, newContent);
}
Usage:
while (true)
{
UpdateLabelContent(this.lblStatus, "Next random number: " + new Random().Next());
Thread.Sleep(1000);
}

C# Making a delegate available to a class

I would like to make a delegate available to an entire class. The point of this is to allow a called method from an external class' backgroundWorker to continually report back through all of it's methods (ExternalClass.Run(); calls ExternalClass.Method2(); ExternalClass.Method3(); etc and they all need to send several progress reports. It seems inefficient to have to continually pass the delegate.
I've tried initializing an instance of the delegate globally and setting it to equal the passed instance in Run(); for each method to then have available to it but I am given an error that a null object cannot be implicitly converted.
thanks!
I cannot show the code I am working with as I do not currently have it with me (it's on my laptop) but I will try to better explain now. PSEUDO-CODE:
class form1 : form {
backgroundWorker_doWork()
{
Class2.Run();
}
backgroundWorker_OnProgressChange()
{
// do this
}
}
class class2{
Run(){
OtherMethod();ThirdMethod();
}
OtherMethod(){ //need to call backgroundWorker.ReportProcess(int, string)}
ThirdMethod(){ //need to call backgroundWorker.ReportProcess(int, string)}
}
I really don't want to have to pass it every time is the point, i'd like to somehow pass it to class2
You should show your code that isn't working and the exact error message. It should be fine - here's an example:
using System;
class Demo
{
private readonly Action action;
public Demo(Action action)
{
this.action = action;
}
public void FirstMethod()
{
Console.WriteLine("In first method");
action();
}
public void SecondMethod()
{
Console.WriteLine("In second method");
action();
}
}
class Test
{
static void Main()
{
Demo demo = new Demo(() => Console.WriteLine("Action called"));
demo.FirstMethod();
demo.SecondMethod();
}
}
You can use the InvokeMethod function from a backgroundWorker to allow the worker to execute any delegate, example below (also waits for the invoke to finish, which you may not need):
BackgroundWorker Function (C++.net)
BackgroundWorkerFunction()
{
::IAsyncResult ^ThreadResult;
SetTileCount_Delegate ^SetCountDel = gcnew SetTileCount_Delegate(this, &PartDetail::SetTileCount_Function);
//RecordingContainer is the class I am invoking into
ThreadResult = this->RecordingContainer->BeginInvoke(
SetCountDel, ThisTest->RecordingsCache->Count);
WaitForInvokeTimeOutOrCompletion(ThreadResult);
}
System::Void WaitForInvokeTimeOutOrCompletion(IAsyncResult ^ThreadResult)
{
if(ThreadResult == nullptr) return;
long SleepTotal = 0;
long SleepInterval = 100;
while ((SleepTotal <= 2000) && !ThreadResult->IsCompleted)
{
ThreadResult->AsyncWaitHandle->WaitOne(SleepInterval, false);
SleepTotal += SleepInterval;
}
}

Categories

Resources