Configure buttons on one form from another form - c#

I have an application that has a main form which has 10 buttons that are user configurable.
I have another form with which to configure those 10 buttons actions.
What is the best way for me to configure these 10 buttons?
Currently, I'm passing the 10 buttons as a List to the second form, and link them to the appropriate combobox tag.
The problem is if sometime in the future I decide to add a button or rearrange them, I'll have to rewrite the code. Anyone know a better way?
private readonly List<PictureBox> pictureBoxes;
private readonly List<ComboBox> comboBoxes;
public Shortcut_Buttons (List<string> stringsList, List<Button> sButtons)
{
InitializeComponent();
pictureBoxes = Controls.OfType<PictureBox>().ToList();
comboBoxes = Controls.OfType<ComboBox>().ToList();
if (pictureBoxes.Count != sButtons.Count)
throw new ArgumentException("Not enough Buttons/Pictureboxes.");
int i = 0;
foreach (var pictureBox in pictureBoxes)
{
pictureBox.BackgroundImage = sButtons[i++].BackgroundImage;
}
i = 0;
foreach (var comboBox in comboBoxes)
{
comboBox.DataSource = stringsList;
comboBox.Tag = sButtons[i++];
comboBox.SelectedIndexChanged += SetShortcutButton;
}
}

Accessing directly the form by getting instance?
Like:
private static MainForm _this = null;
public static MainForm Instance { get { return _this; } }
public MainForm()
{
_this = this;
...
}
Meanwhile in another place:
MainForm.Instace.somePublicComponents....

Related

C# Take combobox item from one form and add its name as text to another

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());
}

How to pass list values from one class to other form in C#?

Actually I have 3 forms, and one class , class(ReadXMLToEcasWindow) in which i defined the list
public List<string> ack_line_path = new List<string>();
From form 1 on button_click , form2 will popup,
inside form2 i am adding values to List under the function
private void add_path_after_successful_load()
{
int rowcount = Ecas_config_gridview.Rows.Count;
for (int i = 0; i < rowcount; i++)
{
XML_To_Ecas.ack_line_path.Add(Ecas_config_gridview.Rows[i].Cells[3].Value.ToString());
}
this.Hide();
}
//once the values got added form2 will be hidden, again **clicking on form1 button** , form3 under which i want to use these list values
private void btn_ECAS_Click(object sender, EventArgs e)
{
ECAS_WINDOW_FORM F_Ecas= new ECAS_WINDOW_FORM(this);
F_Ecas.Show();
}
There are multiple option by which you can send the values to another form in windows application.
1) Setting up properties:
ECAS_WINDOW_FORM F_Ecas= new ECAS_WINDOW_FORM(this);
F_Ecas.ack_line_path = this.ack_line_path;
F_Ecas.Show();
2) Send by method which answered previously:
ECAS_WINDOW_FORM F_Ecas= new ECAS_WINDOW_FORM(this);
F_Ecas.setYourList(list);
F_Ecas.Show();
3) Build a static class which will hold the values. You can access the values of the static class throughout any where in the application:
static class Holder
{
public static List<string> ack_line_path = new List<string>();
}
Set the holder value
ECAS_WINDOW_FORM F_Ecas= new ECAS_WINDOW_FORM(this);
Holder.ack_line_path = this.ack_line_path;
F_Ecas.Show();
Then access the holder value anywhere inside the application.
What about an easy way:
ECAS_WINDOW_FORM F_Ecas= new ECAS_WINDOW_FORM(this);
F_Ecas.setYourList(list);
F_Ecas.Show();
Many Ways to achieve this, simply the most obvious ways are:
A public method on Form 2 that you call it from the opener form after initializing form 2
Or, create another constructor for form 2 that accepts your list as input, and use this constructor when initializing form 2

Wait until user press enter in textbox in another form and return value

I am new to C# and I'm trying to do sth like this:
myList = list of 1000+ string values;
1.StartNewThreads(50); //50 is the numbers of new threads
2.DoSth1(next value from myList);
3.DoSth2();
4. var value = {
ShowNewImageForm(); //show only if not another ImageForm is displayed if another is show - wait
WaitUntilUserPressEnterInTextBox();
ReturnValueFormTextbox();
}
5.DoSth3();
6.StartNewThread();
For now I have:
foreach(String s in myList ) {
DoSth1(s);
DoSth2();
DoSth3();
}
And now I'm looking for ideas to points 1,3,6 Can You suggest me how to resolve this?
How to start 50 threads
How to get value from textbox in another form when user press enter
For your second question:
Just pass your parent form/owner form in the sub form's constructor and use that reference to pass/set the return value or (more elegant) define a event that will fire on the child form once the user presses the return key and then add an event handler in your parent form.
For start some Threads, you can use a Thread[]
public static void StartThreads(int n)
{
System.Threading.Thread[] threads = new System.Threading.Thread[n];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new System.Threading.Thread(new System.Threading.ThreadStart(DoThread));
}
}
public static void DoThread()
{
//do some here
}
In the second form you can define this handler:
public static void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
TextBox myText = (TextBox)sender;
//your code here...
}
}
Then in the first form:
textBox1.KeyDown += new KeyEventHandler(Form2.textBox1_KeyDown);

Limiting number of instances of windows in an MDI application

I want to restrict the user to create multiple instances of a form in an MDI application.
If one instance of that form is opened it must get focus. If it is not a new instance it must be created.
How can I do this?
You can do it like this.
Create a static Method:
public static Form IsFormAlreadyOpen(Type FormType)
{
foreach (Form OpenForm in System.Windows.Forms.Application.OpenForms)
{
if (OpenForm.GetType() == FormType)
return OpenForm;
}
return null;
}
And then when you create your child form.
frmMyChildForm frmChild1;
if ((frmChild1 = (frmMyChildForm)IsFormAlreadyOpen(typeof(frmMyChildForm))) == null)
{ //Form isn't open so create one
frmChild1= new frmMyChildForm ();
}
else
{ // Form is already open so bring it to the front
frmChild1.BringToFront();
}
You could use a singleton-pattern-approach, and let the form have an Instance-member-variable that keeps track of whether it's been initialized or not.
http://en.wikipedia.org/wiki/Singleton_pattern
Maybe something like this could help you
Form frmToCreate;
String strClassName=typeof(FormToCreate).Name
frmToCreate = GetForm(strClass);
if(frmToCreate == null)
{
//create the form here
}
frmToCreate.MdiParent = this; //supposing you are inside of the mainwindow (MDI window)
frmToCreate.Visible = true;
//other code goes here
where GetForm would be something like this
public Form GetForm(String type)
{
int i;
Form[] children = this.MdiChildren; //or mdiwindow.MdiChildren
for (i = 0; i < children.Length; i++)
{
if (children[i].GetType().Name == type)
{
return children[i];
}
}
return null;
}
If just a matter of playing with MdiChildren property.

Single instance form but not singleton

I cannot understand how this is possible. Please help!!
I have an app with a trayicon. I want a form to be show when the user double clicks the trayicon. I have a problem where it is possible to get 2 or more forms showing by quickly triple or quadruple clicking the trayicon. The reason I don't want a singleton is that I want the form to be released each time it is closed to save memory, maybe this is not a good idea?
I have a field called m_form1.
I have a method called ShowForm1;
I call the method ShowForm1 on the double-click of the TrayIcon.
private Form1 m_form1;
private void ShowForm1()
{
if (m_form1 == null)
{
Trace.WriteLine("*CREATE*" + Thread.CurrentThread.ManagedThreadId.ToString());
m_form1 = new Form1();
m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
m_form1.Show();
}
m_form1.BringToFront();
m_form1.Activate();
}
So when Form1 takes a while to construct, then it is possible to create 2 because m_form1 is still null when the second call arrives. Locking does not seem to work as it is the same thread both calls (I'm guessing the UI thread) ie the trace writes out *CREATE*1 twice (below).
[3560] *CREATE*1
[3560] *CREATE*1
Changing the code to include a lock statement does not help me.
private Form1 m_form1;
private object m_Locker = new object();
private void ShowForm1()
{
lock (m_Locker)
{
if (m_form1 == null)
{
Trace.WriteLine("****CREATE****" + Thread.CurrentThread.ManagedThreadId.ToString());
m_form1 = new Form1();
m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
m_form1.Show();
}
}
m_form1.BringToFront();
m_form1.Activate();
}
How should I handle this situation?
Thanks guys
Tim.
Have an additional boolean variable, "m_formUnderConstruction" which you test for before constructing the form, and which you set as soon as you've decided to construct it.
The re-entrancy makes all of this a little icky, unfortunately. I've removed the lock, as if this ever gets called from a different thread then you've got the nasty situation of trying to show a form from a different thread to the one it was constructed on.
private Form1 m_form1;
private bool m_underConstruction = false;
private void ShowForm1()
{
if (m_underConstruction)
{
// We're about to show it anyway
return;
}
m_underConstruction = true;
try
{
if (m_form1 == null)
{
m_form1 = new Form1();
m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
m_form1.Show();
}
}
finally
{
m_underConstruction = false;
}
m_form1.BringToFront();
m_form1.Activate();
}
Use Interlocked.Increment to change the nr of the tries. If it is 1, open the form, otherwise, don't. And use Interlocked.Decrement after the test or on form's close.
private int openedForms = 0;
private Form1 m_form1;
private void ShowForm1()
{
if (Interlocked.Increment(ref openedForms) = 1)
{
m_form1 = new Form1();
m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
m_form1.Show();
}
else
{
Interlocked.Decrement(ref openedForms);
}
if (m_form1 != null)
{
m_form1.BringToFront();
m_form1.Activate();
}
}
private void m_form1_FormClosed(object Sender, EventArgs args)
{
Interlocked.Decrement(ref openedForms);
}
Please see this, it handles all mouse event combinations for NotifyIcon as well as Form1.
More here: http://code.msdn.microsoft.com/TheNotifyIconExample

Categories