Link to home
Start Free TrialLog in
Avatar of TownTalk
TownTalkFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Passing a Form as an argument

I'm trying to duplicate an MsAccess app in C#.  In the Access App I have dozens of forms which have some contols which are common to them all. For Instance, they all have a NewRecordButton.  I want to call a global InsertNewRecord() function when the NewRecordButton is clicked. In MsAccess I pass the name of the form as an argument, but in C# I think I can pass a reference to the form. So in the form I have the code:

 private void NewRecordButtonClick(object sender, EventArgs e)
{
    MyFormClass.InsertNewRecord(this);
}

And in my class which houses my global routines I have this:

public class MyFormClass
{
    Public void InsertRecord(Form TheForm)
    {
        TheForm.NewRecordButton.Enabled=False;
    }
}

This doesn't work. So what am I doing wrong?
ASKER CERTIFIED SOLUTION
Avatar of it_saige
it_saige
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Now I had stated ideally.  Another way you could accomplish this with what you are doing is by searching through the controls on the form in your method and finding the button that matches the criteria (in this case Name).  However, be warned that this method is prone to be easily broken (e.g. - set the wrong name).  This also introduces the concept of extension methods.

Form1.cs -
using System;
using System.Windows.Forms;

namespace EE_Q28691014
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private void OnClick(object sender, EventArgs e)
		{
			this.InsertRecord();
			MessageBox.Show("We have disabled the new record button by using an extension method.");
		}
	}
}

Open in new window

Form1.Designer.cs -
namespace EE_Q28691014
{
	partial class Form1
	{
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.IContainer components = null;

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		#region Windows Form Designer generated code

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.NewRecordButton = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// NewRecordButton
			// 
			this.NewRecordButton.Location = new System.Drawing.Point(197, 227);
			this.NewRecordButton.Name = "NewRecordButton";
			this.NewRecordButton.Size = new System.Drawing.Size(75, 23);
			this.NewRecordButton.TabIndex = 0;
			this.NewRecordButton.Text = "New Record";
			this.NewRecordButton.UseVisualStyleBackColor = true;
			this.NewRecordButton.Click += new System.EventHandler(this.OnClick);
			// 
			// Form1
			// 
			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
			this.ClientSize = new System.Drawing.Size(284, 262);
			this.Controls.Add(this.NewRecordButton);
			this.Name = "Form1";
			this.Text = "Form1";
			this.ResumeLayout(false);

		}

		#endregion

		private System.Windows.Forms.Button NewRecordButton;
	}
}

Open in new window

Extensions.cs -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace EE_Q28691014
{
	static class Extensions
	{
		public static void InsertRecord(this Form form)
		{
			foreach (Control control in form.Controls)
			{
				if (control is Button)
				{
					Button btn = control as Button;
					if (btn.Name.Equals("NewRecordButton"))
						btn.Enabled = false;
				}
			}
		}
	}
}

Open in new window

Program.cs -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace EE_Q28691014
{
	static class Program
	{
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main()
		{
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.Run(new Form1());
		}
	}
}

Open in new window

Produces the following output -User generated imageUser generated image-saige-
Avatar of TownTalk

ASKER

Thank you so much for the comprehensive replies!

I like the idea of using the base form. But nothing like this exists in vb, so I have to learn some new concepts here....

The first thing I found is that on Form1, the btnNewRecord is in a fixed location presumably because it is inherited from BaseForm. In my MsAccess app although I have dozens of forms each with 11 common buttons, the forms are different sizes/shapes according to the nature of the data in there. So I definitely need to move the buttons around and preferably resize them. Is there a workaround for this?

Edit: I found the answer to this. In the BaseForm I changed the Modifiers property to Protected.
I've got your first example working now thank you.

I'm thinking a bit deeper into this now...... So yes the routines in BaseForm can reference all the common buttons, but what about the text boxes which are different on every form? In my MsAccess application the New Record Button creates a new record and places the Key Field value into a text box of the same name. For example, in the Customers screen the New Record Button causes a new record and customer code to be generated, then writes the new customer code into a text box called CustomerCode. I achieve this by supplying the tablename and keyfield name to the routine which inserts the new record.

I would like to write this code in the BaseForm.OnNewRecordClick and supply it with the relevent parameters. But if I am understanding this correctly, code executing within the BaseForm class will be unable to reference any entity on a form which is inheriting that class even if it knows the name of that entity.

In MS Access I can write: Forms![Form1]![CustomerCode]= "ABC123"

Is that not possible in C#?
I think I got it. I put this code in the BaseForm class......

Public Void WriteTextBox(String ControlName, String ControlValue)
{
    this.Controls[ControlName].Text = ControlValue;
}

It even works when called from the chld form.
To provide some clarification to you.  When you instantiate the child class (in this case a form), all of the code in the base class (that the child has not overridden) *is* now a part of the child class.

Consider the following:

BaseForm.cs -
using System;
using System.Windows.Forms;

namespace EE_Q28691014
{
	public partial class BaseForm : Form
	{
		public BaseForm()
		{
			InitializeComponent();
		}

		protected virtual void OnNewRecordClick(object sender, EventArgs e)
		{
			btnNewRecord.Enabled = false;
		}

		protected virtual void WriteTextBox(Control Control, String Value)
		{
			Control.Text = Value;
		}
	}
}

Open in new window

BaseForm.Designer.cs -
namespace EE_Q28691014
{
    partial class BaseForm
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
		   this.btnNewRecord = new System.Windows.Forms.Button();
		   this.SuspendLayout();
		   // 
		   // btnNewRecord
		   // 
		   this.btnNewRecord.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
		   this.btnNewRecord.Location = new System.Drawing.Point(197, 227);
		   this.btnNewRecord.Name = "btnNewRecord";
		   this.btnNewRecord.Size = new System.Drawing.Size(75, 23);
		   this.btnNewRecord.TabIndex = 0;
		   this.btnNewRecord.Text = "New Record";
		   this.btnNewRecord.UseVisualStyleBackColor = true;
		   this.btnNewRecord.Click += new System.EventHandler(this.OnNewRecordClick);
		   // 
		   // BaseForm
		   // 
		   this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
		   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
		   this.ClientSize = new System.Drawing.Size(284, 262);
		   this.Controls.Add(this.btnNewRecord);
		   this.Name = "BaseForm";
		   this.Text = "BaseForm";
		   this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button btnNewRecord;
    }
}

Open in new window

Form1.cs -
using System;
using System.Windows.Forms;

namespace EE_Q28691014
{
	public partial class Form1 : BaseForm
	{
		public Form1()
		{
			InitializeComponent();
		}

		protected override void OnNewRecordClick(object sender, EventArgs e)
		{
			base.OnNewRecordClick(sender, e);
			WriteTextBox(textBox1, "Howdy TownTalk");
			MessageBox.Show("We have disabled the new record button by using the click event in the parent (base).");
		}
	}
}

Open in new window

Form1.Designer.cs -
namespace EE_Q28691014
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
		   this.textBox1 = new System.Windows.Forms.TextBox();
		   this.SuspendLayout();
		   // 
		   // textBox1
		   // 
		   this.textBox1.Location = new System.Drawing.Point(13, 13);
		   this.textBox1.Name = "textBox1";
		   this.textBox1.Size = new System.Drawing.Size(178, 20);
		   this.textBox1.TabIndex = 1;
		   // 
		   // Form1
		   // 
		   this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
		   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
		   this.ClientSize = new System.Drawing.Size(284, 262);
		   this.Controls.Add(this.textBox1);
		   this.Name = "Form1";
		   this.Text = "Form1";
		   this.Controls.SetChildIndex(this.textBox1, 0);
		   this.ResumeLayout(false);
		   this.PerformLayout();

        }

        #endregion

	   private System.Windows.Forms.TextBox textBox1;
    }
}

Open in new window

Produces the following output -User generated imageUser generated imageThis shows that the child form is actually calling the method.  Since the child form does know about the textbox control, the method can be used to modify the control.

However, we cannot access textBox1 from the BaseForm because BaseForm knows nothing about textBox1.

For example:
// Specify this in the BaseForm.cs.  Invalid, will produce compiler errors.
protected virtual void WriteTextBox(string Value)
{
	textBox1.Text = Value;
}

Open in new window


-saige-
Thanks again for taking the time to do the above. I agree with everything except your final statement.  The base form CAN write a value into a textbox on the child form. simplifying what I did just now.....

The Load event of Form1 has this line:

 private void FormLoad(object sender, EventArgs e)
  {
               //KeyFieldName is declared in BaseClass
               KeyFieldName = "DepartmentID";
  }

And then in the BaseForm....

Public partial class BaseClass : Form
{
     protected string KeyFieldname;

    Public Void InsertRecord()
    {
            this.Controls[KeyFieldName].Text = "Dept007"
    }
}

Form1 contains a field called DepartmentID and the above Base Class code  writes "Dept007" into it.

So i've got this doing everything I need. Thank you so much for your help.