Despre CompositeDataBoundControl
CompositeDataBoundControl este clasa de baza pentru DetailsView, FormView si GridView si are o metoda abstracta ce trebuie implementata de orice clasa mostenitoare.
protected abstract int CreateChildControls(IEnumerable dataSource, bool dataBinding);
dataBinding reprezinta:
-true: controlul va fi construit pe baza unui dataSource
-false: controlul se va reconstrui din ViewState
DetailsView, FormView sau GridView ofera diverse templaturi: de edit, de view sau atunci cand nu exista date in dataSource poti sa arati un anumit mesaj ce prezinta aceasta stare. Ca idee un template face legatura intre date si HTMLul scris de developer la design time, adica separa prezentarea de implementare.
Din urmatorul Xml, care reprezinta organizarea unei anumite firme, as vrea sa obtin un control care sa arate ierarhia din acea firma, adica un fel de tree. Xmlul are 3 niveluri: Managers urmat de Sales, Development si Administrative, fiecare la randul lui avand alte diverse categorii de angajati. Fiecare categorie nu poate avea alta subcategorie.
<Organigram>
<Managers ID="managersDepartment">
<Sales ID="salesDepartment">
<Salesmen ID="salesMen">
</Salesmen>
</Sales>
<Development ID="developmentDepartment">
<TeamLeaders ID="teamLeaders">
</TeamLeaders>
<Developers ID="developers">
</Developers>
</Development>
<Administrative ID="administrativeDepartment">
<HumanResources ID="humanResources">
</HumanResources>
<AssistantManagers ID="assistantManagers">
</AssistantManagers>
</Administrative>
</Managers>
</Organigram>
De fapt din acest Xml sa obtin urmatorul Html.
<h1 style="padding-left: 10px;">
Managers
</h1>
<h2 style="padding-left: 40px;">
Sales
</h2>
<h3 style="padding-left: 80px;">
<hr />
Salesmen
<hr />
</h3>
<h2 style="padding-left: 40px;">
Development
</h2>
<h3 style="padding-left: 80px;">
<hr />
TeamLeaders
<hr />
</h3>
<h3 style="padding-left: 80px;">
<hr />
Developers
<hr />
</h3>
<h2 style="padding-left: 40px;">
Administrative
</h2>
<h3 style="padding-left: 80px;">
<hr />
HumanResources
<hr />
</h3>
<h3 style="padding-left: 80px;">
<hr />
AssistantManagers
<hr />
</h3>
Pentru fiecare nivel din XML va exista un template: Level1, Level1 si Level2. Developerul de la design time va putea avea posibilitatea sa defineasca fiecare template. Pentru a genera html-ul de mai sus, controlul a fost scris astfel:
<my:MyCompositeDataBoundControl runat="server" ID="myCompositeDataBoundControl" DataSourceID="ObjectDataSource1">
<Level0>
<h1 style="padding-left: 10px;">
<%# Eval("Name") %>
</h1>
</Level0>
<Level1>
<h2 style="padding-left: 40px;">
<%# Eval("Name") %>
</h2>
</Level1>
<Level2>
<h3 style="padding-left: 80px;">
<hr />
<%# Eval("Name") %>
<hr />
</h3>
</Level2>
</my:MyCompositeDataBoundControl>
In metoda abstracta CreateChildControls se instantiaza fiecare template. Daca momentul este la dataBinding == true, atunci templaturile se aleg in functie de starea fiecarui dataItem din dataSource. Odata ales, trebuie intr-un fel a se memora faptul ca un anumit template reprezinta un anumit item si acest lucru se face in ViewState. Apoi, cand dataBinding e false ( nu s-au reluat datele dintr-un mediu de stocare, controlul e reconstruit din ViewState), atunci in functie de ce s-a memorat la dataBinding = true, se reface controlul. Metoda returneaza numarul de dataItem-uri din dataSource.
namespace CDataBoundControls
{
public class MyCompositeDataBoundControl : CompositeDataBoundControl
{
private ITemplate _level0;
[PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(Item))]
public ITemplate Level0
{
get { return _level0; }
set { _level0 = value; }
}
private ITemplate _level1;
[PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(Item))]
public ITemplate Level1
{
get { return _level1; }
set { _level1 = value; }
}
private ITemplate _level2;
[PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(Item))]
public ITemplate Level2
{
get { return _level2; }
set { _level2 = value; }
}
protected int ItemCount
{
get { return (int)ViewState["@itemCount"]; }
set { ViewState["@itemCount"] = value; }
}
protected string[] Templates
{
get { return (string[])ViewState["Templates"]; }
set { ViewState["Templates"] = value; }
}
protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
if (dataSource == null)
{
ItemCount = 0;
return 0;
}
if (dataBinding)
{
int count = 0;
int itemIndex = 0;
var templates = new List<string>();
foreach (var dataItem in dataSource)
{
if (dataItem is Department)
{
var department = (Department)dataItem;
ITemplate template = null;
if (Level0 != null && department.Level == 0)
{
template = Level0;
templates.Add("Level0");
}
else
if (Level1 != null && department.Level == 1)
{
template = Level1;
templates.Add("Level1");
}
else
if (Level2 != null && department.Level == 2)
{
template = Level2;
templates.Add("Level2");
}
else template = null;
var item = new Item(department, itemIndex++);
if (template != null)
{
count++;
template.InstantiateIn(item);
Controls.Add(item);
if (dataBinding)
item.DataBind();
}
}
}
ItemCount = count;
Templates = templates.ToArray();
DataBind(false);
return count;
}
else
// the control is build from viewstate
{
ITemplate template = null;
for (int i = 0; i < ItemCount; i++)
{
string templateName = Templates[i];
switch (templateName)
{
case "Level0":
{
template = Level0;
break;
}
case "Level1":
{
template = Level1;
break;
}
case "Level2":
{
template = Level2;
break;
}
default:
{
break;
}
}
if (template != null)
{
Item item = new Item(null, i);
template.InstantiateIn(item);
Controls.Add(item);
}
}
return ItemCount;
}
}
}
}
Download Source Code and Play
Multumesc Adrian, explicatiile sunt foarte bune, m-au ajutat sa rezolv un bug pe o aplicatie mai veche. Ai un blog foarte interesant. Keep it up!
ReplyDelete@AdrianPetcu.. si postare e destul de veche, mersi! O sa mai scriu
ReplyDelete