So I roughly got this:
public partial class Form1 : Form
{
GameEngine engine;
public Form1()
{
engine = new GameEngine();
}
public void repaint()
{
}
}
class GameEngine
{
public void update()
{
}
}
Now i wanna add something to the update() method, which makes it call the repaint() method, inside of that instance of the Form1 class, in which the respective object of the GameEngine class was created.
In java i could've done it like this
engine = new GameEngine()
{
public void repaintCaller()
{
repaint();
}
};
and call repaintCaller() in the update() method, but that doesn't work in c#, now what is the equilvalent way to do this in c#?
One way to have it is:
public partial class Form1 : Form
{
GameEngine engine;
public Form1()
{
engine = new GameEngine();
engine.update(this);
}
public void repaint()
{
}
}
class GameEngine
{
public void update(Form1 form)
{
form.repaint();
}
}
You could pass to the GameEngine.update method an Action delegate to the repaint method in the form instance
public partial class Form1 : Form
{
GameEngine engine;
public Form1()
{
engine = new GameEngine();
// I put the call here for demo purpose,
// of course you call the engine.update
// method when you need and where you want
engine.update(repaint)
}
public void repaint()
{
Console.WriteLine("repaint called in the Form1 instance");
}
}
class GameEngine
{
public void update(Action clientUpdate)
{
if(clientUpdate != null)
clientUpdate.Invoke();
// or just... clientUpdate();
}
}
The parameterless Action delegate in C# is a way to pass, as parameter, a method (repaint) to the called method (update). Of course, you could pass the whole Form1 instance to the update but this approach binds the GameEngine class to the Form1 class. With the Action approach you are free from this coupling and you could pass any other method that returns void and doesn't take any parameter defined in any class of your program. This frees your GameEngine.update method from any specific bind to the caller.
I would try something like that
class GameEngine
{
public void update(ref Form1 caller)
{
caller.Refresh() //msdn says "Forces the control to invalidate its client area and immediately redraw itself and any child controls."
}
}
public partial class Form1 : Form
{
[...]
engine = new GameEngine()
engine.update(ref This)
}
I'm not sure of anything, i'm not used to C#.
I just hope it will help a bit :)
You can also set Events from your form, like this:
public partial class Form1 : Form
{
GameEngine engine;
public Form1()
{
InitializeComponent();
engine = new GameEngine();
engine.repaintRequired += engine_repaintRequired;
engine.update();
}
private void engine_repaintRequired(object sender, EventArgs e)
{
repaint();
}
public void repaint()
{
Console.Write("repaint");
}
}
class GameEngine
{
public event EventHandler repaintRequired;
public void update()
{
onRepaintRequired();
}
private void onRepaintRequired()
{
if (repaintRequired != null)
repaintRequired(this, new EventArgs());
}
}
Related
I try to get working a debugger window which tell me what is going on in my code while running.
So I have one mainform which write in debug console when it runs. and a second form which write in my debug console too.
Now I found another way to do it but I'm wondering what it really do.
Source(my code but their Idea):: this site QA: Can I make a Form a singleton?
DebugConsole create a static Instance::
public partial class DEBUGConsole : Form
{
public static DEBUGConsole Instance;
public DEBUGConsole()
{
Instance = this;
InitializeComponent();
}
internal void SetValueFunction(string value)
{
ListBoxDEBUG.Items.Add(value);
}
}
Form1 create the Instance to share::
public partial class MainForm : Form
{
public DEBUGConsole ConsoleDEB;
internal SecondForm Formsnd2;
public MainForm()
{
InitializeComponent();
DEBUGConsole ConsoleDEB = new DEBUGConsole();
ConsoleDEB.Show();
DEBUGConsole.Instance.SetValueFunction("Hello 1");
Formsnd2 = new SecondForm();
Formsnd2.Show();
}
private void textBoxForm1_TextChanged(object sender, EventArgs e)
{
DEBUGConsole.Instance.SetValueFunction("line has changed 1 : " +textBoxForm1.Text);
}
}
and Form2 use it too::
public partial class SecondForm : Form
{
public SecondForm(/*DEBUGConsole _ConsoleDEB*/)
{
InitializeComponent();
DEBUGConsole.Instance.SetValueFunction("Hello 2");
}
private void textBoxForm2_TextChanged(object sender, EventArgs e)
{
DEBUGConsole.Instance.SetValueFunction("line has changed 2 : " + textBoxForm2.Text);
}
}
Is that correct?
Is there only one Instance of DEBUGConsole in here?
Should it be sealed?
Is that a way to Singleton?
Thanks
I have two Projects one is a Winform application another is a Class library. I have added a reference to the class Library in Winform and called a method of the class library. Now I want to call a different method in winform application from class library but I can't add a reference to winform to the class library.
IN CODE:-
public partial class Form1 : Form
{
private void btn_Click(object sender, EventArgs e)
{
A obj = new A();
obj.foo();
}
public string Test(par)
{
//to_stuff
}
}
and in Class library
class A
{
public void foo()
{
//Do_stuff
//...
Test(Par);
//Do...
}
}
You can achieve this by injecting Test into class A.
For example:
public partial class Form1 : Form
{
private void btn_Click(object sender, EventArgs e)
{
A obj = new A();
obj.foo(Test);
}
public string Test(string par)
{
//to_stuff
}
}
class A
{
public void foo(Func<string, string> callback)
//Do_stuff
//...
if (callback != null)
{
callback(Par);
}
//Do...
}
}
While the callback method from David is a sufficient solution, if your interactions gets more complex, I would use this approach
Create an inteface in your class libary
public interface ITester
{
string Test(string value);
}
Rewrite your code so class A expects an ITester interface
public class A
{
public A(ITester tester)
{
this.tester = tester;
}
public string foo(string value)
{
return this.tester.Test(value);
}
}
Implement your interface in Form1
public partial class Form1 : Form, ITester
{
private void btn_Click(object sender, EventArgs e)
{
A obj = new A(this);
obj.foo("test");
}
public string Test(string value)
{
//to_stuff
return value;
}
}
I have an winform and an interface callback that continuously sends updates. I want to be able to update a label1.Text from the callback interface. However since the intrface runs on an seperate thread I do not think i can do it directly so I was trying to use a delegate and invoke.
However I am running into an error:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created - at
form1.Invoke(form1.myDelegate, new Object[] { so.getString() });
Here is the full code.
public partial class Form1 : Form
{
MyCallBack callback;
public delegate void UpdateDelegate(string myString);
public UpdateDelegate myDelegate;
public Form1()
{
InitializeComponent();
myDelegate = new UpdateDelegate(UpdateDelegateMethod);
callback = new MyCallBack(this);
CallBackInterfaceClass.SetCallBack(callback);
callback.OnUpdate();
}
public void UpdateDelegateMethod (String str)
{
label1.Text = str;
}
}
class MyTestCallBack : Callback
{
public Form1 form1;
public SomeObject so;
public MyTestCallBack(Form1 form)
{
this.form1 = form;
}
public void OnUpdate(SomeObject someobj)
{
so = someobj;
OnUpdate();
}
public void OnUpdate()
{
form1.Invoke(form1.myDelegate, new Object[] { so.getString() });
}
}
Two questions.
Can anyone explain what I am doing wrong?
Is this actually best method to do this?
Here is the answer based on the reply by bokibeg (see below) with a couple of modifications to make it work:
public partial class Form1 : Form {
MyTestCallBack _callback;
public Form1()
{
InitializeComponent();
_callback = new MyTestCallBack();
_callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
_callback.OnUpdate();
}
private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (InvokeRequired)
{
Invoke(new Action(() =>
{
callback_MyTestCallBackEvent(sender, e);
}));
return;
}
label1.Text = e.SomeObject.GetDisplayString();
}
class MyTestCallBackEventArgs : EventArgs
{
public SomeObject SomeObj { get; set; }
}
class MyTestCallBack : Callback
{
public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
private SomeObject _so;
protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (MyTestCallBackEvent != null)
MyTestCallBackEvent(this, e);
}
public void OnUpdate(SomeObject someobj)
{
_so = someobj;
OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
} }
Here's what I'd do:
public partial class Form1 : Form
{
MyTestCallBack _callback;
public Form1()
{
InitializeComponent();
_callback = new MyTestCallBack();
_callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
_callback.OnUpdate();
}
private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
{
// TODO: Invoke code here with e.g. label1.Text = e.SomeObj.ToString()...
}
}
class MyTestCallBackEventArgs : EventArgs
{
public SomeObject SomeObj { get; set; }
}
class MyTestCallBack : Callback
{
public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
private SomeObject _so;
public MyTestCallBack()
{
//
}
protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (MyTestCallBackEvent != null)
MyTestCallBackEvent(e);
}
public void OnUpdate(SomeObject someobj)
{
_so = someobj;
OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
}
}
It separates the GUI logic from whatever that thread is doing. It fires an event and it's Form's duty to do whatever it pleases with it.
I'm not sure if this compiles, I wrote it in text pad. Tell me if you have questions.
You probably just learned about delegates and got carried away with it, this is similar as it uses an event but the event is properly placed in the "back end" logic - form may or may not use it. Also notice how form's code is a lot better, it doesn't have so much boilerplate code just to implement some background service class.
Note however that MyTestCallBackEvent event may keep firing even after you close the form so make sure you unsubscribe from it when the form closes or disposes (or whenever you feel like form doesn't need it anymore).
Oh and I almost forgot: the original error you were getting is because you called Invoke when one wasn't required and Form definitely wasn't ready for it. Read this question to see how to safely invoke controls.
I am used to writing embedded c and poorly skilled in c#.
My problem is that I want to be able to run the function openAnotherForm() from Welcome_Form and right now the code does not work. I patiently tried different things but only managed to push my frustration.
I simplified my relevant code to illustrate the problem.
File 1 - This will run and open file 2.
class UIcode
{
private Welcome_Form Welcome;
private AnotherForm_Form AnotherForm;
public UIcode()
{
Welcome = new Welcome_Form();
Application.Run(Welcome);
}
public void openAnotherForm()
{
Welcome.Hide();
AnotherForm = new AnotherForm_Form();
AnotherForm.ShowDialog();
}
}
File 2 - When I click TheButton, the program should run the function openAnotherFrom from file 1.
public partial class Welcome_Form : Form
{
public Welcome_Form()
{
InitializeComponent();
}
private void TheButton_Click(object sender, EventArgs e)
{
// Function from file 1
UIcode.openAnotherForm();
}
}
I realize the problem might be quite trivial but I would still be grateful for an explanation on how to do this.
Preferable: The functions from UIcode should only be recognized by classes specified by UIcode.
You can change the constructor to take a reference to the instance of UIcode that opened it:
private static UIcode myParent;
public Welcome_Form(UIcode parent)
{
myParent = parent;
InitializeComponent();
}
Now in UIcode:
public UIcode()
{
Welcome = new Welcome_Form(this);
Application.Run(Welcome);
}
And finally, back in Welcome_Form:
private void TheButton_Click(object sender, EventArgs e)
{
// Function from file 1
myParent.openAnotherForm();
}
Your openAnotherForm() method is not static, so it needs an instance reference in order to be used. Either instantiate a UICode object, or mark the method as static.
You to create an instance of the class in File1 to call the method. You've called the class UICode, so the constructor should be renamed from public UserInterface() to public UICode().
class UIcode
{
private Welcome_Form Welcome;
private AnotherForm_Form AnotherForm;
public UIcode() // Renamed Constructor
{
Welcome = new Welcome_Form();
Application.Run(Welcome);
}
public void openAnotherForm()
{
Welcome.Hide();
AnotherForm = new AnotherForm_Form();
AnotherForm.ShowDialog();
}
}
public partial class Welcome_Form : Form
{
public Welcome_Form()
{
InitializeComponent();
}
private void TheButton_Click(object sender, EventArgs e)
{
// Create an instance UICode
UICode instance = new UICode();
// Call the method from the instance, not from the class.
instance.openAnotherForm();
}
}
Alternatively, you can make openAnotherForm() a static method, but you'll also need to make the instance variables (Welcome and AnotherForm) static. You will also need to initialize them, but you can do that by making the constructor static as well.
class UIcode
{
private static Welcome_Form Welcome;
private static AnotherForm_Form AnotherForm;
public static UIcode() // Renamed Constructor
{
Welcome = new Welcome_Form();
Application.Run(Welcome);
}
public static void openAnotherForm()
{
Welcome.Hide();
AnotherForm = new AnotherForm_Form();
AnotherForm.ShowDialog();
}
}
I have a simple forms program that I have been fighting with for a while now. I simply want to be able to call a method from a different class file (when a certain step is triggered in the code in that class file) in order to insert a string in the listBox.
Here is my main method, pretty standard:
class Program
{
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
Here is the function which resides in my MainForm.cs file, which I can call just fine from that class file (via 'TextToBox(myString);'):
public partial class MainForm : Form
{
...
// Function to output results to main Listbox window
public void TextToBox(string aString)
{
// Place messages in Main Display list box window
this.listBox1.Items.Insert(0, aString);
}
...
}
But my problem is when I am in another class and I want to call 'TextToBox(myString);'. If I create another object reference of the MainForm, the code compiles fine but nothing will show up in the listBox. How do I do this? I cannot simply make TextToBox() static. I know I must create the object reference but I can't figure out how to reference the ORIGINAL MainForm, the one that was created in the Main method. Thanks in advance...
This will work, but only when you have one instans of MainForm.
public class MainForm : Form
{
public MainForm()
{
Instance = this;
}
public static MainForm Instance { get; private set; }
// Function to output results to main Listbox window
public void TextToBox(string aString)
{
// Place messages in Main Display list box window
this.listBox1.Items.Insert(0, aString);
}
}
public class Other
{
public void AddTextToListBox()
{
MainForm.Instance.TextToBox("Test");
}
}
...Edit...
Alternative:
class Program
{
public static MainForm MainFormInstance;
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainFormInstance = new MainForm();
Application.Run(MainFormInstance);
}
}
public class Other
{
public void AddTextToListBox()
{
Program.MainFormInstance.TextToBox("Test");
}
}
I would just pass a delegate to the other class.
/* this runs (previous code was not guaranteed to run) */
class OtherClass
{
public delegate void TextToBox(string s);
TextToBox textToBox;
int next = 0;
public OtherClass(TextToBox ttb)
{
textToBox = ttb;
}
public void SendSomeText()
{
textToBox(next.ToString());
next++;
}
}
I'm assuming you'll be instantiating OtherClass from MainForm. Is this how you're calling "OtherClass"?
public partial class MainForm : Form
{
OtherClass otherClass;
public MainForm()
{
/* just two controls -- listBox1 and button1 */
InitializeComponent();
otherClass = new OtherClass(this.TextToBox);
}
public void TextToBox(string aString)
{
listBox1.Items.Add(aString);
}
private void button1_Click(object sender, EventArgs e)
{
otherClass.SendSomeText();
}
}
On a button click the next numeric value is added at the beginning of the ListBox. You'll have to post some of your code if you need further help.
alternatively you could use a singleton pattern, or static methods and make sure you include the class in a 'using' statement at the top of your program