Reputation: 2362
suppose I have 100 windows form written in C#, is there a way to define a function to be called on the load of any of these forms without needing to change the code of any of these forms, and without inheriting from other form, that is a function that is called automatically whenever a new form is opening?
Thanks,
Upvotes: 1
Views: 2370
Reputation: 91
You could use a form factory and implement interception to intercept the OnLoad method or any other virtual method within the Form class.
I created a proof of concept below using Autofac, Castle.Core, and Autofac.Extras.DynamicProxy to intercept the OnLoad method of each form and write to the console before and after the OnLoad method was called on the form.
Form factory passing back Owned. CreateForm(string formName) takes the name of a named Autofac service.
public interface IFormFactory
{
Owned<Form> CreateForm(string formName);
}
public class FormFactory : IFormFactory
{
private readonly IContainer _container;
public FormFactory(IContainer container)
{
_container = container;
}
public Owned<Form> CreateForm(string formName)
{
return _container.ResolveNamed<Owned<Form>>(formName);
}
}
The interceptor below will be called every time a virtual method on the form. I added a check for OnLoad however you can intercept and call for other virtual methods as well.
public class FormInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
bool isFormOnLoad = invocation.InvocationTarget is Form && invocation.Method.Name.Equals("OnLoad");
if(isFormOnLoad)
{
Console.WriteLine("Before OnLoad");
}
invocation.Proceed();
if(isFormOnLoad)
{
Console.WriteLine("After OnLoad");
}
}
}
Register your interceptor, forms, and factory taking care to used named services. The names will used to create the forms in the factory method.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<FormInterceptor>();
Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add<System.Security.Permissions.UIPermissionAttribute>();
// Register your forms
builder.RegisterType<frmMain>()
.Named<Form>("frmMain")
.EnableClassInterceptors()
.InterceptedBy(typeof(FormInterceptor));
builder.RegisterType<frmSubForm>()
.Named<Form>("frmSubForm")
.EnableClassInterceptors()
.InterceptedBy(typeof(FormInterceptor));
FormFactory = new FormFactory(builder.Build());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(FormFactory.CreateForm("frmMain").Value);
}
public static IFormFactory FormFactory { get; set; }
}
Below is an example of creating and showing a test form within the main form:
private void button1_Click(object sender, EventArgs e)
{
using (var subForm = Program.FormFactory.CreateForm("frmSubForm").Value)
{
subForm.ShowDialog(this);
}
}
Upvotes: 0
Reputation: 27009
You can use MessageFilter and intercept the message for the loading of forms. Below is a sample for intercepting forms and then adding an event handler to an event. You can do whatever you need in the event handler.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.AddMessageFilter(new TestMessageFilter());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
I know you need to run code just before the load event but for some reason subscribing to the Load
event does not fire in the code below. But if you can do it in Activated
event, then it will work. Or if you want to do it in another event then just modify the code below and see if that event gets triggered. The code is also keeping a list of all the forms so you don't add multiple handlers for the same event of the same form. When the form is closed, it will remove all the handlers.
[SecurityPermission(SecurityAction.LinkDemand, Flags =
SecurityPermissionFlag.UnmanagedCode)]
public class TestMessageFilter : IMessageFilter
{
private Hashtable forms = new Hashtable();
public bool PreFilterMessage(ref Message m)
{
Control c = Control.FromHandle(m.HWnd);
var form = c as Form;
if (form != null &&
!this.forms.ContainsKey(form))
{
form.Load += Form_Load;
form.Activated += Form_Activated;
form.FormClosed += Form_FormClosed;
this.forms.Add(form, form);
}
return false;
}
private void Form_FormClosed(object sender, FormClosedEventArgs e)
{
if (this.forms.ContainsValue(sender))
{
var f = sender as Form;
f.Activated -= Form_Activated;
f.Load -= Form_Load;
this.forms.Remove(sender);
}
}
private void Form_Activated(object sender, EventArgs e)
{
MessageBox.Show("Form_Activated...");
}
private void Form_Load(object sender, EventArgs e)
{
MessageBox.Show("Form_Load...");
}
}
Upvotes: 2
Reputation: 1820
There is no such other way. You just have to use one of the solutions you've said in your question.
Upvotes: 1