Im using Visual C# 2010 express. Im working on a game, and have come accross a small, newbie problem. Thing is, i guess we're dealing with a best practise type situation, and none of the few beginner books i have really helped with it, so i hope you guys can.
So, i have two forms, one is a splashscreen/startup form and the other is the main game window. I made a class that contains all world data, and when in the first screen user clicks on "new game", a new instance of this class is generated and populated with data.
So far so good.
The newgame button, in addition to creating the world instance, opens up the main game window. The problem is, in the main game window, when i try to use attributes of the gameworld instance, it says that it doesnt exist in this context.
So, if i get it right, the created instance only exists within the first form class... is that correct?
So if i'd like to move that whole data, should i actually serialize and save the world class instance data, and load it in the second form? Or how should i approach this.
I understand it's a very newbie question, and i could propably hack it to work, but the thing is - i really feel like i have to understand everything im doing.
Thanks in advance!
You need to create a constructor on your game form that accepts an instance of your world class and assign it to a field of the same type - the field will be accessible to the game form methods.
World world;
// constructor
public GameForm (World world)
{
this.world = world;
}
// Can now use `world` in all `GameForm` methods
Instead of constructor injection (as I have shown in my example), you can use property injection, though I like the former better (tends to ensure proper initialization - though you may want to check for a null being passed in).
If there is a reference of the world data object in the splash screen, you can assign this to a public member in the main screen, or pass it to the main screen through a constructor.
so in the splash screen
FrmMain frmMain = new FrmMain();
frmMain.WorldData = this.WorldData;
if it is an instance member of the splash screen
or maybe something like
FrmMain frmMain = new FrmMain();
frmMain.WorldData = new WorldData();
or even
FrmMain frmMain = new FrmMain(this.WorldData);
or
FrmMain frmMain = new FrmMain(new WorldData());
with the FrmMain constructor as
public FrmMain(WorldData worldData)
{
this.m_WorldData = worldData;
}
Have a look at Passing Data Between Forms
Assuming you're using just windows forms, and not XNA or similar framework, where theres no winforms.
Startup form:
void StartButton_Click(object sender, EventArgs e)
{
GameWorld gw = new GameWorld();
// Initialize gw instance here
GameForm mainForm = new GameForm(gw);
mainForm.Show();
}
And add a constructor to the game form:
public class GameForm
{
private GameWorld _gw;
public GameForm()
{
InitializeComponent();
}
public GameForm(GameWorld gw) : this()
{
_gw = gw;
}
}
At that point you can use private variable _gw in the game form.
Also, i would suggest passing the GameWorld instance through the constructor, not the property as that value is crucial for the form. Generally properties might be better suited to provide a way to adjust some behavior, and any constructor parameter can be seen as mandatory for the object to work as it should.
You can also make default constructor (the one without parameters) private.
Depending on the size of the data and if the world object class is serializable, You might think about caching it. Then each form that needs the data can just get it from the cache whenever it is needed.
Related
I am new to C# and i am making a Tic-Tac-Toe game in Visual Studio 2010. I've already made the whole structural part of it, but i am having trouble loading images from pictureBoxes. The command i am using is pictureBox.Load(string imageUrl), and it works fine when i use it on the method TicForm_Load, since TicForm is a Form:
private void TicForm_Load(object sender, EventArgs e)
{
//picture1 is the name of a pictureBox created on TicForm.cs (Design)
picture1.Load(imageUrl);
}
However, in my game, the changes to the images in the pictureBoxes are controlled by a class called Board. I want to be able to change images from this class.
I've already set the modifiers of the pictureBoxes to public, yet, if i want to access a command from TicForm, the program asks me to instantiate an object of the type TicForm first:
//Somewhere on the class Board
TicForm ticform = new Ticform();
ticform.picLocation00.Load(imageUrl);
This, however, causes an exception StackOverflow, since i create a new Board, at the start of the TicForm class, and i create a new TicForm, at the start of the Board class.
I would really appreciate an answer from someone with more experience / knowledge in Visual Studio than me. How do you normally change the images from the pictureBox in run-time? Am i doing something wrong?
It's been solved. Instead of using a method from TicForm into a class (which would never work), i created new variables on the class that indicate in what circustamces the method is to be used, and this happens inside the TicForm class.
I have my WinForm (Form1) and another class (Class1). Form1 has a textbox with the name of TextBox and I'm wanting to change the text inside TextBox within Class1. How can I do this? I've already checked everywhere and tried all the methods on Stackoverflow and other sources but they all seem to not be working.
EDIT:
I've tried making the TextBox public, but that didn't work.
Then I tried using the following code:
private readonly Form1 form;
public Class1(Form1 form) {
this.fom = form;
}
private void EditText() {
form.TextBox.Text = .... (TextBox seems to not be an object, even though it is)
}
Which also ended up not working.
Finally once I set the TextBox component modifiers to public inside the visual studio editor it gave me no errors until I ran the program in which it said:
Error 1 Inconsistent accessibility: field type 'NSTextBox' is less accessible than field 'OverloadBot_Panel.Form1.ConsoleTextBox' D:\Users\goff-laptop\Desktop\OverloadBotNet\OverloadBot_Panel\OverloadBot_Panel\Form1.Designer.cs 364 26 OverloadBot_Panel
I am using a Theme if that matters at all.
Basically, you need to do this the right way. Or at least a better way than exposing the control directly.
In order to keep tight encapsulation, as well as separation of concerns, you shouldn't be exposing your controls to the outside world anyways. Instead, define a method on your form class that sets the text box for you:
public void UpdateText(string text)
{
myTextBox.Text = text;
}
And in the caller:
private void DoStuff()
{
form.UpdateText("Test");
}
Now the caller doesn't need to know about the internal structure of the form. In fact, if you went through an interface instead, it wouldn't care if it was event talking to a form. The class on the other side would simply react to the "UpdateText" message in whatever way it needed to. This design is much better in addition to fixing your problem.
For the life of me I can't find a solution for this:
I have a class set up that does several things, one of which is manipulate wpf windows. One of these windows is the MainWindow.xaml.
So for instance I want this class to close the MainWindow.xaml, that is if for example a method is called from within OtherClass.cs.
Basically: I want to access unreferenced methods in the codebehind of a window (MainWindow.xaml.cs) from a distant class, though within the same project (Visual Studio).
When I use the MainWindow (using MainWindow or imports MainWindow in vb.net) and use its members for closing the window, as so:
using MainWindow
Public class OtherClass
{
// OtherClass is instanced elsewhere and then is decided to close MainWindow.
// So I imagine doing the following:
private void CommandClose()
{
CloseMainWindow();
}
...
}
And inside MainWindow.xaml.cs:
Public class MainWindow
{
Public void CloseMainWindow()
{
this.close();
}
...
}
Though I receive the following error: "Reference to a non-shared member requires an object reference".
Meaning I should do something like:
using MainWindow
Public class OtherClass
{
MainWindow myWindow // Though now I have a new window :/
private void CommandClose()
{
myWindow.CloseMainWindow();
}
...
}
So to access any members within MainWindow.xaml.cs and not create a new instance I need them to be shared? But that would defeat the purpose of having it do anything to the window, because the close command wouldn't be available then!
I might be overlooking some things, but atm I can't seem to notice it.
Would anyone be so kind as to show me a way to handle such a situation?
NB I am working in Windows Presentation Foundation if anyone was wondering.
To clarify further, here is a comment to one of the previous answers describing my situation:
What I am trying to do is run a command within another class, which is a public member of a WPF window. The window is already instanced and displayed. I just want to access its public members so I can manipulate the window from another class for convenience sake. This OtherClass.cs of mine is a class that handles console actions. The console is a custom control situated in the MainWindow.xaml.cs. When the user types for instance: /close, I want the MainWindow.xaml to close, but not just that, for in the future I want to add more features.
You still need an object reference. And you're gonna have to use the application context for that.
Here's how you would get a list of references to whatever type of window you have open and also close them:
var windows = Application.Current.Windows.OfType<MainWindow>();
foreach (var window in windows)
{
window.Close();
}
You could call any other method you want on them, and you can search for any other type of window, but this is how you find the one you're specifically looking for.
I'm developing an appliaction that has 2 forms and I ran into a problem. When I create a new instance of a class in Form2, then close Form2, I lose the instance values. So, I've solved this using static class, is this the correct approach?
The class name is Matriz_de_registracion and I have a function within it that's called "solver" that assigns values to the class properties ("Double MR0" is one of the variables as an example)
here's the code in Form2 (see I don't use the "new" statement otherwise once I close form2 I loose the instance values..
private void btn_iniciar_registro_de_puntos_Click(object sender, EventArgs e)
{
Matriz_de_Registracion.solver(_pois);
}
Then I can reference, in Form1, one of the properties of the Class by just doing this:
Matriz_de_Registracion.MR0
Now, is this correct approach or static classes are used for something else? I just wan to reference the values of the MR0 variable across all my forms without having to pass the instances through forms every time I open/close forms.
This is not the right approach. You should copy the values from the form object after it has closed but before you get rid of it.
The problem with using static data anything other than extremely sparingly is that you will find it makes the application much harder to work on as it gets larger - for example, you wouldn't be able to open two instances of the form at the same time, as they'd tread on each other's data.
Anyone who's done any UI work with .Net and WinForms is very familiar with this type of code:
TestForm frm = new TestForm();
frm.ShowDialog();
I found myself wishing that a call to show a modal dialog was a little less verbose, more like a static call. Andf there is a simpler way. All you really need, in a simple case, is something like this:
new TestForm().ShowDialog();
Am i missing anything? Could there be any repercussions from this kind of shorthand? E.g. windows messages not handled/routed correctly, dialog's resources not disposed etc.?
Was working on this when i saw Ray's feedback:
i suppose an even shorter way would be to create a static member withing TestForm that creates an instance of itself and calls ShowDialog internally. So, this code:
public static DialogResult DoModal()
{
return new TestForm().ShowDialog();
}
could be invoked thusly:
TestForm.DoModal();
If you don't want to reuse the form object anywhere in your code you can just use the short form
new TestForm().ShowDialog();
If you want to do something later with that object then you must assign it to a variable.
Pretty simple. Hide form constructor (make it private), then add static factory method that would initialize new instance of the form and show it straight away.
For example see MessageBox source codes (Mono, if I'm not mistaken)
link
Typical use of ShowDialog should look like this:
using (Form form = new Form())
{
// here setup your form instance
if (DialogResult.OK == form.ShowDialog())
{
// here read data from form instance
}
}
Please be aware that ShowDialog() causes form instance to not dispose itself. You should dispose it manually once you are done with it - hence using clause See http://msdn.microsoft.com/en-us/library/w61zzfwe.aspx for details. In your scenario this should look like below:
public static DialogResult DoModal()
{
using (Form form = new TestForm())
return form.ShowDialog();
}
But this is useful only when you don't need to read any data back from your form instance. So the only scenario that fits here is message box. MessageBox.Show(...) methods utilize pattern you want to implement in your post.
in other scenarios forms are supposed to return data other than DialogResult back to application after they are closed. And that's why standard Form does not have static ShowDialog() or DoModal() methods. Static method should dispose temporal form instance. Such disposal would make impossible to read data back from form. What is more, in static method scenario there is no form instance to read data back from.
Putting all together, your method, to be compliant with guidelines, should look more like:
public static YourResultClass DoModal()
{
using (TestForm form = new TestForm())
if (DislogResult.OK == form.ShowDialog())
{
YourResultClass result = new YourResultClass();
// Here initialize YourResultClass instance with data from form instance
return result;
}
}
But that is very use case specific and quite far from your one-liner utility method.