Any widget can contain child controls. A widget supports templates. In fact, it adds child controls to it. In what form is a template made public in the control? Think about it. We can use a general control to directly specify attributes with tags, and templates also use tags for operations. When using a template, the template is assigned a value using the TAG method. According to "any control can contain subcontrols", the template can also contain controls. Therefore, the template is represented as an attribute in the control definition. Our tags are all presented in XML format. Therefore, using a template is actually assigning a property of the control to a value such as XML tag.
Can the above statements be understood? See the following mark.
<PC3: templatecontrol id = "TEM" runat = "server">
<Itemtemplate>
<Asp: button id = "button1" text = "<% # container. Text %>" runat = "server">
</ASP: button>
<Asp: Label id = "label1" text = "<% # container. Text %>" runat = "server"> </ASP: Label> <br>
<Div> Hello world! </Div>
</Itemtemplate>
</PC3: templatecontrol>
This is a custom control that supports template and data binding. It is done in this way.
Itemtemplate is an itemplate type attribute. As long as this attribute is made public to the outside, the control can support the template.
// Template
Private itemplate itemtemplate;
Public itemplate itemtemplate
{
Get {return this. itemtemplate ;}
Set {This. itemtemplate = value ;}
}
After setting attributes, you also need to add a class-level [parsechildren (true)] feature to the control to indicate that the XML tag elements in the control are treated as attributes, in this way, you can define your own template in the <itemtemplate> tag.
After completing these steps, you need to add the template as a child control to the control.
Protected override void createchildcontrols ()
{
If (this. itemtemplate! = NULL)
{
// When implemented by a class, define the control object to which the child control and template belong. Then define these child controls in the inline template.
// Specify the container control as this representing the control itself
This. itemtemplate. instantiatein (this );
}
Else
This. Controls. Add (New literalcontrol ("No itemtemplate! "));
Base. createchildcontrols ();
}
You can override the createchildcontrols method.
Data can be bound to a child control only when databind is called. Therefore, we need to rewrite ondatabinding.
Protected override void ondatabinding (eventargs E)
{
// Determine whether the server control contains a child control. If not, a child control is created.
This. ensurechildcontrols ();
Base. ondatabinding (E );
}
Here, even if a simple template-supporting control is completed, you can combine the above items to see the effect.
We usually need to bind the template to data. Next, let's take a look at how to implement it.
<% # Container. Text %>
The above is a general binding expression. We can see that it actually calls an object attribute. The container is used to represent the container of the multiplication template, and text is the attribute of the container.
The templatecontainer feature is generally used to specify the container type, as shown in the following code:
[Templatecontainer (typeof (containercontrol), browsable (false)]
Public itemplate itemtemplate
{//...}
Containercontrol is a container control, which is defined as follows:
Public class containercontrol: control, inamingcontainer
{
Private string text;
Public String text
{
Get {return this. Text ;}
}
Public containercontrol (string text)
{
This. Text = text;
}
}
Note: This is a control, but it is not displayed to the page. In fact, it is used to multiply and place templates. In this way, we changed the template to this type when creating the template.
Containercontrol cc = new containercontrol (text );
This. itemtemplate. instantiatein (CC );
This. Controls. Add (CC );
The complete code for this control is as follows :)
Using system;
Using system. Web. UI;
Using system. componentmodel;
Namespace CC
{
/// <Summary>
/// Template exercise
/// </Summary>
// Analyze the XML Element tags in the control
[Parsechildren (true)]
Public class simpletemplate: control, inamingcontainer
{
// Template
Private itemplate itemtemplate;
// Specify the Container Control for storing the Template
[Templatecontainer (typeof (containercontrol), browsable (false)]
Public itemplate itemtemplate
{
Get {return this. itemtemplate ;}
Set {This. itemtemplate = value ;}
}
// Attributes for secondary binding
Private string text;
Public String text
{
Get {return this. Text ;}
Set {This. Text = value ;}
}
/// <Summary>
/// Create a child Control
/// </Summary>
Protected override void createchildcontrols ()
{
If (this. itemtemplate! = NULL)
{
// Use the obtained properties to create a container control
Containercontrol cc = new containercontrol (this. Text );
// Add the template to a dedicated container
This. itemtemplate. instantiatein (CC );
// Add the container to the parent Control
This. Controls. Add (CC );
}
Else
This. Controls. Add (New literalcontrol ("No itemtemplate! "));
Base. createchildcontrols ();
}
/// <Summary>
/// Check and create a template here
/// </Summary>
/// <Param name = "E"> </param>
Protected override void ondatabinding (eventargs E)
{
// Determine whether the server control contains a child control. If not, a child control is created.
This. ensurechildcontrols ();
Base. ondatabinding (E );
}
}
}
For the specific page used, the tag on the. ASPX page is as we did at the beginning. The code in. CS is
If (! This. ispostback)
{
This. TEM. Text = "Hello world! ";
This. TEM. databind ();
}
Here, even if PostBack is enabled, the displayed page remains unchanged. This is mainly because the controls in the template maintain their own statuses. It is important to understand this, this is required for data binding.
Another point is that if databind is commented out, text will not be bound to the template. You can try to put ensurechildcontrols in other methods (such as render or something.
Next, we will create a template control that supports data binding today.
The general mode that supports data binding controls is
Private void page_load (Object sender, system. eventargs E)
{
If (! This. ispostback)
{
This. tm. datasource = new string [] {"one", "two "};
This. tm. databind ();
}
}
Specify the data source first. After confirming that the data source is correct, call the databind method to bind the data. Then you can call the handler of the control's own databinding event, which is generally ondatabinding. In this method, you can operate the data source.
I have analyzed the data source in the previous article. It can be thought that, unlike the general control, the control supporting the template needs to take another step during data binding, except to retrieve the data from the data source, bind the data to the template.
In addition, data sources are generally used when loading pages for the first time. data sources are no longer provided after sending a response, but maintained by viewstate. Therefore, the "bind data to template" operation should be performed on ondatabinding (because databind is only used once), while createchildcontrols should do some maintenance work.
The first step is to prepare an attribute for receiving data sources.
Then there is a method to "bind data to a template", just like the following:
Private void createtemplate ()
{
If (this. itemtemplate! = NULL)
{
If (this. datasource! = NULL)
{
// Calculate the number of subtemplates created
Int itemcount = 1;
// Assume that datasource is string [] for convenience.
Foreach (string text in (string []) This. datasource)
{
Containercontrol cc = new containercontrol (text );
This. itemtemplate. instantiatein (CC );
This. Controls. Add (CC );
Itemcount ++;
}
// Save the quantity to viewstate
This. viewstate ["itemcount"] = itemcount;
}
Else
This. Controls. Add (New literalcontrol ("No itemtemplate! 1 "));
This. childcontrolscreated = true;
}
Else
This. Controls. Add (New literalcontrol ("No itemtemplate! "));
}
In the preceding method, the template container is added cyclically by traversing the data source. You can take a look at the previous note.
Viewstate can maintain the status of controls in the template, but it is not responsible for creating controls. Therefore, we save the number of itemcount templates here, and then create itemcount empty templates in createchildcontrols, in this way, viewstate can be attached to these empty templates.
Protected override void createchildcontrols ()
{
If (this. itemtemplate! = NULL)
{
Int itemcount = (INT) This. viewstate ["itemcount"];
For (INT I = 1; I <itemcount; I ++)
{
Containercontrol cc = new containercontrol (string. Empty );
This. itemtemplate. instantiatein (CC );
This. Controls. Add (CC );
}
}
Else
This. Controls. Add (New literalcontrol ("No itemtemplate! "));
Base. createchildcontrols ();
}
Then it becomes. below is all the code.
Using system;
Using system. Web. UI;
Using system. componentmodel;
Using system. collections;
Using system. Data;
Namespace CC
{
/// <Summary>
/// Template exercise
/// </Summary>
// Analyze the XML Element tags in the control
[Parsechildren (true)]
Public class templatecontrol: control, inamingcontainer
{
// Template
Private itemplate itemtemplate;
// Specify the Container Control for storing the Template
[Templatecontainer (typeof (containercontrol), browsable (false)]
Public itemplate itemtemplate
{
Get {return this. itemtemplate ;}
Set {This. itemtemplate = value ;}
}
// Data Source
Private object datasource;
[Browsable (false)]
Public object datasource
{
Get {return this. datasource ;}
Set {This. datasource = value ;}
}
/// <Summary>
/// Create a sub-control. The created sub-control is an empty shell, and its content is filled according to viewstate.
/// </Summary>
Protected override void createchildcontrols ()
{
If (this. itemtemplate! = NULL)
{
Int itemcount = (INT) This. viewstate ["itemcount"];
For (INT I = 1; I <itemcount; I ++)
{
Containercontrol cc = new containercontrol (string. Empty );
// Add the template to a dedicated container
This. itemtemplate. instantiatein (CC );
// Add the container to the parent Control
This. Controls. Add (CC );
}
}
Else
This. Controls. Add (New literalcontrol ("No itemtemplate! "));
Base. createchildcontrols ();
}
/// <Summary>
/// When the user side calls databind, the data source is ready. You can create a subtemplate.
/// </Summary>
/// <Param name = "E"> </param>
Protected override void ondatabinding (eventargs E)
{
// Determine whether the server control contains a child control. If not, a child control is created.
// This. ensurechildcontrols ();
This. createtemplate ();
Base. ondatabinding (E );
}
// Manually create a subtemplate
Private void createtemplate ()
{
If (this. itemtemplate! = NULL)
{
// When implemented by a class, define the control object to which the child control and template belong. Then define these child controls in the inline template.
// This. itemtemplate. instantiatein (this );
If (this. datasource! = NULL)
{
// Calculate the number of subtemplates created
Int itemcount = 1;
// Assume that datasource is string [] for convenience.
Foreach (string text in (string []) This. datasource)
{
Containercontrol cc = new containercontrol (text );
// Add the template to a dedicated container
This. itemtemplate. instantiatein (CC );
// Add the container to the parent Control
This. Controls. Add (CC );
Itemcount ++;
}
// Save the quantity to viewstate
This. viewstate ["itemcount"] = itemcount;
}
Else
This. Controls. Add (New literalcontrol ("No itemtemplate! 1 "));
// Confirm that the sub-template has been created
This. childcontrolscreated = true;
}
Else
This. Controls. Add (New literalcontrol ("No itemtemplate! "));
}
}
/// <Summary>
/// Container control, used to store templates
/// </Summary>
Public class containercontrol: control, inamingcontainer
{
Private string text;
Public String text
{
Get {return this. Text ;}
}
Public containercontrol (string text)
{
This. Text = text;
}
}
}
Now, we can write a repeater control by ourselves!
There is a bug in the status maintenance. If this control is not processed with PostBack, it will occur when PostBack occurs.
Private void page_load (Object sender, system. eventargs E)
{
This. tm. datasource = new string [] {"one", "two "};
This. tm. databind ();
}
In this way, the PostBack is not judged. Although this is not a good program, this situation does happen.
The specific manifestation is that the template already appears, and the template that appears at the beginning has no content ...... This is mainly because after the page is sent back, createchildcontrols and ondatabinding are called successively within the control according to the time. In this way, the template is created twice, because the original intention of creating the template in createchildcontrols is to assign them null values, and then fill in the viewstate. However, because ondatabinding re-creates the template instance, the required viewstate disappears, so there is a null value. The solution is to add a controls. Clear () in ondatabinding so that you can clear the previously created control that has not been assigned a value!
Protected override void ondatabinding (eventargs E)
{
// Clear the previously created unassigned control!
This. Controls. Clear ();
This. createtemplate ();
Base. ondatabinding (E );
}