Late Binding a Method to a Timer

R

Ronin

I need a little help trying to figure out the last piece of this
puzzle.

I've got a form with an associated toolbox that will allow a user to
drag a control off the toolbox and drop it onto the form. The form
instantiates the control using the Activator.CreateInstance method.
What I then need to do (and the part I have not figured out) is how to
attach a method from that control (a custom data refresh method) to a
timer.elapsed eventhandler on the form. I have been unable to figure
out how to get visibility of the method I need to attach.

Here's the code I have so far.

private void layoutPanel_DragDrop(object sender, DragEventArgs e)
{
NavBarItemLink link = GetItemLink(e.Data);
if (link != null && link.Item.Enabled)
{
String ctrlName = link.ItemName;
String className;
String methodName;
Type t = null;
MethodInfo m = null;
Assembly a = Assembly.Load("JacobsControls");
Type[] types = a.GetTypes();
foreach (Type definedType in types)
{
className = definedType.Name;
if (className == ctrlName)
{
t = definedType;
MethodInfo[] methods = t.GetMethods();
foreach (MethodInfo method in methods)
{
methodName = method.Name;
if (methodName == "RefreshControl")
{
m = method;
break;
}
}
break;
}
}
Object[] constructorArgs = { (int)13, (int)1 };
Object o = Activator.CreateInstance(t, constructorArgs);
layoutPanel.Controls.Add((Control)o);

// This is where the newly created control needs to have
// a "RefreshControl" method attached to a timer.

}

Thanks,
Jason
 
M

Marc Gravell

Well, you can do this via reflection... however, can I suggest
something better? Define an interface eith the RefreshData method, and
ensure your custom controls implement this interface; you should then
be able to do something like:

IRefreshControl control = Activator.CreateInstance(...) as
IRefreshControl;
if(control!=null) {
timer.Elapsed += delegate {
control.RefreshData();
};
}

Otherwise, you could relace control.RefreshData() with mi.Invoke();

Marc
 
D

DeveloperX

How about Add a new Event to the form, hook the timer to a private
method on the form and have that raise the event. You can then hook it
up to the new control in the same way all the other controls are done
by the IDE

ie this.button1.Click += new System.EventHandler(this.button1_Click);
becomes this.TimerFired+= new system.EventHandler(newControl.Fired);
 
D

DeveloperX

Killing time, here's a complete little program that creates a derived
textbox for a form and wires it up to an event on the form that is
fired when a timer fires.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace WindowsApplication3
{
public delegate void UpdateTextDelegate(object sender,
UpdateTextEventArgs e);

public class Form1 : System.Windows.Forms.Form
{
private System.ComponentModel.Container components = null;
private System.Windows.Forms.Button button1;
private System.Threading.Timer _timer = null;

public event UpdateTextDelegate UpdateText;

public Form1()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();

this.button1.Location = new System.Drawing.Point(8, 8);
this.button1.Name = "button1";
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void TimerFired(object p)
{
if(null != UpdateText)
{
UpdateText(this,new UpdateTextEventArgs(DateTime.Now.ToString()));
}
}
private void button1_Click(object sender, System.EventArgs e)
{
_timer = new System.Threading.Timer(new
System.Threading.TimerCallback(this.TimerFired),null,1000,1000);
ThingFactory.AddTextBox(this);
button1.Enabled = false;
}
}
public class UpdateTextEventArgs : EventArgs
{
public UpdateTextEventArgs(string pNewText)
{
NewText = pNewText;
}
public string NewText;
}
public class ThingFactory
{
private ThingFactory()
{
}
public static void AddTextBox(Form1 pForm)
{
NuTextBox t = new NuTextBox();

t.Location = new System.Drawing.Point(184, 136);
t.Name = "textBox1";
t.TabIndex = 0;
t.Text = "";
t.Size = new System.Drawing.Size(152, 20);
pForm.Controls.Add(t);
pForm.UpdateText+=new UpdateTextDelegate(t.SetText);
}
}
public class NuTextBox : System.Windows.Forms.TextBox
{
public NuTextBox()
{
}
public void SetText(object sender, UpdateTextEventArgs e)
{
this.Text = e.NewText;
}
}
}






How about Add a new Event to the form, hook the timer to a private
method on the form and have that raise the event. You can then hook it
up to the new control in the same way all the other controls are done
by the IDE

ie this.button1.Click += new System.EventHandler(this.button1_Click);
becomes this.TimerFired+= new system.EventHandler(newControl.Fired);

I need a little help trying to figure out the last piece of this
puzzle.

I've got a form with an associated toolbox that will allow a user to
drag a control off the toolbox and drop it onto the form. The form
instantiates the control using the Activator.CreateInstance method.
What I then need to do (and the part I have not figured out) is how to
attach a method from that control (a custom data refresh method) to a
timer.elapsed eventhandler on the form. I have been unable to figure
out how to get visibility of the method I need to attach.

Here's the code I have so far.

private void layoutPanel_DragDrop(object sender, DragEventArgs e)
{
NavBarItemLink link = GetItemLink(e.Data);
if (link != null && link.Item.Enabled)
{
String ctrlName = link.ItemName;
String className;
String methodName;
Type t = null;
MethodInfo m = null;
Assembly a = Assembly.Load("JacobsControls");
Type[] types = a.GetTypes();
foreach (Type definedType in types)
{
className = definedType.Name;
if (className == ctrlName)
{
t = definedType;
MethodInfo[] methods = t.GetMethods();
foreach (MethodInfo method in methods)
{
methodName = method.Name;
if (methodName == "RefreshControl")
{
m = method;
break;
}
}
break;
}
}
Object[] constructorArgs = { (int)13, (int)1 };
Object o = Activator.CreateInstance(t, constructorArgs);
layoutPanel.Controls.Add((Control)o);

// This is where the newly created control needs to have
// a "RefreshControl" method attached to a timer.

}

Thanks,
Jason
 
R

Ronin

Marc,

Thanks for the advice. I had to make a few tweaks, but it works very
well (and with a lot let work).

Here's what I did to get it to work:

IRefreshControl control = Activator.CreateInstance(...) as
IRefreshControl;
if(control!=null)
{
timer.Elapsed += control.RefreshData;
}
 
M

Marc Gravell

That'll do it. By using the inline delegate I was trying to avoid
having to have the (sender, args) signature on the method, but that
works fine too. Another approach (as suggested by another poster) is to
put the control into a field and have a form level event handler check
the field and invoke the method - same result, different approach.

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top