Unless you need to play around with setting control properties prior to tracking ViewState, I would personally go ahead and place my dynamic control addition logic in the OnInit event.
If you really want to dynamically add a control during the PreInit (when using master page) you can always do something like this:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
TextBox textBox = new TextBox();
textBox.Text = "Dynamic TextBox";
textBox.Width = 100;
textBox.ReadOnly = false;
var master = this.Master;
plcHolder.Controls.Add(textBox);
textBox.ApplyStyleSheetSkin(this.Page);
}
accessing the "Master" property would instantiate the controls
and it should work, but you get nested master pages scenarios (this.Master.Master ...), update panels and so on.
This might be relevant and helpful: http://weblogs.asp.net/ysolodkyy/archive/2007/10/09/master-page-and-preinit.aspx
Moreover, one reason I can think of (besides following the defined page lifecycle) MS recommends that we place all the logic for dynamic control creation in the Preinit event so we can take advantage of the theme service, which will apply all available skin properties automatically for us, before the Init event takes place.
Say your markup looks something like that:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Trace="true" Inherits="_Default" Theme="Test" %>
...
<form id="form1" runat="server">
<div>
<p>
<asp:TextBox ID="TextBox1" runat="server" TextMode="Password" Text="Control TextBox"></asp:TextBox>
</p>
<p>
<asp:PlaceHolder ID="plcHolder" runat="server"></asp:PlaceHolder>
</p>
</div>
</form>...
and you have a skin like this:
<asp:TextBox runat="server" BackColor="Yellow" Wrap="false" Text="Skin property!" > </asp:TextBox>
Just add this to your code behind:
private TextBox tb1;
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
tb1 = new TextBox();
tb1.Text = "PreInit Dynamic TextBox";
Trace.Write(String.Format("tb1 Wrap Property-> {0}",tb1.Wrap));
Trace.Write(String.Format("tb1 Text Property-> {0}", tb1.Text));
Trace.Write("Add tb1 to the placeholder.");
plcHolder.Controls.Add(tb1);
Trace.Write(String.Format("tb1 Wrap Property-> {0}", tb1.Wrap));
Trace.Write(String.Format("tb1 Text Property-> {0}", tb1.Text));
}
protected override void OnInit(EventArgs e)
{
Trace.Write(String.Format("tb1 Wrap Property-> {0}", tb1.Wrap));
Trace.Write(String.Format("tb1 Text Property-> {0}", tb1.Text));
base.OnInit(e);
}
protected void Page_Load(object sender, EventArgs e)
{
Trace.Write(String.Format("tb1 Wrap Property-> {0}", tb1.Wrap));
Trace.Write(String.Format("tb1 Text Property-> {0}", tb1.Text));
}
You will notice how before going into the Init event all skin properties are already applied to the dynamically created textbox :)