Hej, Glömde lägga till Count implementationen. StateBag, ViewState hantering av lösa Klasser...
Jag har fått frågor ang hur man ev skall hantera Items i egna Nod baserad kontroll. Ex
ett träd, en lista med noder etc… Eftersom informationen hur detta kan gå till är tunn på nätet och i MS dokumentation tänker jag försöka förklara hur det går till.
StateBag är en klass som hanterar viewstaten för ASP .Net sidor. Denna hantering finns inbyggt i allt som ärver control klassen. ASP .Net kan dock inte hantera lösa klasser så som egna deffinerade Items, Nodes, Points etc... Låt säga att du bygger en Kontroll som skall ha en Collection av Nodes.
Kontrollen i sig ärver någon slags kontroll som i sin tur ärver Control klassen. De nodes man ev vill lägga till är en vanlig klass utan arv av någon control klass.
Ex:
<code>
public class Node
{
private string _Value;
public string Value
{
set { this._Value = value; }
get { return ( this._Values ); }
}
}
</code>
När denna node läggs till i din NodeCollection som finns i din NodeControl så finns inte dess information kvar vid en PostBack. NodeCollection blir tom och man måste fylla upp den på nytt.
<code>
public class NodeControl : ...Control
{
private NodeCollection _Items = new NodeCollection();
public NodeCollection Items
{
set { this._Items = value; }
get { return (this._Items) ;}
}
...
}
</code>
<code>
public class NodeCollection
{
private ArrayList _nodeItems = new ArrayList();
public void Add(Node node)
{
_nodeItems.Add(node);
}
}
</code>
<info>
OBS! Koden jag skriver är ren från konstruktorer och annan info, detta för att spara plats, tar bara med de viktigaste bitarna.
</info>
Vad vi i princip med ovan kod kan göra nu är att skapa Kontrollen på vår ASP .Net sida, sedan i code behind lägga till noder i vår NodeCollection som vi sedan signar vår Items property i NodeControl. Koden för att rita ut Noderna har jag inte med (detta får ni sköta själva, det är inte syftet med detta inlägg. Jag utgår att ni redan vet hur ni skall göra.)
När sidan postas om blir noderna tomma och ni måste fylla dem igen. Detta vill vi slippa. Så vad vi måste göra är att spara ner vår NodeControls data i viewstaten. (vi kan använda sessions variabler eller liknande, men då man ev inte vill ha sessionsstödet på är detta inget att föredra.)
För att hantera viewstate finns det två grundläggande metoder. LoadViewState och SaveViewState. Dessa måste implementeras i samtliga klasser. Vi börjar med vår NodeControl.
Vi börjar med att lägga dit SaveViewState. Eftersom vår NodeControl redan ärver en Control class finns metoderna där, så vi gör en överlagring av dem. (Override).
<code>
Public class NodeControl…
{
…
protected override object SaveViewState()
{
object[] array = new object[2];
array[0] = base.SaveViewState();
array[1] = ((IStateManager)this._Items).SaveViewState();
return array;
}
…
}
</code>
Först skapar vi en object array som är två stor. På index 0 sparar vi original viewstaten. (Detta är den som ASP .Net kommer att skapa för den Control typ vi ärvt. ViewState data vi itne behöver bry oss om.)
På index 1 lägger vi vår egna ViewState data. Vad jag gör här är att jag nyttjat IStateManager interfacet för att komma åt den skyddade metoden SaveViewState som vi kommer att bygga in i vår NodeCollection lite senare. (Kom ihåg att _Items var vår NodeCollection.
<code>
public class NodeControl : ...Control
{
private NodeCollection _Items = new NodeCollection();
…
}
</code>
Sedan returnerar vi arrayen som ASP .Net motorn kommer ta hand om.
När vi ändå modifierar vår NodeControl kan vi lägga till LoadViewState.
<code>
protected override void LoadViewState(object savedState)
{
if(savedState == null)
{
base.LoadViewState(null);
return;
}
object[] array = (object[])savedState;
base.LoadViewState(array[0]);
if(array[1] != null)
((IStateManager)this.Items).LoadViewState(array[1]);
}
</code>
Först kollar jag om vår ViewState är null. (det som kommer in här är vår Array från SaveViewState.) Därefter kör jag bas klassens (någon control klass) LoadViewState och skickar in null. (Detta behövs egentligen inte, men jag gör det bara av ren rutin, känns bättre ;-) )
Jag kör en return om ViewStaten var null. Om den inte är null kör jag vidare, jag kastar om min ViewState till objekt arrayen. (object[])savedState;
Sedan skickar jag in index 0 in bas klassens LoadViewState så den kan hantera sin data. Därefter kollar jag så min viewstate data finns. Och kör LoadViewState på min NodeCollection.
Det som händer nu är följande. Då ASP .Net lägger till min kontroll kommer LoadViewState att anropas. Om det finns någon ViewState för denna kontroll så laddas den in annars inte. (OBS! Första gången är den tom, det finns ingen data i den.)
När jag sedan i min Code Behind lägger till noder och ASP .Net sidan renderats klart kommer ASP .Net att köra min SaveViewState som i sin tur kommer att ropa på min NodeCollections SaveViewState metod.
Än så länge fungerar inte vår kod. SÅ vi måste implementera SaveViewState och LoadViewState i vår NodeCollection.
Då NodeCollection är en vanlig klass och inte ärver någon Control class måste vi slänga till med ett Interface. I detta fall IStateManager. Vi kan alltså inte överskrida metoderna som vi gjorde i vår kontrol.
En property och tre metoder kommer att krävas.
IsTrackingViewState en property som returnerar en boolean om kontrollen sparar ändringar i viewstaten.
TrackViewState som kollar om det finns någon ViewState data som ändrats.
Load respektiva SaveViewState. Ja ni vet redan vad de gör.
Vi börjar med TrackViewState:
<code>
public class NodeCollection
{
…
public Node this[int index]
{
get { return ( (Node)this._ nodeItems index] ); }
}
Private bool _isTrackingViewState;
void System.Web.UI.IStateManager.TrackViewState()
{
this._isTrackingViewState = true;
for(int i=0;i<this.Count;i++)
{
((IStateManager)this[i]).TrackViewState();
}
}
…
</code>
Först lägger jag till en this för att göra koden smidigare. Vad jag gör här är att jag kastar om mina värden i ArrayListen till Noder.
Här sätter jag _isTrackingViewState till true då jag kör en tracking av mina Noder.
this[int index], detta för att sätta alla noders trackings till true (kommer till detta sen.)
Därefter implementerar jag IsTrackingViewState propertyn.
<code>
bool System.Web.UI.IStateManager.IsTrackingViewState
{
get
{
return ( this._isTrackingViewState );
}
}
</code>
Som returnerar _isTrackingViewState värdet.
<info>
TackViewState anropas alltid efter kontrollens init. Kan vara bra o veta.
</info>
Nu till det viktiga; SaveViewState.
<code>
object System.Web.UI.IStateManager.SaveViewState()
{
object[] array1 = new object[this.Count];
for(int i=0;i<this.Count;i++)
array1[i] = ((IStateManager)this[i]).SaveViewState();
return array1;
}
</code>
Ser nästan ut som den vi byggde i vår kontroll. Lite skillnader finns dock.
Först anger jag en object array som är lika stor som min colletion. Detta för att spara ner varje nodes viewState. Sedan loopar jag genom alla Nodes som ligger i min collection och ropar på dess SaveVeiwState metod. Det som kommer hända nu är alltså att vår NodeControll kör sin SaveViewState som sedan ropar på NodeCollections SaveViewState som i sin tur kör genom alla Noders SaveViewState metod.
Nästa steg är att få vår NodeCollection att lägga till alla noder som vi sparade undan.
<code>
void System.Web.UI.IStateManager.LoadViewState(object state)
{
if(state == null)
return;
object[] array = (object[])state;
for(int i=0;i<array.Length;i++)
{
if(array[i] != null)
{
((IStateManager)this[i]).LoadViewState(array[i]);
}
}
}
</code>
Vi kollar om Staten är null om inte så loopar vi genom vår state array. Där vi laddar in varje Node med hjälp av Nodens LoadViewState metod.
Nu till vår Node.
Node är också en vanlig klass och behöver därför ärva IStateManager interfacet.
Det som skiljer Node från NodeCollection är att jag nu nyttjar StateBag klassen. Jag kommer även göra en lätt Factory property (ViewState)
<info>
Factory är ett designmönster, där en metod eller property har i egenskap att skapa en klass eller flera.
</info>
Vad denna property gör är inte mycket, jag skapar min StateBag klass kollar om nodens isTaravkincViewState variabel är true och kör då TrackViewState på den.
Sedan har vi min Value property som jag byggde tidigare. (Node klassen i börjar av denna artikel) Här ändrar jag nu om så den hämtar värdet ur min StateBag och sätter värdet till min Statebag. Så gör du på alla de properties dina noder har. (De du vill spara till viewstaten förståss).
<code>
public sealed class Node : IStateManager
{
private StateBag _viewState = null;
private bool _isTrackingViewState = false;
private StateBag ViewState
{
get
{
if (this._viewState == null)
{
this._viewState = new StateBag();
if (this._isTrackingViewState)
((IStateManager)this._viewState).TrackViewState();
}
return this._viewState;
}
}
public string Value
{
get
{
object obj1 = this.ViewState["Value"];
if (obj1 == null)
return string.Empty;
return ((string) obj1);
}
Set { this.ViewState["Value"] = value; }
}
…
}
</code>
Vi måste även implementera de fyra kraven IStateManager har.
IsTracking-…, Save-…, Load-… och TrackViewstate.
Vi börjar med TrackViewState.
<code>
void System.Web.UI.IStateManager.TrackViewState()
{
this._isTrackingViewState = true;
if(this.ViewState != null)
((IStateManager)this.ViewState).TrackViewState();
}
</code>
Samma implementation som NodeCollection.
Sedan IsTrackingViewState, även här samma som i NodeCollection.
<code>
bool System.Web.UI.IStateManager.IsTrackingViewState
{
get
{
return this._isTrackingViewState;
}
}
</code>
Nu till SaveViewState.
<code>
object System.Web.UI.IStateManager.SaveViewState()
{
object[] array = new object[1];
if (this.ViewState != null)
array[0] = ((IStateManager)this._viewState).SaveViewState();
if(array[0] == null)
return ( null );
return ( array );
}
</code>
Även här inga konstigheter, jag sparare ner StateBagens data i en array för noden som jag returnerar.
Och LoadViewSte.
<code>
void System.Web.UI.IStateManager.LoadViewState(object state)
{
object[] array = ((object[])state);
if (array == null)
return;
if (array[0] != null)
((IStateManager)this.ViewState).LoadViewState(array[0]);
}
</code>
Så det var allt. Varje Node har sin egna StateBag som innehåller State information som sedan skickas upp till NodeCollection som plockar Node efter Nodes Sate data. Den i sin tur skickar upp state datan till NodeControl som ser till så den läggs till på ASP .Net sidan.
Mvh JohanSv: StateBag, ViewState hantering av lösa Klasser...
kör en this.Count.
Se till så det finns en metod för Count som ger ArrayListens .Count som retur. Dina Items.
Artiklen skrevs som hastigast och är långt från god kvalité.
Mvh Johan