See the question and my original answer on StackOverflow

The ASP.NET page parser doesn't support custom directives (although it does support custom attributes on the Page or MasterPage directive). But you still can do what you want using server controls.

Let's suppose you have a base plugin class, like this:

// we derive from control, so all plugins are automatically
// added to the Page.Controls collection
public class MyPluginBase : Control
{
}

And for example, two plugin classes like this:

public class MyPlugin : MyPluginBase
{
    public string MyFirstProperty { get; set; }
    public int MySecondProperty { get; set; }
}

public class MyOtherPlugin : MyPluginBase
{
    public string MyFirstProperty { get; set; }
    public int MySecondProperty { get; set; }
}

Then you can write an ASPX webform (or an ASCX) like this:

<%@ Page ... standard attributes ... %>

<plugins:MyPlugin runat="server" MyFirstProperty="blah" MySecondProperty="1"></plugins:MyPlugin>
<plugins:MyOtherPlugin runat="server" MyFirstProperty="blahblah" MySecondProperty="2"></plugins:MyOtherPlugin>

etc...

Note for this to work, you'll have to declare the plugins prefix somewhere, for example in the web.config, like this:

<configuration>
  <system.web>
    <pages>
      <controls>
        <!-- all assemblies & namespaces that contain plugins classes -->
        <add assembly="WebApplication1" namespace="WebApplication1.Code" tagPrefix="plugins" />
        <add assembly="MyOtherPlugins" namespace="MyOtherPlugins.Plugins" tagPrefix="plugins" />
      </controls>
    </pages>
  </system.web>
</configuration>

And in the code behind class, you'll have automatic access to the plugins, in all events including Init, with a code like this:

public partial class WebForm1 : Page
{
    protected void Page_Init(object sender, EventArgs e)
    {
        // get all plugins on the page
        var plugins = Controls.OfType<MyPluginBase>();
    }
}