Accessing form controls from a different class - c#

I am creating a windows form application using c#. My form has labels, list-boxes and buttons, all of which need to be edited. They are edited from a different class, and further will need the values in the labels to perform other logic.
My problem is accessing these controls from other classes. I tried making an instance of the form and using it like this:
Form frm = new Form();
myVar = frm.lblMylabel.Text;
However, using a breakpoint, I followed the code, and it loops between the above instance being created, and the start of the Form code, which calls the class again where the instance was created.
I have tried to find the answer online, however they don't seem applicable to what I am trying to do, and whilst I am unsure how to do it, they appear to be overly complex.
So, my question it: is there a relatively simple way to be able to access values and edit values from controls in a form from another class?
Thanks in advance.
EDIT - Breakpoint starts at Point 3, '=new Form();'.
Loops to Point 1.
Point 2 calls GamePlay().
Reaches Point 3 and loop restarts.
public partial class frmGame : Form //Point 1
{
public frmGame()
{
InitializeComponent();
Game.GamePlay(); //Point 2
}
class Game{
public static void GamePlay()
{
frmGame form = new frmGame(); //Point 3
form.lstPrevious1.Items.Add("Item Number");
}
}

Your problem has nothing to do with accessing properties of a different class, you are just producing a recursive loop:
At Point 3, within the method GamePlay you create a new instance of frmGame. When creating an instance (an object), its constructor is called. In this case, your constructor is the method public frmGame().
Within this constructor, you now call GamePlay (Point 2), which, as we saw from before, again creates a new instance of frmGame (Point 3 again), also again calling its constructor (Point 1).
At this point, you have already two forms created (although not shown yet).
Very soon you will receive a stack overflow.
One possible solution: Move the line creating your instance into a different method that is guaranteed to be called only once at program start:
public partial class frmGame : Form //Point 1
{
public frmGame()
{
InitializeComponent();
Game.GamePlay(); //Point 2
}
}
class Game{
public static void GameStart(){
frmGame form = new frmGame(); //Point 3
}
public static void GamePlay()
{
form.lstPrevious1.Items.Add("Item Number");
}
}

Related

Moving Between One Form to Another - C#

I am currently creating a video game and coding the movement in a house between upstairs and downstairs. I'm using PictureBoxes in combination with an IntersectWith event to transition between forms.
Transition Code to go Upstairs:
if(picPlayer.Bounds.IntersectsWith(picUpstairsTransition.Bounds))
{
MapFrmHouseUpstairs upstairs = new MapFrmHouseUpstairs();
this.Hide();
upstairs.ShowDialog();
}
Transition Code to Go Back Downstairs:
if(picPlayer.Bounds.IntersectsWith(picGoDownstairs.Bounds))
{
MapFrmHouse goDownstairs = new MapFrmHouse();
this.Hide();
goDownstairs.ShowDialog();
picPlayer.Location = new Point(497, 103);
}
The issue I have is that when the player enters the house, he starts at the front. When he tries to come back from upstairs, the character is moved back to the front instead of the base of the stairs. Is there anyway I could create a method within MapFrmHouse such as:
public void fromDownstairs{picPlayer.Location = new Point(x,y);}
And call it when going downstairs?
First of all you'd better use a game engine to design games like Unity which is much easier and more fun. This might be a learning task for you though which I don't blame :)
You can send information between forms in Windows Forms in many ways, two of which seems to fulfill your needs:
Using a higher level modifier for objects
Initializing values in constructors
To do the first you set the modifier property of the picture box called picPlayer in your MapFrmHouse class from private to public then you can do exactly what you mentioned:
if(picPlayer.Bounds.IntersectsWith(picGoDownstairs.Bounds))
{
MapFrmHouse goDownstairs = new MapFrmHouse()
{
picPlayer.Location = new Point(x,y);
};
this.Hide();
goDownstairs.ShowDialog();
}
For the second method you should create an overload constructor in your MapFrmHouse and accept a Point value in it and then set it your value like this:
public MapFrmHouse(Point p)
{
InitializeComponent();
picPlayer.Location = p;
}
And then use it in your code like this:
if(picPlayer.Bounds.IntersectsWith(picGoDownstairs.Bounds))
{
MapFrmHouse goDownstairs = new MapFrmHouse(new Point(x,y));
this.Hide();
goDownstairs.ShowDialog();
}
I hope this helps :)

Method return value resets value after changing into another form

I have a form, Form2 which contains a Hangman game (yes, it is the similar Hangman game to the one that i'm referring to in my previous question) and once the player finishes the game, the player will be brought to either one of two forms, Form4 and Form6 (one is a congratulations form for winning and one is a mocking/defeat form for losing) and in both forms Form4 and Form6, there is a label that displays the score that the player gets from the previous Form2.
I had placed a method to return the value of the score in Form2.
public int getScore()
{
return score;
}
And then in both forms, Form4 and Form6, there is these codes in each form respectively.
Form4
private void Form4_Load(object sender, EventArgs e)
{
Form2 game = new Form2();
lblFinalScore.Text += game.getScore().ToString();
}
Form6
private void Form6_Load(object sender, EventArgs e)
{
Form2 game = new Form2();
lblFinalScore.Text += game.getScore().ToString();
}
So after passing Form2 and playing the game, and when for example, I won the game and got 7 points, and Form4 appears, the lblFinalScore displays 0... WHY?!
Please help...
If you use the new keyword you create a new instance of the Form2 class.
This way you are using two different instances. Differnt instances are having data fields with different values.
If you want to access the same data field from any intances of a class, use the static keyword. You could store the score value in a static field, this way you could easily access it.
If you have a static field defined in a class, you can access the very field from any instance. However, using static fields are highly discouraged, since this violates basic OO principles.
Example:
class Program
{
static void Main(string[] args)
{
ExampleClass ex1 = new ExampleClass();
ExampleClass ex2 = new ExampleClass();
ex1.normalfield = "new value for ex1";
ex2.normalfield = "new value for ex2";
ExampleClass.staticfield = "static value";
Console.WriteLine(ex1.normalfield);
Console.WriteLine(ex2.normalfield);
Console.WriteLine(ExampleClass.staticfield);
}
class ExampleClass
{
public string normalfield = "";
public static string staticfield = "";
}
}
Passing the original instance of the Form could be a better way to go in your situation.
This could be done by storing a reference to the Form, or implementing the Singleton pattern.
However, the best solution in my opinion, would be to decouple the presentation of your game logic. You should be using a different class to implement the game logic and to store data values connected to the game and the form classes should be only responsible to present visually the current state of the object representing the state of your game.
You are creating two different instances of Form2. Refer to actual original instance of Form2.
Have class level variable on both Form4 and Form6 and set it from respective constructor. Pass instance from Form2.
Refer to Passing a value from one form to another.

C# - Update GUI datagridview from another thread using delegate

I'm trying to update a datagridview with some data calculated in a different class and thread, using a delegate. Unfortunately I'm having trouble with a variety of different errors, depending on the approach I try.
The code I am trying to execute in the form thread looks like this:
public partial class AcquireForm : Form
//
// ...
//
// update the grid with results
public delegate void delUpdateResultsGrid(int Index, Dictionary<string, double> scoreCard);
public void UpdateResultsGrid(int Index, Dictionary<string, double> scoreCard)
{
if (!this.InvokeRequired)
{
//
// Some code to sort the data from scoreCard goes here
//
DataGridViewRow myRow = dataGridViewResults.Rows[Index];
DataGridViewCell myCell = myRow.Cells[1];
myCell.Value = 1; // placeholder - the updated value goes here
}
}
else
{
this.BeginInvoke(new delUpdateResultsGrid(UpdateResultsGrid), new object[] { Index, scoreCard});
}
}
Now, I need to get this method to run from my other thread and class. I have tried:
public class myOtherClass
//
// ...
//
private void myOtherClassMethod(int myIndex)
{
// ...
AcquireForm.delUpdateResultsGrid updatedelegate = new AcquireForm.delUpdateResultsGrid(AcquireForm.UpdateResultsGrid);
updatedelegate(myIndex, myScoreCard);
}
Unfortunately this gives an "Object reference is required for the non-static field, method, or property AcquireForm.UpdateResultsGrid(int, System.Collections.Generic.Dictionary)" error. I seem to be unable to reference the UpdateResultsGrid method at all...
I have noticed that
public class myOtherClass
//
// ...
//
private void myOtherClassMethod(int myIndex)
{
// ...
AcquireForm acquireForm = new AcquireForm();
acquireForm.UpdateResultsGrid(myIndex,myScoreCard);
}
does not throw any errors when compiling, but it tries to create a new form and that is something I do not want to do. I don't want to create a new instance of AcquireForm, I want to reference the pre-existing one, if that's possible.
I have also tried making the UpdateResultsGrid method static, but this throws up problems with several things incuding the use of "this.(anything)".
I've also tried moving the majority of the UpdateResultsGrid method into myOtherClassMethod, leaving behind in the AcquireForm class just the delegate. Again, this does not work because many of the references to UI objects break (there aren't any dataGridViews in scope).
I'm starting to run out of ideas here. Unfortunately I'm rather new to C# (as you can probably tell), and I'm editing someone else's code rather than writing my own entirely from scratch. If anyone could offer some advice on this problem it'd be most appreciated.
Make sure your objects are communicating with each other: Your myOtherClass is going to have to know about the AcquireForm object - you can't just create a new one (as you've discovered). You'll need to pass the AcquireForm object into the myOtherClass object (myOtherObject.SetForm(myAcquireForm, for example) and reference it when you need to.
In case you're having issues with invoking this might be of help - how I invoke a "next" button click:
BeginInvoke(new Action(()=>button_next_Click(null,null)));
Moreover, it sounds like maybe this should not be separate classes and you should be utilising a BackgroundWorkder instead.

c# objects modification: a strange behaviour

I'm developing a WPF C# application and I have a strange behaviour in modification of objects. I try to explain it in general way.
Suppose that you have an object of a class described as follows:
public class A
{
int one;
bool two;
List<B> listofBObjects;
}
where B is:
public class B
{
int three;
int four;
}
I pass an instance of A class and an instance of B class from a window to another, only defining two variables of type A and B in the second window and passing them before the Show() method, with the following code, executed into an instance of window FirstWindow:
SecondWindow newWindow = new SecondWindow();
newWindow.instanceOfA = this.instanceOfA; //instanceOfA is of type A
newWindow.instanceOfB = this.instanceOfA.listOfBObjects[0]; //instanceOfB is of type B
newWindow.Show();
If I have to repeat this code twice(that is, opening twice the window), in the first execution everything works as expected, infact if I modify values in instanceOfB variable, I see the modification also in instanceOfA variable. But, in the second execution, the modification in instanceOfB does not affect instanceOfA...
The modifications are done in newWindow. For example:
this.instanceOfB.three++;
this.instanceOfB.four--;
Imagine that you are in the FirstWindow. Click on a button and SecondWindow opens, passing both variables as described above. In SecondWindow, do some modifications, click on OK and SecondWindow closes, returning control to FirstWindow. If I reclick on the same button, I reopen SecondWindow. If I do modifications now, they do not affect both variables.
I try to have a look (in VS2012) at both variables in the console with control expression and I see that, in the first pass of code, both variables changes when code above is executed but, in the second pass of code, only instanceOfB changes...
EDIT:
Following the code that I use to pass parameters to SecondWindow...types are explaind below
IntermediatePosition obj = ((FrameworkElement)sender).DataContext as IntermediatePosition; //IntermediatePosition is Class B
IntermediatePositionsSettingsWindow ips = new IntermediatePositionsSettingsWindow();
ips.currentIntermediatePosition = obj;//this is the instanceOfB
ips.idxOfIpToModify = obj.index;
ips.currentSingleProperty = this.currentPropertyToShow; //this is the instanceOfA object
ips.sideIndex = this.sideIndex;
ips.ShowDialog();
Consider that obj is given by a button selection into a datagrid, in which each row represents an IntermediatePosition object. In the datagrid, there is a column button and, clicking by buttons, IntermediatePositionsSettingsWindow is opened with the proper data
EDIT:
I've performed the folloqing check:
this.currentPropertyToShow.sides[this.sideIndex].intermediatePositionList[i].Ge‌​tHashCode() == obj.GetHashCode()
where i is the index of related IntermediatePosition object. At first usage of IntermediatePositionsSettingsWindow the objects result equals, but in second usage they are different
Why this thing happens?
If it is needed any other clarification, I will edit the question
Thanks
It's difficult to give a proper answer to this, as there is insufficient code to correctly work out the issue. However, if you are databinding, then I believe you need to implement this interface. It is possible that you're issue is simply that you're model is not reflecting the changes to the screen.
I can't reproduce your problem. Here's a simplified representation of your class relation (as I understood from your question). Please let us know if this is correct:
public partial class MainWindow : Window
{
internal A instanceOfA;
internal B instanceOfB;
public MainWindow()
{
InitializeComponent();
instanceOfB = new B() { };
instanceOfA = new A() { listOfBObjects = new List<B>() { instanceOfB } };
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SecondWindow newWindow = new SecondWindow();
newWindow.instanceOfA = this.instanceOfA; //instanceOfA is of type A
newWindow.instanceOfB = this.instanceOfA.listOfBObjects[0]; //instanceOfB is of type B
newWindow.Show();
}
}
public partial class SecondWindow : Window
{
internal A instanceOfA;
internal B instanceOfB;
public SecondWindow()
{
InitializeComponent();
Loaded += SecondWindow_Loaded;
}
void SecondWindow_Loaded(object sender, RoutedEventArgs e)
{
MessageBox
.Show(String.Format("{0}",
this.instanceOfB == this.instanceOfA.listOfBObjects[0]));
this.instanceOfB.three++;
this.instanceOfB.four--;
}
}
Note: this is not an answer, just trying to establish some common ground for further discussions, as comments don't leave you enough freedom for code samples.
Thanks to #pm_2 and #BillZhang comments, I found a row in my code in which this.currentPropertyToShowwas edited. After the returning back at first window, infact, I perform the refresh of the window, but it is not needed to edit this.currentPropertyToShow, so I have commented it and everything works!
Thanks everybody for precious comments and suggestions!

C# Newbie Question from tutorial book: "Head Start C# Greyhound Lab"

I'm an extreme newbie at C#, but I've been slowly moving through the Head Start C# tutorial book (and finding it extremely enjoyable so far). However, I've hit a wall on the first "lab" assignment: They give code for controlling a PictureBox, and I can get that code to work on the main Form, but I can't get it to work from within a Class. I've gone back over the old lessons, and I've got a fairly good idea of what I'm missing, but for the life of me I can't figure out how to access the main Form's PictureBox from within my class (as the tutorial is telling me I should do).
It's a bit frustrating, because I didn't jump ahead in the book at all, but I'd swear we haven't covered this yet. Anyway, appealing to Real programmers.
Here's the code provided in the tutorial, in a section called "Your object can control things on your form" (p208 for anyone with the book).
Point p = MyPictureBox.Location
p.x += distance;
MyPictureBox.Location = p
Below I'm posting the relevant (I think?) parts of my code below. Button1 works for me when compiled, Button2 "works," in the sense that the current class just tells it to print the passed INT because I've commented out the code I can't get to work.
Thanks in advance!
Code for the Form1:
//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
// Namespaces I'll need.
namespace Troubleshooting_PicBoxes
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent(); // Start all the Form1 stuff (all IDE-generated)
}
private void button1_Click(object sender, EventArgs e) //method from clicking the first button
{
int distance = 5; // Create this variable called "distance"
Point BoxMovement = MyPictureBox.Location; //create a point called BoxMovement
BoxMovement.X += distance; // Adjust the X of BoxMovement by my distance int.
MyPictureBox.Location = BoxMovement; // now adjust the Box by the Point's location.
}
private void button2_Click(object sender, EventArgs e)
{
PicMover PicMoverObject1 = new PicMover(); // Reserve Space for&Create object
PicMoverObject1.MoveThatPic(5); // Execute Object Method with a value of 5
}
}
}
Code for the PicMover class:
//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Troubleshooting_PicBoxes
{
class PicMover
{
public void MoveThatPic(int distance) // New method,
// takes a variable called Distance.
{
MessageBox.Show(distance.ToString()); // Just show us that Variable.
// I need to be able to access Form1's picture box before I can use this. :(
/* Point BoxMovement = MyPictureBox.Location; //create a point called BoxMovement
BoxMovement.X += distance; // Adjust the X of that by distance.
MyPictureBox.Location = BoxMovement; // now adjust the Box by the Point's location.
*/
}
}
}
If you need to access something, why don't you just give it access? Like passing it as a argument to the method.
public void MoveThatPic(PictureBox picBox, int distance) // New method,
// takes a variable called Distance.
{
MessageBox.Show(distance.ToString()); // Just show us that Variable.
// I need to be able to access Form1's picture box before I can use this. :(
Point BoxMovement = picBox.Location; //create a point called BoxMovement
BoxMovement.X += distance; // Adjust the X of that by distance.
picBox.Location = BoxMovement; // now adjust the Box by the Point's location.
}
now in button2 click event handler:
private void button2_Click(object sender, EventArgs e)
{
PicMover PicMoverObject1 = new PicMover(); // Reserve Space for&Create object
PicMoverObject1.MoveThatPic(MyPictureBox, 5); // Execute Object Method with a value of 5
}
The tutorial code LOOKS like you are grabbing the location from your class (MyPictureBox.Location) then changing the location, then moving your object to that new location.
Point p = MyPictureBox.Location // Save the location of your object
p.x += distance; // Increase the distance
MyPictureBox.Location = p // Set your object to the new location
The second button press event is different. Perhaps you should be returning a location from the function? So, when you call the function, you set the PictureBox on the main form to the value returned.
If you want to create a general purpose class that can be accessed from any Form ... that creates an instance of it ... to move any PictureBox on that Form, then 'Deerchao's answer shows you how to do it.
Although ... consider ... every Form is going to have declare its own instance of PicMover; Form1's instance of PicMover is not "visible" to any other Form unless you also somehow publish it.
To put it more technically : the PicMover class, as defined here, exists in the Scope of the Application NameSpace; it's a template for creating type of object that every other Class in the Application can use to make an instance of, but that no Class in the Application "has" an instance of by default.
Here's an alternative to Deerchao's excellent answer that demonstrates "injection" : injection is appropriate when you want an instance of a class to "hold onto" a reference : in this example we say that the Form's instance of a PictureBox gets "bound" to an instance of the 'PicMover class :
We declare a Public Property within the class 'PicMover that will hold a reference to Form1's PictureBox :
public class picMover
{
// note use of C# 3.0 automatic property feature here
public PictureBox myPictureBox { get; set; }
public void movePic(int distance)
{
// note test for null here
if (myPictureBox != null)
{
myPictureBox.Left += distance;
}
}
}
So in Form1 after you create the instance of 'PicMover, you set its internal PictureBox Property, and then use its internal 'movePic method : like this :
// instance of PicMover created in the Form's scope
picMover myPicMover = new picMover();
private void Form1_Load(object sender, EventArgs e)
{
// when the Form loads inject the reference to the PictureBox instance into the instance of 'PicMover
myPicMover.myPictureBox = pictureBox1;
}
private void button1_Click(object sender, EventArgs e)
{
myPicMover.movePic(23);
}
Note that, imho, testing to make sure an "injected" reference to an object exists, by testing for null before using it is a good habit to get into.
Another way you can get an instance of the PictureBox object "bound" into an instance of 'PicMover is to pass the instance of the PictureBox to the Constructor of the class as a parameter : I bet by the time I finish posting this, someone else will have already posted an answer showing that technique. You might want to "inject" using a Public Property as shown here when you expect the internal reference to be changed vs. passing the PictureBox in to the Constructor of the Class when you don't expect it to be changed.
Another strategy is to make 'PicMover a public static class, with public static methods : then every Form can "see it," and there's no need for any form to make an instance of it (in fact you can't "instance" a static class if you wanted to : that's what a static class is).

Categories

Resources