Hello, this is my first question, I'll keep the tips on how to make a good question in mind, but if I make a mistake (like making this question too long!), please excuse me.
Now let's talk about my struggles, you can find my current situation and what I'm trying to do toward the end of this post.
I'm making my own dll and I encountered my current problem when I first tried to use one of my threading methods:
public void DoThread(Action method, bool isBG = false)
{
Thread workerThread =
new Thread(new ThreadStart(method)) {
Name = method.Method.Name + "Thread",
IsBackground = isBG
};
workerThread.Start();
}
to pass it a method that updated the WinForm on the MainThread.
Of course, it didn't work.
I asked Google-sensei, and discovered that I had to use Invoke
.
So to learn a bit I used the same WinForm app from before to execute this code:
ARThreads customThreads = new ARThreads();
public delegate void DelegateClass();
public DelegateClass delegateMethod;
private void Form1_Load(object sender, EventArgs e)
{
delegateMethod = new DelegateClass(ComboBox1);
customThreads.DoThread(ThreadFunction);
}
private void ComboBox1() => comboBox1.Items.Add("1");
private void ThreadFunction()
{
MyThreadClass myThreadClassObject = new MyThreadClass(this);
myThreadClassObject.Run();
}
public class MyThreadClass
{
Form1 myFormControl1;
public MyThreadClass(Form1 myForm) => myFormControl1 = myForm;
public void Run() => myFormControl1.Invoke(myFormControl1.delegateMethod);
}
Taken from a stackoverflow answer (I'm sorry I can't find the source again).
But, as I said in the title, I want my code to use generics, so it wasn't good enough. So I looked at these:
How to pass a Class as parameter for a method? [duplicate]
How to use class name as parameter in C#
C# Pass a Class as a parameter
C# Passing a class type as a parameter
And learnt another new thing, that I had to use Reflection:
How do I use reflection to call a generic method?
How to invoke static generic class methods if the generic type parameters are unknown until runtime?
Dynamically invoking any function by passing function name as string
And so I ended up with this method:
public void DoInvoke<T>(T orignal, string methodName,
params object[] parameters) where T : new()
{
MethodInfo method = typeof(T).GetMethod(methodName);
if(method != null)
method.Invoke(orignal, parameters);
}
Where I purposefully omitted to do this:
T instance = new T();
MethodInfo method = typeof(T).GetMethod(methodName);
method.Invoke(instance, parameters);
Because when I used my DoInvoke
method like that, even though there where no errors, the ComboBox on the WinForm didn't update.
During my trials with invoking, I discarded the delegate
parts, both because this worked:
DoInvoke(this, "ComboBox1", null);
And also because these didn't work as they weren't methods:
DoInvoke(this, "delegateMethod", null);
DoInvoke(this, "DelegateClass", null);
Then, since DoInvoke
worked, I started trying to use threads, at first with:
public void ThreadInvoke<T>(T original, string methodName, bool isBG = false,
params object[] parameters) where T : new()
{
Thread workerThread =
new Thread(() => DoInvoke(original, methodName, parameters)) {
Name = methodName + "ThreadInvoke",
IsBackground = isBG
};
workerThread.Start();
}
It didn't work, I also decided to just put the two methods together:
public void ThreadInvoke<T>(T original, string methodName, bool isBG = false,
params object[] parameters) where T : new()
{
Type classType = original.GetType();
MethodInfo method = classType.GetMethod(methodName);
System.Threading.Thread workerThread =
new System.Threading.Thread(new System.Threading.ThreadStart (
delegate () { method.Invoke(original, parameters); })) {
Name = methodName + "ThreadInvoke",
IsBackground = isBG
};
workerThread.Start();
}
Mostly thanks to:
Invoke method in new thread (method name from string)?
And I also "made" the following method thanks to a different answer given to the above question:
public void TaskInvoke<T>(T original, string methodName, bool isBG = false,
params object[] parameters) where T : new()
{
MethodInfo method = original.GetType().GetMethod(methodName,
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic
);
Task.Factory.StartNew(delegate () { method.Invoke(original, parameters); },
TaskCreationOptions.LongRunning);
}
In case you're wondering, if ThreadInvoke
and TaskInvoke
had worked, I wouldn't be here.
So what am I exactly trying to do, in a clean and concise way?
In general: 1. Create a new thread; 2. The thread invokes the method it received; 3. The method updates the WinForm, as needed.
Using the WinForm I'm currently testing my dll on as example:
SQLQueries customSQL = new SQLQueries();
ARThreads customThreads = new ARThreads();
private void Form1_Load(object sender, EventArgs e)
{
//Either this
customThreads.ThreadInvoke(this, "ComboBoxSetup", parameters: null);
//Or this
customThreads.TaskInvoke(this, "ComboBoxSetup", parameters: null);
//As long as it works
}
public void ComboBoxSetup()
{
//This is a method to make object arrays
//out of the result from a sql query
object[] first = customSQL.ObjectReader(
"select Column1, Column2 from TestTable order by Column1",
connectionString,
2
);
if(first != null)
comboBox1.Items.AddRange(first);
comboBox2.Items.Add("Test");
}
I also saw this:
How do I update the GUI from another thread?
But it's not really what I wanted, as it would turn to this: 1. Create a new thread; 2. The method starts; 3. When needed, each control would call an invoke.
In conclusion, can I thread and invoke a method so that it can update controls of the WinForm/MainThread when and if it needs to? Or do I have to thread the method, and in the method call the invoke for each specificcontrol?
P.S.: I really hope the answer isn't as long-winded as my post lol