Basically, I have a settings window, and when you click "OK", it's suppose to apply settings to the main form (eg, set font of a control, etc), and then close.
frmmain frm = new frmmain();
frm.OLVAltBackColor = Color.Aquamarine ;
I tried that, but it only applies the settings to that instance, and you can see it if you do frm.Show();
I'm trying to make it so the already opened form has it's control's properties changed.
What you are trying to do is not working because you are creating a NEW instance of your main form and updating that rather than the first instance. It is possible to update the main form by keeping a reference to it in your settings form... but...
...it sounds like you are approaching this from the wrong direction.
Don't make the settings form dependent on the main form. Instead create the settings form from the main dialog.
class SettingsForm : Form
{
// You need to ensure that this color is updated before the form exits
// either make it return the value straight from a control or set it
// as the control is updated
public Color OLVAltBackColor
{
get;
private set;
}
}
In your main form
(I'm assuming some kind of button or menu click)
private void ShowSettingsClicked(object sender, EventArgs args)
{
using (SettingsForm settings = new SettingsForm())
{
// Using 'this' in the ShowDialog parents the settings dialog to the main form
if (settings.ShowDialog(this) == DialogResult.OK)
{
// update settings in the main form
this.OLVAltBackColor = settings.OLVAltBackColor;
}
}
}
Apply the property change to the form that already exists and is already shown instead of creating a new form and changing that one.
In this code you're creating a new instance of the frmmain. Any changes you make to that new object will happen in the new object, not the one you actually want to change.:
frmmain frm = new frmmain(); //Creating a new object isn't the way.
frm.OLVAltBackColor = Color.Aquamarine ;
What you're looking for is a way to call on the already existant frmmain class and change the property of that.
Edit, for example:
using System;
class Statmethod
{
//A class method declared
static void show()
{
int x = 100;
int y = 200;
Console.WriteLine(x);
Console.WriteLine(y);
}
public static void Main()
{
// Class method called without creating an object of the class
Statmethod.show();
}
}
Related
Ok so I'm attempting to create a simple game. In a nutshell it's a resource management game where the player will attempt to manage a thieves guild. In regards to running missions I've created a Thief class, a new instance of which is created when a new thief is recruited. I have coded within the thief class the ability to gain experience and level up.
Here's my specific problem:
I want the player to be able to select which thief/thieves to send on a mission. I have thought about it and figured that opening a new form and populating it with checkboxes is the easiest way to allow this. These checkboxes will be related to a List<thief> of thieves, the player then checks the thieves s/he wants to send and these are then stored in another List<thief> and passed on to the run mission function.
I've built a separate project with the intention of testing and playing around with this before putting it into the main program. The test project consists of two forms: The first (frmMain) with a textbox to hold the selected options and a button to open the second form (frmSelect). Currently I can open and populate the second form (frmSelect) but when I try to add the checked options to the textbox I simply...well can't.
So far I have tried directly accessing the textbox by typing frmMain.txtOptionsDisplay in the cs file of frmSelect but it causes the following error:
An object reference is required for the non-static field, method or
property
I tried to create a new form in frmSelect and make it equal to the active instance of frmMain with: Form frmTemp = frmMain.ActiveForm; and then alter the textbox using frmTemp as a go-between but that produced the error:
'System.Windows.Forms.Form' does not contain a definition for
'txtOptionsDisplay'.
Having searched both google and stackoverflow forums I've encountered answers that I either have never heard of (Threading) or answers that I kind've recognise but can't interpret the code pasted to make it relevant to my problem (delegates).
Any advice or pointers would be fantastic.
EDIT:
frmMain code:
public frmMain()
{
InitializeComponent();
selections.Add("Option 1");
selections.Add("Option 2");
}
private void btnClick_Click(object sender, EventArgs e)
{
frmSelectOptions.Show();
int length = selections.Count();
for (int i = 0; i < length; i++)
{
CheckBox box = new CheckBox();
box.Text = selections[i];
box.AutoSize = true;
box.Location = new Point(50, 50*(i+1));
frmSelectOptions.grpControls.Controls.Add(box);
}
}
public void updateText(string option)
{
txtOptionsDisplay.Text += option;
}
}
frmSelect code:
public List<CheckBox> selectedOptions = new List<CheckBox>();
Form frmTemp = frmMain.ActiveForm;
public frmSelect()
{
InitializeComponent();
}
private void btnSelect_Click(object sender, EventArgs e)
{
foreach (CheckBox box in grpControls.Controls)
{
if (box.Checked == true)
selectedOptions.Add(box);
}
this.Hide();
}
}
I hope this formats correctly... I'm kinda new and don't know how to indent. Oh look there's a preview...
Does this help?
Your problem is that controls defined within a form by default receive the private access identifier. Hence you could just add a property along the lines of
public ControlType ProxyProperty {
get {
return txtOptionsDisplay;
}
}
Besides from that you should think about wether what you're trying is actually a good solution. Manipulating forms from one to another will become a huge clusterfuck in terms of maintenance later on.
I'd suggest using the Singleton pattern for your frmMain. This will help safeguard you from accidentally launching another instance of frmMain and at the same time, will give you access to frmMain's objects. From there, you can either write accessors to Get your txtOptionsDisplay or you can make it public. Below is an example:
public class frmMain
{
private static frmMain Instance = null;
private static object LockObj = new object();
public static frmMain GetMain()
{
// Thread-safe singleton
lock(LockObj)
{
if(Instance == null)
Instance = new frmMain();
return Instance;
}
}
public string GetOptionsDisplayText()
{
return txtOptionsDisplay.Text;
}
}
public class frmSelect
{
private void frmSelect_Load(object sender, EventArgs e)
{
// Set whatever text you want to frmMain's txtOptionsDisplay text
txtDisplay.Text = frmMain.GetMain().GetOptionsDisplayText();
}
}
If you do go this route, don't forget to update Program.cs to use frmMain's singleton.
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Application.Run(new frmMain()); - Old method
Application.Run(frmMain.GetMain());
}
Im making a game with a menu that opens new forms for each level. When you complete a level, it is supposed to congratulate you through a message box (it does) and then enable the next level's button on the different menu form. So I accessed the designer and made the buttons public and tried:
new LevelMenu().button2.Enabled = true;
But that didn't work.
I also tried doing:
public event Action levelCompleted;
//then down lower i did (after it is declared that you won the level):
if (levelCompleted != null)
levelCompleted();
W1L1.levelCompleted += () => LevelMenu.button2.Enabled = true;
But that give me the error of:
An object reference is required for the non-static field, method, or property 'The_Levels.W1L1.levelCompleted'
The form i'm working with is "W1L1", and "LevelMenu" is the menu form. Thanks in advance
You basically need a reference to your instance of LevelMenu.
So when you create your "W1L1" form, you might just pass the LevelMenu to it.
public class W1L1
{
private readonly LevelMenu _levelMenu;
public W1L1(LevelMenu levelMenu)
{
this._levelMenu = levelMenu;
}
//Where you want to enable the button
this._levelMenu.button2.Enabled = true;
}
It's not the best solution, in an architectural way, but it works.
But it would be better if you create a more OOP way for enabling the button of the next level.
When you open the next level open it as a separate object as a new form. Now when the level is completed you can access the public controls on that form from the originating class. Something like this might help:
LevelMenu NextLevel = new LevelMenu();
public event Action levelCompleted;
if (levelCompleted != null)
levelCompleted();
NextLevel.button2.Enabled = true;
here's what I did I hope this helps
I created 2 Forms
Form1 = where menu is (buttons), Form2 = the game level (i.e. level 1)
then in Form2 I added an even LevelCompleted that will notify form1 that the player completed the level
//add this to form2
//the delegate
public delegate void LevelCompleted(Int32 level);
//the event
public event LevelCompleted LevelCompletedEvent;
then on Form1 (the menu form) when you create an instance of Form2 (which has the event) subscribe to it and create a handler, in my case I added it after i created the instance of Form2
private void button1_CLick(object sender, EventArgs e)
{
Form2 level1 = new Form2();
level1.LevelCompletedEvent += new Form2.LevelCompleted(level1_LevelCompletedHandler);
level1.Show();
}
//and this is the handler method
void level1_LevelCompletedHandler(int level)
{
//the logic for controlling the button states
// the level parameter can be used to indicate what is the current level completed.
if(level == 1)
{
button1.Enabled = false;
button2.Enabled = true;
}
}
Note: that in Form2 (the game level) I created a field gameOver that can be used if he did not complete the game
If in case he is permitted to go to next level, You must raise the event in this form to notify Form1 (the menu)
that he (the user) completed the level and Form1 will execute the method level1_LevelCompletedHandler(int level).
I know this is not well explained but I hope I can give you an idea on the event.
I have a form.
In that form I create an instance of a class on a new thread because it runs some long running logic. The form also gives the user the ability to cancel this logic/thread.
That class opens a new form if input is required.
The new form sometimes appears behind the other form.
I set a property on the class:
public Form ParentForm{get;set;}
I can now do:
MyForm form = new MyForm();
form.ShowDialog(ParentForm);
However I get a cross thread exception when calling ShowDialog(ParentForm).
I know I can use InvokeRequired somehow but not sure how on a property.
Thanks
UPDATE: Have tried doing this but still get exception:
MyForm form = new MyForm();
form.ShowDialog(GetParentForm());
private Form GetParentForm()
{
//You have to Invoke() so you can wait for the function to return and obtain its return value.
if (ParentForm.InvokeRequired)
{
return (Form)ParentForm.Invoke(new Func<Form>(() => GetParentForm()));
}
else
{
return ParentForm;
}
}
Your updated method (GetParentForm) won't work because you're wrapping the task of getting the reference to ParentForm in an InvokeRequired block. You could try wrapping the ShowDialog call in such a block instead, but I think you would still get the cross-threading error.
Your simplest fix would be to move the code that creates and shows the second form out of your class and into ParentForm. So instead of this:
MyForm form = new MyForm();
form.ShowDialog(ParentForm);
you would do this:
ParentForm.showMyNewForm();
and in ParentForm you would have this:
public void showMyNewForm()
{
MyForm form = new MyForm();
form.ShowDialog(this);
}
If MyForm needs to have a reference to the class on the other thread, you would just add a parameter to showMyNewForm() so that the reference to it can be passed in.
What you're trying to do here (creating and showing related, connected forms that are created on different threads) is really going against the grain of how forms are meant to be used in .NET.
you can add async method to a form.
Let's say like this:
public class MyForm : Form
{
public void ShowModalAsync()
{
this.Invoke(new Action(()=> {
ShowDilaog(..);
}));
}
}
and use this, like:
MyForm form = new MyForm();
form.ShowModalAsync(...);
Should work for you.
By the way, if your problem is only the fact that the window appears on bihind of others, try to make use of Form.TopMost property setting it to true. Having in mind that it, yes, will bring it infront of other forms, but not necessary infront of other topmost forms.
I have a c# windows form app which contains several forms.
Generally, for example, in form1, I create a instance of form2 and then
form1.hide();
form2.show();
But sometimes I want the previous form to show and dispose current form. How can I call the previous form?
Thanks in advance.
To answer your question, you need to maintain references in your views to each other. While this might work it's messy and error prone. It sounds like all your control logic is probably contained within your form class code and I would suggest moving away from that and separate your concerns.
Solving your form management issues becomes very simple if you create a controller class that, at a minimum, manages the creation and disposal of your forms in whatever way you see fit.
So your code sample would actually be launched from a controller class as something like:
public class FormsController
{
private Form form1 = new Form();
private Form form2 = new Form();
public void SwitchForms()
{
form1.hide();
form2.show();
}
}
For further edification checkout the MVC architectural pattern for cleanly working with data, biz logic and UI.
You might consider extending Form to include some properties/fields that allow you to access other forms. the Form class can be inherited from just like most other .Net classes.
You may also consider doing some of that management in the Program.cs file that is part of you project, if neither form is really supposed to be a child of the other.
If you inherit a new class for your form1 from Form and add a method like closeSecondForm you can have it close and dispose the second form.
There are probably a bunch of different ways to solve the issue. These are just a few.
If you set the new form's Owner to a reference to the current form, you can reference that Owner from the new form. You could also subscribe to the new form's Closed() event from the old form, with code to dispose it (though the form can dispose itself by overriding OnClosed, if it doesn't happen there anyway).
This logic should be handled in Program.cs. The Main() method initializes Form1. You want to take control there instead of passing control to the form.
Example:
static class Program
{
internal static Form1 MyForm1;
internal static Form2 MyForm2;
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
// Initialize Form1
MyForm1 = new Form1();
MyForm1.FormClosing += new FormClosingEventHandler(MyForm1_FormClosing);
// You may want to initialize Form2 on-demand instead of up front like here.
MyForm2 = new Form1();
MyForm2.FormClosing += new FormClosingEventHandler(MyForm2_FormClosing);
// Show Form1 first
MyForm1.Show();
// Now we need to occupy the thread so it won't exit the app. This is normally the job of Application.Run.
// An alternative to this is to have a third form you pass on control to.
while (true)
{
Application.DoEvents();
System.Threading.Thread.Sleep(10);
}
}
static void MyForm1_FormClosing(object sender, FormClosingEventArgs e)
{
// Do something, for example show Form2
MyForm2.Show();
// EXAMPLE: We only want to hide it?
e.Cancel = true;
MyForm1.Visible = false;
}
static void MyForm2_FormClosing(object sender, FormClosingEventArgs e)
{
// Do something, for example show Form1
MyForm1.Show();
// EXAMPLE: We only want to hide it?
e.Cancel = true;
MyForm2.Visible = false;
}
}
Since Program is static you can access MyForm1 and MyForm2 anywhere in that project by:
Program.MyForm1.Show();
Program.MyForm2.Hide();
If you plan to have many forms/complex logic I suggest moving this to a separate class. Also consider using a single form and rotate user controls inside it instead.
Form2 myform = new Form2();
myform.show();
this.hide();
You could do this in form1:
...
var form2 = new form2();
form2.Closing += (form2_Closing);
this.hide();
form2.show();
...
private void form2_Closing(object sender, System.EventArgs e)
{
this.show();
}
I'm new to C# and would like to allow to Windows forms to comminicate with each other. I googled bubbling in C# but it wasn't much help. What are some good ways I can learn bubbling?
EDIT: I want to have an options form that is shown/created when my user clicks on Edit->Preferances. I then want the settings the user changed in the options form to be relayed to the main form.
Two approaches:
Put properties on your preferences form and access them from the main form when the user clicks OK.
if (preferenceForm.ShowDialog() == DialogResult.OK)
{
this.Color = preferenceForm.UserSelectedColor;
//etc...
}
Send your preference form a delegate from the main form and let the preference form call it with the appropriate changes.
class FormSettings
{
object Color {get, set}
}
class MainForm
{
...
void ChangeSettings(FormSettings newSettings)
{ ... }
void EditPreferences_Click(...)
{
...
EditPreferencesForm editPreferences = new EditPreferencesForm(this.ChangeSettings)
editPreferences.ShowDialog();
}
}
class EditPreferencesForm
{
...
ChangeSettingsDelegate changeSettings;
FormSettings formSettings;
void OkButton_Click(...)
{
changeSettings(formSettings);
}
}
You don't state as much, but is the main form also the form that contains the Edit->Preferences menu? If so, you are already at the correct point in the code
// This is the event handler in the main form
private void mnuEditPreferencesClicked...
{
FrmPreferences frmPreferences = new FrmPreferences();
frmPreferences.ShowDialog(this);
// Preferences saved, implement changes to main form here
}
If the preferences form is not generated from the main form, fire off an event when the preferences form closes, and have the main form handle the event that way.