I've create the following class in Visual Studio 2010:
public class Bat : Form1
{
public int BatLocation;
public void draw()
{
Pen batPen = new Pen(Color.Black);
batPen.Width = 10;
playArea.DrawRectangle(batPen, BatLocation, (picPlayArea.Height - 30), 50, 10);
}
}
But when I try to create an instance of the class, I get a stack overflow exception, advising me to make sure that I don't have an infinite loop or infinite recursion. I have tried creating the instance two different ways, as below:
Bat bottomBat;
bottomBat = new Bat();
and
Bat bottomBat = new Bat();
But both ways return the same error when I try to run the program. I've also tried the class definition with and without the public modifier.
I'm pretty new to programming and have no idea what might be causing this problem. Am I doing something wrong?
Edit: The code for the Bat class is everything I have at the moment, haven't created a specific constructor for it... Didn't think I needed to?
Anyway, here is the Form1 class in its entirety:
public partial class Form1 : Form
{
// Define various objects for the game
public Graphics playArea;
Bat bottomBat = new Bat();
public Form1()
{
InitializeComponent();
// Create instances of objects
playArea = picPlayArea.CreateGraphics();
//bottomBat = new Bat();
// Delegate the mouseMove event for picPlayArea
picPlayArea.MouseMove += new MouseEventHandler(picPlayArea_MouseMove);
}
private void picPlayArea_MouseMove(object sender, MouseEventArgs e)
{
bottomBat.Location = e.X;
}
private void btnExit_Click(object sender, EventArgs e)
{
string msg = "Are you sure you want to exit?",
title = "Confirm Exit";
DialogResult res = MessageBox.Show(msg, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (res == DialogResult.Yes)
{
Environment.Exit(0);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
// This is where most of the functionality is executed within the game
playArea.Clear(Color.White);
}
private void btnStart_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
}
It appears you have combined inheritance and composition in an impossible sort of way. The base Form1 type has a field declared to be of the derived Bat type. In addition, it uses a field-initializer to initialize it to a new instance of that type. Clearly, you have a turtles-all-the-way-down issue: when you create a Bat (or a Form1 for that matter), the field-initializer will run - this will create an instance of another Bat, which in turn will create yet another Bat, and so on, ad-infinitum in theory. (in practice: until you run out of stack-space).
Here's a simple fix that should solve the stack-overflow issue, but may not be the most appropriate design in the 'big-picture':
public class Bat
{
public void Draw(Graphics playArea)
{
...
}
}
Notice how this type no longer subclasses Form1; it inherits directly from System.Object. Now neither the Form1 nor the Bat classes will exhibit infinite recursion when instances of them are created.
It's hard to suggest the best fix without knowing the ultimate aim here. I suggest you give some thought to the best way to design these classes. I would think you need to spend some time learning about the C# programming language, OO design, as well as WinForms specifics. I think you're actually looking to override the OnPaint virtual method here.
Often the cause is confusing a property with its backing variable.
Something along the lines of:
public class tmp
{
private int _x;
...
public int X(x)
{
X = x;
}
You have a simple problem.
your class Bat is derived from Form1, and in Form1 you create a new instance of Bat, which in turn is based on Form1 so that creates a new instance of Bat.... and so it repeats until your stack space is used up.
In general, Form1 probably should not know about the class Bat, and that all code that needs to know about Bat should be in the Bat class. However, in exceptional circumstances, you can solve this issue like this:
partial class Form1
{
public Form1(Bat _bat)
{
mBat = _Bat;
}
protected Bat mBat;
}
and on the Bat class
public class Bat : Form1
{
public Bat() : base(this)
{
}
}
Related
New C# and Entity Framework user. I want to put the results of a table into a C# list. I want to reference this list in multiple form events. I will cycle through the list from beginning to end throughout the life of the form.
Currently, I have this code (snippet):
public partial class FrmMain : Form
{
private readonly admEntities _admEntities = new admEntities();
public FrmMain()
{
InitializeComponent();
}
private void frmMain_Load(object sender, EventArgs e)
{
var exhibitor = _admEntities.Exhibitors.ToList();
// Put the first value in a text box - this works
txtCurrentSaleOrder.Text = exhibitor.First().SaleOrder.ToString();
}
}
I was hoping to use the exhibitor variable elsewhere on other events. However, I am unable to reference it.
I have not been successful in my searching and coding attempts to resolve this problem so far. Any pointer in the right direction would be appreciated.
One thing I tried was to put this code in a class above FrmMain(). That also failed. Probably due to not fully understanding classes yet.
You should make exhibitor a field, which is a fancy way of saying declare it outside of a method, inside of your class. Here is an example where exhibitor can be accessed from anywhere else in the class.
public partial class FrmMain : Form
{
private readonly admEntities _admEntities = new admEntities();
//It's unclear what exhibitor is in your question, so I'm using generic code that will allow it to work regardless.
private Type exhibitorType = typeof(admEntities.Exhibitors);
private List<exhibitorType> exhibitor = new List<exhibitorType>();
public FrmMain()
{
InitializeComponent();
}
private void frmMain_Load(object sender, EventArgs e)
{
//Can reference here
exhibitor = _admEntities.Exhibitors.ToList();
txtCurrentSaleOrder.Text = exhibitor.First().SaleOrder.ToString();
}
private void AnotherMethod()
{
//Can reference exhibitor here too.
}
}
So, I'm currently working on a game library app, where you can add and organize games that are a part of your game library. So far I have three textboxes (one for game title, developer, and publisher), one checklistbox (to select which platform or system the game is on), three buttons ("Save Changes", "Add", and "Delete"), and finally a listview where the title of the game will be displayed whenever the user enters in the information and clicks the "Add" button. I was able to get it to work with no problems using the Form1.csclass, but figured it'd be best to organize the code using classes, which I've NEVER done with Windows Forms before (so I'm kinda a noob at this). For consistency reasons for this post, I made a saveAddDelete.csclass that has all my code for the "Save", "Add", and "Delete" buttons, which I transferred over from Form1.cs. I then call the saveAddDelete.csclass in Form1.csunder the designated button click events. There are no errors, but when I run the program, I enter in the information and click on the buttons, but they do nothing. So I think I'm either missing code or misunderstanding how classes work in Windows Forms. Here's the code below, and if anyone has any helpful feedback that would help me, I'd greatly appreciate it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GameLibrary
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public List<Library> Game = new List<Library>();
public Library lib = new Library();
public void saveBttn_Click(object sender, EventArgs e)
{
saveAddDelete save = new saveAddDelete();
save.Save();
}
private void button1_Click(object sender, EventArgs e)//Add Button
{
saveAddDelete add = new saveAddDelete();
add.Add();
}
private void deleteBtnClick_Click(object sender, EventArgs e)
{
saveAddDelete delete = new saveAddDelete();
delete.Delete();
}
public class Library
{
public string gametitle
{
get;
set;
}
public string developer
{
get;
set;
}
public string publisher
{
get;
set;
}
public string platform
{
get;
set;
}
}
class saveAddDelete
{
Form1 f = new Form1();
public void Save()
{
if(f.gameList.SelectedItems.Count == 1)
{
f.Game[f.gameList.SelectedItems[0].Index].gametitle = f.titleText.Text;
f.Game[f.gameList.SelectedItems[0].Index].developer = f.developerText.Text;
f.Game[f.gameList.SelectedItems[0].Index].publisher = f.publisherText.Text;
f.Game[f.gameList.SelectedItems[0].Index].platform = f.platformCheckBox.CheckedItems.ToString();
}
}
public void Add()
{
f.lib.gametitle = f.titleText.Text;
f.lib.developer = f.developerText.Text;
f.lib.publisher = f.publisherText.Text;
f.lib.platform = f.platformCheckBox.CheckedItems.ToString();
f.Game.Add(f.lib);
f.gameList.Items.Add(f.lib.gametitle);
f.titleText.Clear();
f.developerText.Clear();
f.publisherText.Clear();
foreach(int i in f.platformCheckBox.CheckedIndices)
{
f.platformCheckBox.SetItemCheckState(i, CheckState.Unchecked);
}
}
public void Delete()
{
Remove();
f.titleText.Clear();
f.developerText.Clear();
f.publisherText.Clear();
foreach (int i in f.platformCheckBox.CheckedIndices)
{
f.platformCheckBox.SetItemCheckState(i, CheckState.Unchecked);
}
}
void Remove()
{
try
{
f.gameList.Items.Remove(f.gameList.SelectedItems[0]);
f.Game.RemoveAt(f.gameList.SelectedItems[0].Index);
}
catch { }
}
}
Your helper class does not have reference to real form clicks are performed on (and hence do not get any values from the form).
One option - pass Form1 to each call instead of new'ing up Form1 in saveAndDelete class:
class saveAddDelete
{
// Form1 f = new Form1(); - delete that line
public void Save(Form1 f) {....} // pass Form1
and in each event handler pass this:
public void saveBttn_Click(object sender, EventArgs e)
{
saveAddDelete save = new saveAddDelete(this);
save.Save();
}
Note that you may consider using static methods for Save depending on your goals.
In your Form1 code, you create a new child object of type SaveAddDelete, but when you use the methods you created, you aren't passing any data into them. Instead, you are creating a new (empty) instance of Form1, which doesn't have any data to save or delete, and if you make any modifications to that new Form1, the original version of Form1 is not able to see those changes. You wind up with 2 different Form1's, neither of which know anything about the other one.
If you want to do it this way, you will need to pass a reference to the existing Form1 to the SaveAddDelete class:
public void saveBttn_Click(object sender, EventArgs e)
{
saveAddDelete save = new saveAddDelete();
save.Save(this);
}
and your SaveAddDelete methods will have to be expecting to receive an object of type Form1:
public void Save(Form1 f)
{
if(f.gameList.SelectedItems.Count == 1)
{
f.Game[f.gameList.SelectedItems[0].Index].gametitle = f.titleText.Text;
f.Game[f.gameList.SelectedItems[0].Index].developer = f.developerText.Text;
f.Game[f.gameList.SelectedItems[0].Index].publisher = f.publisherText.Text;
f.Game[f.gameList.SelectedItems[0].Index].platform = f.platformCheckBox.CheckedItems.ToString();
}
}
Notice that in the top sample, I pass "this" to the method, which is C# code for "whatever class is calling the code." In this case, "this" refers to your existing Form1 object. In the bottom sample, notice that the save method expects a Form1 object named "f" as a parameter. It does not create a new Form1 - it uses the one you pass to it.
For this example, I would actually just leave all of those methods in your Form1 class, since they are all related to the controls in Form1. New classes are generally used to model real-world objects - not to break out various pieces of code related to an existing class. Your "Library" class is a correct use of a separate class, since each instance of it represents a different game, and you have more than one of them that you need to keep track of separately (although I would call the class "Game" instead of "Library", since each instance of the class represents a single game, whereas the word library implies a collection of games.)
I am attempting to use Delegates to pass data between two Windows. I want a list of People to appear when the user clicks a button on the main Window, this will then allow the user to select a Person. After doing this it will fill in a TextBox on the original form with the Person name.
I started off much simpler than this however to get my head around Delegates and I'm having issues understanding how to call methods on separate Windows with the Delegates. This is what I have attempted so far;
The method on the main Window that waits for a string (ShowMessage);
public static DelegateTestWindow.TestDelegate ShowDelegateMessage(string message)
{
MessageBox.Show(message);
return null;
}
The second Window that contains the Delegate;
public partial class DelegateTestWindow : Window
{
public delegate string TestDelegate();
public DelegateTestWindow()
{
InitializeComponent();
}
private void TestDelegateClick(object sender, RoutedEventArgs e)
{
var tDelegate = new TestDelegate(CompanyManagement.ShowDelegateMessage("Test"));
this.Close();
}
}
This does show a message saying "Test" however it also throws an Exception after doing so: Delegate to an instance method cannot have null 'this'.
The method on the MainWindow especially does not look correct to me, however after trying different options this is the closest I've got to a working Delegate. Could someone explain where I have (clearly) gone wrong here and how I can improve the system I've got now?
Like Mat linked, events are a special kind of delegate, which is probably what you are looking for.
I've written a small example, hopefully this is enough for you to resolve your issue.
public partial class Form1 : Form
{
public event EventHandler PersonSelected;
public String PersonName { get; set; }
public Form1()
{
InitializeComponent();
PersonName = "Person's name";
}
protected override void OnClick(EventArgs e)
{
Form2 form2 = new Form2(this);
form2.Show();
PersonSelected(this, EventArgs.Empty);
}
}
public partial class Form2 : Form
{
public Form2(Form1 form)
{
InitializeComponent();
form.PersonSelected += (sender, args) =>
{
Form1 form1 = sender as Form1;
if(form1 != null)
textBox1.Text = form1.PersonName;
};
}
}
Now, this can be done differently, and better than this, however I think this should suffice. When you get this to work, I recommend you look into another way of doing this. Typically you will create a PersonEventArgs class which inherits from EventArgs. This class then holds a reference to the person. Here is a decent link with an example:
https://msdn.microsoft.com/en-us/library/system.eventargs(v=vs.110).aspx
Good luck :)
first post of mine here and hoping to get a little bit of help for a problem i seem to can't figure out. For an assignment I am supposed to do, I need to create a game with 2 player attacking each other. I want the users to be able to upgrade their stats after each round, but upgrade them in a form separate from the game form (well call them UpgradeForm and GameForm, respectively).
I am keeping the majority of the players' data in class Helicopters in a List (List Heli = new List();) My biggest problem is trying to call that list from the GameForm into my UpgradeForm. Here is what I've got for code so far:
public partial class GameForm : Form
{
public List<Helicopter> Heli = new List<Helicopter>();
private void GameForm_Load(object sender, EventArgs e)
{
Heli.Add(new Helicopter(1, this));//give helicopter player # and set a form variable to the current GameForm
Heli.Add(new Helicopter(2, this));
}
private void GameForm_to_UpgradeForm()
{
newForm = true;
UpgradeForm UF = new UpgradeForm(this);
UF.Show();
FormProvider.GameForm.Hide();
//FormProvider is how i'm calling my GameForm.
//Shouldn't have an effect on what I'm trying to do.
}
}
In my UpgradeForm:
public partial class UpgradeForm : Form
{
private GameForm gF;
List<Helicopter> HeliList = new List<Helicopter>();
public UpgradeForm(GameForm gForm)
{
InitializeComponent();
this.gF = gForm;
HeliList = this.gF.Heli;
}
}
However, I get an error:
"Error 1 Inconsistent accessibility: field type 'System.Collections.Generic.List' is less accessible than field 'Project.GameForm.Heli'
What am i doing wrong, and what do i need to fix? Will post the code to FormProvider class if needed. Thanks in advance.
modifyyour UpgradeForm:
public partial class UpgradeForm : Form
{
private GameForm gF;
List<Helicopter> HeliList = new List<Helicopter>();
public UpgradeForm(List<Helicopter> list)
{
InitializeComponent();
HeliList.AddRange(list);
}
}
and from the GameFprm class:
private void GameForm_to_UpgradeForm()
{
newForm = true;
UpgradeForm UF = new UpgradeForm(Heli);
UF.Show();
FormProvider.GameForm.Hide();
}
"What am i doing wrong, and what do i need to fix?"
please don't pass an object of an ENTIRE form just for a list with 2 items
make your own classes and make instances of them without involving UI components
I almost never used (advanced, or at all) graphical interfaces, or one simple form with simple controls... but this time I've got something a little more complex, and I don't have much experience with GUI.
I have one main form (and possibly more in the future) from which other sub-forms open (and they might have sub-forms of themselves) and I wonder what is, in your opinion, the best way to communicate between them?
I thought of passing the main form as a parameter to the constructors of the sub-forms, but it doesn't seem like a good way, especially if I'm going to need to communicate between other, distinct, sub-forms, not to mention I have to double check the input, or make a few methods, but it seems more like functional programming than object oriented programming...
Perhaps I can:
Create a static class (or Properties.Settings) for global settings. Cons: every change of data is needed to be copied to the class, I'm looking for something a bit more comfortable and elegant.
Use the ugly way of accessing the controls from Application.OpenForms - fixes the problem of passing the main form as parameter. Cons: not very stable.
Do something else I haven't thought of. Suggestions? Cons: don't know what it is yet.
Your constructor idea is probably the most sound method of communication back to the main form. Your sub form would do something like the following:
public class SubForm : Form
{
public SubForm(MainForm parentForm)
{
_parentForm = parentForm;
}
private MainForm _parentForm;
private void btn_UpdateClientName_Click(object sender, EventArgs e)
{
_parentForm.UpdateClientName(txt_ClientName.Text);
}
}
And then you expose public methods on your MainForm:
public class MainForm : Form
{
public void UpdateClientName(string clientName)
{
txt_MainClientName.Text = clientName;
}
}
Alternatively, you can go the other way around and subscribe to events from your SubForms:
public class MainForm : Form
{
private SubForm1 _subForm1;
private SubForm2 _subForm2;
public MainForm()
{
_subForm1 = new SubForm1();
_subForm2 = new SubForm2();
_subForm1.ClientUpdated += new EventHandler(_subForm1_ClientUpdated);
_subForm2.ClientUpdated += new EventHandler(_subForm2_ProductUpdated);
}
private void _subForm1_ClientUpdated(object sender, EventArgs e)
{
txt_ClientName.Text = _subForm1.ClientName; // Expose a public property
}
private void _subForm2_ProductUpdated(object sender, EventArgs e)
{
txt_ProductName.Text = _subForm2.ProductName; // Expose a public property
}
}
A good way is to declare delegates in the form that want to start the communication. You need a delegate and a callback function:
public delegate void SetValueDelegate(string value);
public SetValueDelegate SetValueCallback;
Another form can then attach to this delegate. At that moment both forms have to know each other, but not after that moment:
firstForm.SetValueCallback += new SetValueDelegate(secondForm.SetValueFunction);
The second form has to declare a function that matches the delegate definition:
public void SetValueFunction(string value)
{
// do something
}
Now the first form can use the delegate to use the function of the second form (and all other forms or classes that were attached to the delegate:
SetValueCallback(txtParam.Text);
Edit: made an complete example
using System;
namespace DelegateTest
{
public delegate void SetValueDelegate(string value);
public class Class1
{
public SetValueDelegate SetValueCallBack;
public void Test()
{
if(SetValueCallBack != null)
{
SetValueCallBack("Hello World!");
}
}
}
public class Class2
{
public void SetValueFunction(string value)
{
Console.WriteLine(value);
}
}
public class Launcher
{
public static void Main(string[] args)
{
Class1 c1 = new Class1();
Class2 c2 = new Class2();
c1.SetValueCallBack += new SetValueDelegate(c2.SetValueFunction);
c1.Test();
}
}
}
The most flexible, scalable (and IMHO the most professional) way to do it is to use CAB (Composite Application Block). In simple terms CAB is a set of 2-3 assemblies that implement a lot of plumbing required to make complex UI applications the right way and it exposes this plumbing to the user of the library in a nice way. Among others it has a very nice event and command (as in command pattern) system.
The downside: requires some time to learn and not very trivial to grasp.
Here is a comprehensive (but easy to understand) tutorial that will help you make the learning easier.
You can use the built in Tag property of the form which is an "object" class.
public Form1()
{
(ComplicatedDataStructure)Tag = new ComplicatedDataStracture();
}
.
.
form1 = new Form1();
.
.
form2 = new Form2();
.
.
form2.Tag = form1.Tag;
so form2.Tag is equals to "ComplicatedDataStracture" object;