C#: String as parameter to event? - c#

I have a GUI-thread for my form and another thread that computes things.
The form has a richtTextBox. I want the worker-thread to pass strings to the form, so that every string is displayed in the textbox.
Everytime a new string is generated in the worker thread I call an event, and this should now display the string.
But I don't know how to pass the string! This is what I tried so far:
///// Form1
private void btn_myClass_Click(object sender, EventArgs e)
{
myClass myObj = new myClass();
myObj.NewListEntry += myObj_NewListEntry;
Thread thrmyClass = new Thread(new ThreadStart(myObj.ThreadMethod));
thrmyClass.Start();
}
private void myObj_NewListEntry(Object objSender, EventArgs e)
{
this.BeginInvoke((MethodInvoker)delegate
{
// Here I want to add my string from the worker-thread to the textbox!
richTextBox1.Text += "TEXT"; // I want: richTextBox1.Text += myStringFromWorkerThread;
});
}
///// myClass (working thread...)
class myClass
{
public event EventHandler NewListEntry;
public void ThreadMethod()
{
DoSomething();
}
protected virtual void OnNewListEntry(EventArgs e)
{
EventHandler newListEntry = NewListEntry;
if (newListEntry != null)
{
newListEntry(this, e);
}
}
private void DoSomething()
{
///// Do some things and generate strings, such as "test"...
string test = "test";
// Here I want to pass the "test"-string! But how to do that??
OnNewListEntry(EventArgs.Empty); // I want: OnNewListEntry(test);
}
}

Like this
public class NewListEntryEventArgs : EventArgs
{
private readonly string test;
public NewListEntryEventArgs(string test)
{
this.test = test;
}
public string Test
{
get { return this.test; }
}
}
then you declare your class like this
class MyClass
{
public delegate void NewListEntryEventHandler(
object sender,
NewListEntryEventArgs args);
public event NewListEntryEventHandler NewListEntry;
protected virtual void OnNewListEntry(string test)
{
if (NewListEntry != null)
{
NewListEntry(this, new NewListEntryEventArgs(test));
}
}
}
and in the subscribing Form
private void btn_myClass_Click(object sender, EventArgs e)
{
MyClass myClass = new MyClass();
myClass.NewListEntry += NewListEntryEventHandler;
...
}
private void NewListEntryEventHandler(
object sender,
NewListEntryEventArgs e)
{
if (richTextBox1.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
this.NewListEntryEventHandler(sender, e);
});
return;
}
richTextBox1.Text += e.Test;
}
I've taken the liberty of making the NewListEntryEventArgs class immutable, since that makes sense. I've also partially corrected your naming conventions, simplified and corrected where expedient.

You need to create a new class by inheriting off EventArgs.

Create your own version of the EventArgs.
Do it like this:
public class MyEventArgs : EventArgs
{
public string MyEventString {get; set; }
public MyEventArgs(string myString)
{
this.MyEventString = myString;
}
}
Then in your code replace the EventArgs with MyEventArgs and create an MyEventArgs object with your string in it.
Then you can access it by using the MyEventArgs instance .MyEventString.
So you would do something like this:
///// myClass (working thread...)
class myClass
{
public event EventHandler NewListEntry;
public void ThreadMethod()
{
DoSomething();
}
protected virtual void OnNewListEntry(MyEventArgs e)
{
EventHandler newListEntry = NewListEntry;
if (newListEntry != null)
{
newListEntry(this, e);
}
}
private void DoSomething()
{
///// Do some things and generate strings, such as "test"...
string test = "test";
OnNewListEntry(new MyEventArgs(test));
}
}
And in your form:
private void myObj_NewListEntry(Object objSender, MyEventArgs e)
{
this.BeginInvoke((MethodInvoker)delegate
{
// Here I want to add my string from the worker-thread to the textbox!
richTextBox1.Text += e.MyEventString;
});
}

In general you need to inherit EventArgs and add a string property, and then make your event of type EventHandler<YourEventArgs>, but that is a classic case for the BackgroundWorker.
Sample here:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
and here:
C# backgroundWorker reports string?

Related

Delegate with generic list signature for passing data to another form

I'm quite new in C#, so I'm struggling with this more than two days. I hope that some one can help me out with this one.
Below some simplified code from my application.
I want to pass a List from Form1 to Form2 using delegate and event.
How can I do this? I read tons of explanations about events and delegates, but I still can't figure it out, how this really works.
Form1:
public delegate List<string> ProfileImportEventHandler();
public event ProfileImportEventHandler ProfileImported;
private void btnImport_Click(object sender, EventArgs e)
{
// raise an event
OnProfileImported();
}
protected virtual void OnProfileImported()
{
if (ProfileImported != null) // check if there are subscribers
{
ProfileImported();
}
}
Form2:
public partial class Form2 : Form
{
Form1 frm1;
public Form1()
{
// Constructor logic
frm1.ProfileChanged += new Form1.ProfileImportEventHandler(Form1_OnProfileImported);
}
}
List<string> Form1_OnProfileImported()
{
// TO DO
}
UPDATE
None of the solutions worked so far. Here is what I have already tried:
Form 2
// use generic list for profiles that will be imported from USB-Stick
private List<string> profilePaths = new List<string>();
public delegate void ProfileImportEventHandler(object sender, ProfileImportEventArgs e);
public event ProfileImportEventHandler ProfileImported;
public delegate void ImportButtonClickedEventHandler();
public event ImportButtonClickedEventHandler ButtonImportClicked;
public delegate void HaveDataDelegate(IList<string> data);
public event HaveDataDelegate HaveData;
//....
private void btnImport_Click(object sender, EventArgs e)
{
// do something...
// raise an event
var ea = new ProfileImportEventArgs(profilePaths);
OnProfileImported(ea);
OnButtonImportClicked();
// When there is data:
var copy = HaveData; // Use copy to avoid race conditions
if (copy != null)
{
copy(profilePaths);
}
// close form
this.Dispose();
}
protected virtual void OnProfileImported(ProfileImportEventArgs ea)
{
if (ProfileImported != null) // check if there are any subscribers
{
ProfileImported(this, ea);
}
}
protected virtual void OnButtonImportClicked()
{
if (ButtonImportClicked != null)
{
// fire event
ButtonImportClicked();
}
}
Form 1
public partial class frm_1 : Form
{
// child form
frm_2 frm2;
public frm_1()
{
InitializeComponent();
// do something...
// not sure if this is correct code and the correct place for it
frm2 = new frm_2();
frm2.ProfileImported += new frm_2.ProfileImportEventHandler(frm2_OnProfileImported);
//frm2.ProfileImported += frm2_OnProfileImported;
frm2.ButtonImportClicked += new frm_2.ImportButtonClickedEventHandler(frm2_ButtonImportClicked);
// In creation/init:
frm2.HaveData += DataFromForm2;
}
void frm2_OnProfileImported(object sender, ProfileImportEventArgs e)
{
// do something
}
void frm2_ButtonImportClicked()
{
// do something
}
private void DataFromForm2(IList<string> data)
{
// Process the data from Form2.
}
}
What am I still missing? Thank you for your help.
frm1.ProfileChanged += new Form1.ProfileImportEventHandler(Form1_OnProfileImported);
[…]
List<string> frmLoadProfileUSB_OnProfileImported()
First those names do not match. Second, with matching signatures you do not need (since C#2 if I recall correctly) to explicitly create the delegate. Thus:
frm1.ProfileChanged += frmLoadProfileUSB_OnProfileImported;
However, I think you have the event in the wrong place. It appears it is Form2 trying to pass data to Form1. Thus the event needs to be on Form2, with a delegate that is passed the data. Thus:
In Form2
public delegate void HaveDataDelegate(IList<string> data);
public event HaveDataDelegate HaveData;
// When there is data:
var copy = HaveData; // Use copy to avoid race conditions
if (copy != null) {
copy(data);
}
In Form1
// In creation/init:
Form2Instance.HaveData += DataFromForm2;
private void DataFromForm2(IList<string> data) {
// Process the data from Form2.
}
It's better not to use strong coupling.
So best solution here would be to store data in database or create proxy-object (class/struct).
like:
public (static) class ProfileChangesMonitor
{
...your logic here
}
If you want to use event handlers, you should follow the general pattern, defining a class that inherits EventArgs (supposing you want to involve a list in the event) in this way:
// Event Args
public class ProfileImportEventArgs : EventArgs {
private IList<string> list;
public ProfileImportEventArgs(IList<string> list) {
this.list = list;
}
public IList<string> List {
get {
return this.list;
}
}
}
// Event Handler Delegate
public delegate void ProfileImportEventHandler(object sender, ProfileImportEventArgs e);
// Form1:
public event ProfileImportEventHandler ProfileImported;
// ...
private void btnImport_Click(object sender, EventArgs e)
{
// raise an event
List<string> list = new List();
// Add something to list if needed
var ea = new ProfileImportEventArgs(list);
OnProfileImported(ea);
// Use ea.list here if necessary
}
protected virtual void OnProfileImported(ProfileImportEventArgs ea)
{
if (ProfileImported != null) { // check if there are subscribers
ProfileImported(this, ea);
}
}
// Form2:
public partial class Form2 : Form
{
Form1 frm1;
public Form1()
{
// Constructor logic
// TODO: Instantiate frm1 first.
frm1.ProfileImported += new Form1.ProfileImportEventHandler(Form1_OnProfileImported);
}
}
private void frmLoadProfileUSB_OnProfileImported(object sender, ProfileImportEventArgs e)
{
// Use and/or modify e.List if needed
}

Creating a reference to another object

I want to make an object that stores a reference to another object. I have a code like this:
public partial class Form1 : Form
{
int test = 1;
store st;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
st = new store(test);
}
private void button1_Click(object sender, EventArgs e)
{
test = 7;
}
private void button2_Click(object sender, EventArgs e)
{
label1.Text = Convert.ToString((int)st.o);
}
}
public class store
{
public object o;
public store(object obj)
{
o = obj;
}
}
If I click button2 - I can see "1" in my label. But if I click button2 and then button1 - I still see "1". How should I alter my code so I'll see "7" in that case?
When you create the store object you're evaluating the value of the test variable, and storing that value rather than the test variable. If you want to have a way of evaluating the variable to its value later, you can use a lambda to close over the variable, since closures in C# close over variables, not values.
public partial class Form1 : Form
{
int test = 1;
Store<string> store;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
store = new Store<string>(() => test.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
test = 7;
}
private void button2_Click(object sender, EventArgs e)
{
label1.Text = store.GetValue();
}
}
public class Store<T>
{
private Func<T> function;
public Store(Func<T> function)
{
this.function = function;
}
public T GetValue()
{
return function();
}
}
Note that I made a few changes to the names of items to be in line with standard C# conventions, rather than having Store expose the generator function's field publicly, I provide a function that lets you get the value, and I've also made it generic, rather than using object, as this both avoids boxing, and prevents the need to cast the object returned from the store.
With minimal changes, here you go.
Properties and constructor of Form1:
public Form1()
{
st = new store(test);
}
private int testPrivate { get; set; }
public int test
{
get { return testPrivate; }
set
{
testPrivate = value;
st.o = value;
}
}
public store st { get; set; }
The store class:
public class store
{
public object o { get; set; }
public store(object obj)
{
o = obj;
}
}

Can I prevent ListBox.RefreshItem(object item) from removing and re-adding the object?

The issue I am having is that when I update my object, the ListBox automatically removes and then re-adds the object to the list, thus calling the index and value changed events. I was able to prevent this by creating a custom ListBox control and when the PropertyChangedEvent was called, I would raise a flag that would prevent those events in the base class from being called. What is happening now is that my entire reference is being replace by a new reference and unless I re-select the item in the ListBox, I have the wrong reference.
What I basically want to do, is to change the Display Value in my object and then have it update only the text in the list box. I do not want it to remove and to re-add the object/reference/whatever it does. It's quite annoying.
Here is the example code I am working with...
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.myListBox1.SelectedValueChanged += this.onchange;
}
private void Form1_Load(object sender, EventArgs e)
{
this.myListBox1.Add(new strobj("z"));
this.myListBox1.Add(new strobj("a"));
this.myListBox1.Add(new strobj("b"));
this.myListBox1.Add(new strobj("f"));
this.myListBox1.Add(new strobj("n"));
this.myListBox1.Add(new strobj("h"));
this.myListBox1.Add(new strobj("p"));
this.myListBox1.Add(new strobj("t"));
this.myListBox1.Add(new strobj("c"));
this.myListBox1.Add(new strobj("q"));
}
private void onchange(object sender, EventArgs e)
{
MessageBox.Show("Hello World");
}
int i = 0;
private void button1_Click(object sender, EventArgs e)
{
if (this.myListBox1.SelectedItem != null)
{
strobj item = (strobj)this.myListBox1.SelectedItem;
item.Name1 = i++.ToString();
}
}
}
public partial class MyListBox
{
public MyListBox()
{
InitializeComponent();
}
public void Add(strobj item)
{
item.OnNameChanged += this.MyDispalyMemberChanged;
this.Items.Add(item);
}
bool refreshing = false;
public void MyDispalyMemberChanged(strobj itemChanged)
{
this.refreshing = true;
this.RefreshItem(this.Items.IndexOf(itemChanged));
this.refreshing = false;
}
protected override void OnSelectedValueChanged(EventArgs e)
{
if (!this.refreshing)
{
base.OnSelectedValueChanged(e);
}
}
}
class strobjCollection : List<strobj>
{
NameChangeEventHandler NameChangedEvent;
}
delegate void NameChangeEventHandler(strobj sender);
public class strobj
{
internal NameChangeEventHandler OnNameChanged;
private string _Name1;
public string Name1
{
get { return this._Name1; }
set
{
this._Name1 = value;
if (this.OnNameChanged != null)
{
this.OnNameChanged(this);
}
}
}
public int i = 0;
public string str = "p";
public strobj(string name)
{
this._Name1 = name;
}
public strobj()
{
this._Name1 = "You did not create this object";
}
public override string ToString()
{
return this._Name1;
}
}
This is what the INotifyPropertyChanged interface was made for.
Instead of raising your custom event, you'd raise the PropertyChanged event with the name of the property you changed set in the event args and the listbox would update.
See MSDN.

Delegate doesn't notify the method

Would you look at my code and tell me where I went wrong? in following code I am trying to send a notification to myMethod() method when Form1 gets maximized.
Thanks!
namespace WindowsDelegate1
{
public delegate void ChangedEventHandler(object sender, EventArgs e);
class myForm : Form
{
public event ChangedEventHandler Changed;
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this,e);
}
public override System.Drawing.Size MaximumSize
{
//get
//{
// return base.MaximumSize;
//}
set
{
base.MaximumSize = value;
OnChanged(EventArgs.Empty);
}
}
}
}
namespace WindowsDelegate1
{
class EventListener
{
private myForm TheForm;
public EventListener(myForm theform)
{
TheForm = theform;
TheForm.Changed += new ChangedEventHandler(myMethod);
}
private void myMethod(object sender, EventArgs e)
{
MessageBox.Show("hey, window should be maximized now!");
}
public void Detach()
{
TheForm.Changed -= new ChangedEventHandler(myMethod);
TheForm = null;
}
}
}
Here is the testing unit / or main()
namespace WindowsDelegate1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
myForm f = new myForm();
EventListener listener = new EventListener(f);
f.ShowDialog();
f.WindowState = FormWindowState.Maximized;
listener.Detach();
}
}
}
What's probably happening is the event is either fired after your .Detach() call, or is never fired at all. I would start by removing the listener.Detach() call. Generally, you attach to events when the form is created or when it loads and detach when it is unloading.
Other than that, your Detach method is problematic because it tries to remove a different ChangedEventHandler instance than the one added. If you're wrapping your methods in ChangedEventHandler you need to store the instance you added.
Thank you for sharing your ideas!
I fixed it by removing the property (not idea why I used that!!) and using method instead by:
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
OnChanged(EventArgs.Empty);
}
I have updated my source code above too

Delegates and events?

This question is related to c#.The scenario is that when i click the button the operations like File reading,Data manipulation ,and file dumping are going to be happened.After the completion of each operation i will update the status(i.e,File reading completed,data manipulation completed) in the label which is in UI(FORM-frmTesting)
The button click event is
namespace frmTesting
{
public partial class Form1 : Form
{
private void button1_Click_1(object sender, EventArgs e)
{
class1 l_objClass1 = new class1();
l_objClass1.DoOperation();
}
}
public class class1
{
public int DoOperation()
{
ReadTextFile();
ParsingData();
SaveTextFile();
return 0;
}
private int ReadTextFile()
{
//Read the text File
return 0;
}
private int ParsingData()
{
// Data manipulation
return 0;
}
private int SaveTextFile()
{
// save the file
return 0;
}
}
}
Is it possible by using Delegates and Events....if you have any queries plz revert back me
You'll have to modify class1 to broadcast events that other classes can listen to:
public class class1
{
// Not necessary, but will allow you to add custom EventArgs later
public delegate void StatusChangedEventHandler(object sender, EventArgs e);
public event StatusChangedEventHandler FileRead;
public event StatusChangedEventHandler FileParsed;
public event StatusChangedEventHandler FileSaved;
public int DoOperation()
{
ReadTextFile();
ParsingData();
SaveTextFile();
return 0;
}
private int ReadTextFile()
{
//Read the text File
OnFileRead(EventArgs.Empty);
return 0;
}
private int ParsingData()
{
// Data manipulation
OnFileParsed(EventArgs.Empty);
return 0;
}
private int SaveTextFile()
{
// save the file
OnFileSaved(EventArgs.Empty);
return 0;
}
protected virtual void OnFileRead(EventArgs e)
{
if(FileRead != null)
FileRead(this, e);
}
protected virtual void OnFileParsed(EventArgs e)
{
if(FileParsed != null)
FileParsed(this, e);
}
protected virtual void OnFileSaved(EventArgs e)
{
if(FileSaved != null)
FileSaved(this, e);
}
}
And then have your form listen for those events and change its label appropriately:
public partial class Form1 : Form
{
private void button1_Click_1(object sender, EventArgs e)
{
class1 l_objClass1 = new class1();
l_objClass1.FileRead +=
delegate { lblStatus.Text = "File Read..."; };
l_objClass1.FileParsed +=
delegate { lblStatus.Text = "File Parsed..."; };
l_objClass1.FileSaved +=
delegate { lblStatus.Text = "File Saved..."; };
l_objClass1.DoOperation();
}
}
The short answer is yes. You add events to class1 and add handlers to Form1 with the appropriate logic. Below is a sample of how to do this
public partial class Form1 : Form
{
private void button1_Click_1(object sender, EventArgs e)
{
class1 obj = new class1();
obj.FileReadingComplete += HandleFileReadingComplete;
obj.DataManipulationComplete += HandleDataManipulationComplete;
obj.DoOperation();
obj.FileReadingComplete -= HandleFileReadingComplete;
obj.DataManipulationComplete -= HandleDataManipulationComplete;
}
private void HandleFileReadingComplete(object sender, EventArgs args){
//code here
}
private void HandleDataManipulationComplete(object sender, EventArgs args)
{
//code here
}
}
public class class1
{
public event EventHandler FileReadingComplete;
public event EventHandler DataManipulationComplete;
public int DoOperation()
{
ReadTextFile();
OnFileReadingComplete();
ParsingData();
OnDataManipulationComplete();
SaveTextFile();
return 0;
}
private int ReadTextFile()
{
//Read the text File
return 0;
}
private int ParsingData()
{
// Data manipulation
return 0;
}
private int SaveTextFile()
{
// save the file
return 0;
}
public void OnFileReadingComplete()
{
EventHandler handler = FileReadingComplete;
if (handler != null) {
handler(this, EventArgs.Empty);
}
}
public void OnDataManipulationComplete()
{
EventHandler handler = DataManipulationComplete;
if (handler != null) {
handler(this, EventArgs.Empty);
}
}
}

Categories

Resources