Lista produktów dla danej kategorii

 

Ostatnie zmiany w kodzie aplikacji zakończone zostały na dodaniu przeze mnie spisu kategorii produktów w postaci menu znajdującego się w master page. Menu zostało utworzone jako zbiór odnośników, z których każdy tworzony jest przez dodanie tzw. query string do adresu URL. Jako wartość parametru przekazywany jest numer odpowiedniej kategorii. Użytkownik przenoszony jest na stronę ProductsList.aspx. Zadaniem tej strony jest wyświetlenie wszystkich produktów należących do danej kategorii. Na tym etapie prac wyświetlenie jest realizowane w bardzo prosty sposób.

Na stronie z produktami umiesczona została kontrolka ListView, pozwalająca na dość swobodne przeprowadzanie modyfikacji jej wyglądu. Jako, że produkty pobierane są z bazy danych, kontrolka została powiązana ze źródłem danych:

<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
            ConnectionString="<%$ ConnectionStrings:ShoppingAppDbConnectionString %>" 
            SelectCommand="SELECT * FROM [tProducts] WHERE ([ProductCategoryID] = @ProductCategoryID)">
            <SelectParameters>
                <asp:QueryStringParameter Name="ProductCategoryID" 
                    QueryStringField="CategoryId" Type="Byte" />
            </SelectParameters>
</asp:SqlDataSource>

Do połączenia wykorzystywany jest standardowo connectionString znajdujący się w pliku Web.config. W wyniku zapytania pobierane z tabeli tProducts są wszystkie kolumny, przy czym klauzula WHERE mówi o tym, że identyfikator kategorii ProductCategoryID pochodzi z ustawionego parametru. Parametr ten pobierany jest z query string, o czym świadczy kod znajdujący się pomiędzy znacznikami <SelectParameters></SelectParameters>. Do parametru o odpowiedniej nazwie (name), pobierana jest wartość z query string (querystringfield) o typie danych (type) Byte (kolumna w tabeli jest typu tinyint).

W przypadku kontrolki ListView najbardziej interesujące są dwa miejsca. Jest to deklaracja kontrolki oraz jej ItemTemplate. Kontrolka tworzona jest w następujący sposób:

<asp:ListView ID="ListView1" runat="server" DataKeyNames="ProductID" 
            DataSourceID="SqlDataSource1" GroupItemCount="2">
...
</asp:ListView>

Szczególnie interesujący jest atrybut groupitemcount, mówiący o tym, ile produktów wyświetlanych będzie w jednym rzędzie danych.

W ItemTemplate natomiast znalazł się następujący kod:

<ItemTemplate>
	<td runat="server" style="">
		<table border="0" width="300">
		<tr>
			<td style="width: 25px"></td>
			<td style="vertical-align: middle; text-align: right">
				<a href="ProductsDetails.aspx?ProductID=&lt;%# Eval(&quot;ProductId&quot;) %&gt;">
					<image src="Images/triangle.jpg" width="30px" height="30px" border="1"></image>
				</a>
			</td>
			<td style="width: 250px; vertical-align: middle">
				<a href="ProductsDetails.aspx?ProductID=&lt;%# Eval(&quot;ProductId&quot;) %&gt;"><%# Eval("ProductName") %></a>
				<br />Weight:
				<asp:Label ID="WeightLabel" runat="server" Text='<%# Eval("Weight") %>' />
				<br />Capacity:
				<asp:Label ID="CapacityLabel" runat="server" Text='<%# Eval("Capacity") %>' />
				<br />Items:
				<asp:Label ID="ItemsLabel" runat="server" Text='<%# Eval("Items") %>' />
				<br />
			</td>
		</tr>
		</table>
	</td>
</ItemTemplate>

Każdy produkt wyświetlany jest w jednym rzędzie utworzonej tabelki. Pierwsza komórka odpowiedzialna jest za ustalenie odstępów pomiędzy produktami. Zadaniem drugiej komórki jest wyświetlenie obrazka, będącego jednocześnie odnośnikiem, natomiast w trzeciej komórce znajduje się krótki opis produktu. Zarówno po kliknięciu obrazka, jak i wybraniu nazwy produktu użytkownik przenoszony jest do strony ProductsDetails.aspx, na której będzie mógł poznać cenę wybranego produktu.

Post zrealizowany na podstawie: http://www.asp.net/aspnet-4/videos/tailspin-spyworks-display-the-product-list

PS. Po opublikowaniu poprzedniego posta zauważyłem, że kolumna ‘cena’ nie powinna znajdować się w tabeli z produktami, gdyż cena jest zależna od sklepu, w którym znajduje się produkt. Błąd ten niedługo zostanie poprawiony 🙂

Tabela produktów + SMS tips

 

Na wstępie chciałem wyjaśnić, że skrót SMS nie ma nic wspólnego z telefonami komórkowymi, tylko jest to skrót od SQL Management Studio. Dla osób korzystających na co dzień z SQL Server nie muszę wyjaśniać, do czego to narzędzie służy, natomiast dla początkujących znajdzie się kilka porad (niekoniecznie przydatnych 😉 ).

Przede wszystkim, w nawiązaniu do poprzednich postów, znajdujemy się w miejscu, w którym utworzone zostało menu z kategoriami. Na podstawie wyboru odpowiedniej pozycji w menu, użytkownik przenoszony jest za pomocą odnośnika do strony wyświetlającej produkty z danej grupy żywnościowej. Wykorzystany w tym celu został jeden z mechanizmów zarządzaniam stanem sesji, czyli opisane już wcześniej tzw. query strings.

Musimy mieć jednak co wyświetlać. Z tej okazji została zaprojektowana przeze mnie kolejna tabela opisująca produkty. Tak na marginesie, ktoś mógłby spytać czemu nie zaprojektowałem od razu bazy danych – odpowiadam – celem projektu jest poznanie głownie ASP.NET, a do tego kompletna baza danych nie jest mi potrzebna, gdyż ze względu na ograniczenie czasowe trwania projektu prawdopodobnie nie zostanie ona nigdy wykorzystana w pełni. Jednocześnie zmieniona została nazwa jednej z kolumn z tabeli z kategoriami tak, aby uzyskać w miarę ustandaryzowane nazewnictwo (chociaż konflikty nadal występują). Spójrzmy zatem jak wyglądają powiązane ze sobą wspomniane tabele:

tabela produktow 

Tabela produktów w wymyślonej na potrzeby projektu przeze mnie postaci przedstawia się następująco: każdy produkt posiada swoją nazwę; oczywiście poza ceną jest on charakteryzowany również przez inne atrybuty – zdecydowałem się na rozgraniczenie od siebie wagi, pojemności lub liczby sztuk. Z racji tego, że produkt może posiadać tylko jeden z tych atrybutów kolumny te posiadają możliwość przechowywania wartości NULL. W tym momencie nie przychodzi mi do głowy żadne lepsze rozwiązanie na poziomie konstrukcji bazy, które pozwoliłoby na wstawienie wartości tylko do jednej z trzech kolumn. Jeśli ktoś podsunie mi taki pomysł oczywiście będę wdzięczny 🙂 Ponadto produkt przynależy do jakiejś kategorii.

Tabele te powiązane są ze sobą relacją jeden-do-wiele. W kodzie powiązanie takie, za pomocą klucza obcego, wygląda następująco:

ALTER TABLE dbo.tProducts ADD  CONSTRAINT FK_tProducts_ProductsCategories 
FOREIGN KEY(ProductCategoryID) REFERENCES dbo.ProductsCategories (ProductCategoryID);
GO

W wywołaniu takim podajemy nazwę tabeli, w której znajdzie się klucz obcy, informacja, że będzie to CONSTRAINT, oznaczenie, że będzie to klucz obcy (FOREIGN KEY) z podaniem nazwy kolumny na której zostanie on założony oraz zaznaczenie, do jakiej tabeli i kolumny żródłowej odnosił będzie się klucz obcy.

Ograniczenie takie można również prościej wyklikać za pomocą SMS. Uczynić to można np. również przechodząc do tabeli, w której założony ma zostać klucz obcy. W katalogu Keys kliknięcie prawym przyciskiem myszy pozwala na wybranie opcji “New Foreign Key…”. Jakie pola muszą być potem wyklikane nie będę w tym miejscu opisywał.

W swojej tabeli chciałbym, żeby produkty się nie powtarzały. O ile nazwa produktu może być identyczna, to jednak już w połączeniu z określoną wagą, pojemnością lub liczbą sztuk, nie powinna się ona powtarzać. Z tego względu chciałby założyć ograniczenie typu UNIQUE na  wymienionych czterech kolumnach. Kod dla takiego ograniczenia wygląda następująco:

ALTER TABLE dbo.tProducts ADD  CONSTRAINT UQ_tProducts UNIQUE
(
	ProductName,
	Capacity,
	Weight,
	Items
)
GO


Ponownie podajemy nazwę kolumny, nazwę ograniczenia, z zaznaczeniem, że będzie to UNIQUE oraz nazwy kolumn, na których ograniczenie to ma zostać założone. Ponownie chciałbym to również uczynić za pomocą Management Studio. W katalogu Keys (ani Constraints) jednak już nie znajdziemy możliwości dodania ograniczenia typu UNIQUE. W tym celu należy przejść do tabeli i otworzyć ją w trybie Design. Następnie kliknięcie w otwartym oknie prawym klawiszem myszy umożliwia wybranie z menu kontekstowego pozycji Indexes/Keys i po zmianie odpowiednich pól ustawienie Unique Key.

ASP.NET Query strings

 

W moim ostatnim poście utworzone zostało menu z kategoriami, które znalazło się w master page. Stworzone było ono na podstawie danych znajdujących się bazie, przy czym pobierane były dwie kolumny: pierwsza z identyfikatorem kategorii oraz druga z jej nazwą. Następnie dla każdego z elementów tak utworzonego menu stworzone zostały odnośniki, które wykorzystywały jako parametr numer kategorii otrzymany za pomocą funkcji Eval(“ProductsCategories_ID"). Dzisiaj zatem krótki post o kolejnym mechanizmie zarządzania stanem aplikacji po stronie klienta, jakim są tzw. query strings.

Query string jest to informacja dodana na końcu adresu URL. Rozpoczyna się on od znaku zapytania, po którym występuje co najmniej jedna para atrybut/wartość. W przypadku przykładu wykorzystywanego w poprzedni poście adres taki posiada postać:

http://localhost:52467/ProductsList.aspx?CategoryId=3
 
Jeśli byłaby potrzeba przekazania więcej niż jednej pary atrybut/wartość, to powinny one zostać oddzielone znakiem &. Dzięki temu otrzymujemy prosty mechanizm przekazywanie informacji pomiędzy stronami lub kolejnymi żądaniami dla tej samej strony. Zaznaczyć należy, że mechanizm ten działa tylko jeśli żądanie nowej strony następuje za pomocą odnośnika.

Po przekierowaniu na stronę ustawioną za pomocą odnośnika informację z query string można odczytać podając nazwę atrybutu:

Label2.Text = Request.QueryString["CategoryId"];
 
lub też indeks odpowiedniego atrybutu:
Label2.Text = Request.QueryString[0];
 
Należy jednak zauważyć, że o ile w pierwszym przypadku gdy dany query string nie istnieje, nie zostanie przypisana żadna wartość do kontrolki Label, o tyle w drugim, wykorzystującym ideksator w momencie, gdy query string nie istnieje, rzucony zostanie dodatkowo wyjątek ArgumentOutOfRangeException. Aby zapewnić poprawne działanie kodu w drugim przypadku należałoby sprawdzić czy dany query string istnieje np. odczytując liczbę obecnych query stringów:
if (Request.QueryString.Count > 0)
{
    Label2.Text = Request.QueryString[0];
}
Rezultaty odczytane za pomocą query string są oczywiście typu string.

ASP.NET: Menu kategorii na podstawie danych z bazy cz.2 – Menu

 

Po odpowiednim rozmieszczeniu elementów tworzących layout strony czas na właściwą część, czyli utworzenie menu z kategoriami produktów. Najpierw jednak trochę o samym zagadnieniu tworzenia menu. W toolboxie Visual Studio, w części Navigation, można odnaleźć gotową kontrolkę Menu. Kontrolka ta umożliwia utworzenie menu w oparciu o powiązane dane zawarte w pliku XML – Web.sitemap. Dzięki temu odpowiednie pozycje menu przechowywane są w zewnętrznym pliku a nie w danym pliku ze stroną. Po utworzeniu projektu plik ten nie jest dostępny, zatem należy go utworzyć dodając w Solution Explorerze nowy element o nazwie Site Map. Zawartość tego pliku tworzymy pomiędzy znacznikami <siteMap></siteMap>. Poszczególne elementy mogą być natomiast zagnieżdzane i tworzone są poprzez dodanie znacznika <siteMapNode>. Przykładowy plik tworzący pozycje menu może wyglądać następująco:

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
    <siteMapNode url="~/Default.aspx" title="Default"  description="Home Page">
        <siteMapNode url="~/About.aspx" title="About"  description="About me" />
        <siteMapNode url="~/Validation.aspx" title="Validation"  description="Validation of input" />
    </siteMapNode>
</siteMap>
Powiązanie kontrolki Menu z tym źródłem danych spowoduje utworzenie elementu menu o nazwie Default, a po jego wybraniu pojawi się podmenu z dwoma elementami: About oraz Validation.

Na potrzeby mojego projektu chciałbym jednak mieć możliwość automatycznego uaktualniania menu w oparciu o kategorie znajdujące się w mojej bazie danych. W tym celu zamierzam wykorzystać kontrolkę ListView powiązaną z odpowiednią tabelą w bazie danych. Wygląd tabeli z kategoriami przedstawiłem w jednym z poprzednich postów. Zawiera ona jedynie dwie kolumny: jedną z identyfikatorem oraz drugą z nazwą kategorii.

Pierwszym krokiem jest skonfigurowanie źródła danych:

<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
    ConnectionString="<%$ ConnectionStrings:ShoppingAppDbConnectionString %>" 
    SelectCommand="SELECT * FROM [ProductsCategories] ORDER BY [ProductsCategories_ID]">
</asp:SqlDataSource>
 
Do utworzenia połączenia wykorzystywany jest ConnectionString umieszczony w pliku Web.config. Z bazy natomiast, za pomocą polecenia SELECT, pobierane są obie kolumny (aby można było później łatwo przekierować się używając identyfikatora), dane zaś sortowane są w wybranej przeze mnie kolejności dodania danych do tabeli.

Po wstawieniu kontrolki ListView należy jeszcze dokonać jej konfiguracji:

<asp:ListView ID="ListView1" runat="server" DataSourceID="SqlDataSource1">
<EmptyDataTemplate>No items on list</EmptyDataTemplate>
<ItemSeparatorTemplate></ItemSeparatorTemplate>
<ItemTemplate>
   <li>
       <a href="&lt;%# VirtualPathUtility.ToAbsolute(&quot;~/CategoriesList.aspx?CategoryId=&quot; + Eval(&quot;ProductsCategories_ID&quot;)) %&gt;"><%# Eval("Name") %></a>
   </li>
</ItemTemplate>
<LayoutTemplate>
   <ul id="itemPlaceHolderContainer" runat="server" style="font-family: Verdana; font-size: 16px;">
       <li runat="server" id="itemPlaceHolder" />
   </ul>
</LayoutTemplate>
</asp:ListView>
 
Wyświetlane elementy menu poprzedzone są kropkami. Treść danej pozycji w menu jest odpowiednią nazwą pobraną z bazy i jest ona jednocześnie odnośnikiem do strony wyświetlającej produkty dla wybranej kategorii. Odnośnik zawiera w sobie query string utworzony z wykorzystaniem identyfikatora kategorii. Po sformatowaniu za pomocą css:
.contentMenu li a
{
    color: #000;
    text-decoration: none;
    padding: 0.5em;
}

.contentMenu li a:hover
{
    text-decoration: underline;
    color: #ff0000;
}
otrzymujemy treść elementów w kolorze czarnym, jako czysty tekst, natomiast po najechaniu na wybrany element menu zostaje on podkreślony i zmienia się jego kolor na czerwony.

Natomiast dodanie:

.contentMenu li a:active, .contentMenu li a:focus
{
    outline: none;
}
powoduje usunięcie otoczki wokół wybranego elementu menu.
 

Materiały źródłowe: http://msdn.microsoft.com/en-us/library/ecs0x9w5(VS.80).aspx, http://www.asp.net/aspnet-4/videos/tailspin-spyworks-category-menu

ASP.NET: Menu kategorii na podstawie danych z bazy cz.1 – Layout

 

Zgodnie z zapowiedzią, po utworzeniu tabeli z kategoriami dla produktów, przyszedł czas na utworzenie menu ułatwiającego nawigację po grupach produktów. Menu powinno występować na każdej ze stron aplikacji (w pierwszym założeniu), dlatego w tym celu warto wykorzystać mechanizm ASP.NET, jakim jest istnienie tzw. master pages. Nie jest to post o tym właśnie zagadnieniu, zatem napiszę tylko, że master page jest to utworzona strona, która pozwala na zagnieżdżanie w sobie innych stron w odpowiednich miejscach. Sprawia to, że dołączając do tworzonych przez nas stron wybraną master page otrzymujemy powtarzającą się zawartość na każdej ze stron będącej wynikiem takiego połączenia. Master page jest to zatem idealne miejsce do utworzenia elementu występującego na każdej ze stron, czyli menu wyświetlającego kategorie produktów.

Aplikacja tworzona w Visual Web Developer 2010 Express domyślnie posiada wbudowaną już jedną master pager o nazwie Site.Master. Dla przypomnienia, po dodaniu paru elementów do menu nawigacyjnego, wyglądą ona następująco:

poczatkowa SiteMaster

Do moich zastosowań taki layout jest w zupełności wystarczający, a ponadto według mnie jest całkiem przyzwoity. Posiada on jednak jedną wadę, mianowicie posiada tylko jeden ContentPlaceHolder, w którym umieszczana jest cała treść. Może ona zająć w przybliżeniu całą szerokość tworzonej strony. Aby oddzielić menu, a także inne elementy od głównej treści strony proponuję niewielką modyfikację dostępnego szablonu. W miejsce dotychczas istniejącej jednej kolumny utworzę 3 kolumny:

  1. Menu (menu z kategoriami)
  2. Treść (główna zawartość strony)
  3. Gadżety (opcjonalne miejsce na umieszczanie przez użytkownika takich dodatków, jak np. kalendarz)

oraz

  • powyżej nich własny nagłówek
  • poniżej nich własną stopkę

Nagłówek i stopkę postanowiłem utworzyć przyszłościowo – w tym momencie jeszcze nie jestem w stanie określić ich dokładnego zastosowania.

Zmiany w layoucie wymagają modyfikacji zarówno w utworzonym nowym pliku z komponentami, w moim przypadku będzie to SiteNewLayout.Master, jak i w pliku zawierającym style css. Aby przypadkiem czegoś nie skasować również w tym przypadku utworzyłem nowy plik o nazwie SiteNewLayout.css, z zawartością taką jak w domyślnym Site.css.

W layoucie strony w miejsce dotychczasowego wpisu:

<div class="main">
    <asp:ContentPlaceHolder ID="MainContent" runat="server"/>
</div>
wstawione zostały następujące znaczniki:
<div class="top">
    <div class="contentHeader"></div>
    <div class="contentMenu"></div>
    <div class="main">
       <asp:ContentPlaceHolder ID="MainContent" runat="server" />
    </div>
    <div class="contentGadgets"></div>
    <div class="contentFooter"></div>
</div>
 
Widać zatem, że dotychczas występujący ContentPlaceHolder z identyfikatorem MainContent został zachowany i jednocześnie został przeniesiony do kolumny środkowej (czego jeszcze po tym fragmencie kodu nie widać). Zachowana została zatem pełna zgodność z utworzonymi do tej pory stronami wykorzystującymi ContentPlaceHolder o tym właśnie identyfikatorze.

Na marginesie, jako że nie lubię kasować poprzednich treści z pliku, aby wiedzieć jak to wyglądało wcześniej, podaję wskazówkę odnośnie komentowania w plikach z kodem źródłowym strony. Do wstawiania komentarzy po stronie serwera służą znaczniki <%–  –%>

Teraz przyszedł czas na odpowiednie rozmieszczenie przygotowanych kolumn, nagłówka oraz stopki. Zaznaczam w tym miejscu, że nie mam na co dzień styczności z css, a więc jest bardzo duża szansa, że przedstawiony przeze mnie kod nie zadziała poprawnie w większości przypadków i przeglądarek. Przy takich ustawieniach jednak wygląd w Visual Studio oraz w przeglądarce Internet Explorer 9 Beta jest dla mnie zadowalający:

.contentHeader
{
    background-color: Aqua;
}

.contentMenu
{
    width: auto;
    background-color: Fuchsia;
    float: left;
    overflow: hidden;
}

.main
{
    min-height: 420px;
    min-width: 150px;
    background-color: Blue;
    float: left;
}

.contentGadgets
{
    width: 150px;
    background-color: Yellow;
    float: right;
    overflow: hidden;
}

.contentFooter
{
    clear: both;
    width: 100%;
    background-color: Green;   
}
 
Efekt wymienionych wyżej zmian wygląda następująco (kolory zostały celowo przejaskrawione dla łatwiejszego zobrazowania):

poczatkowa2 SiteMaster

Menu z kategoriami znajdzie się w lewej części ekranu w miejscu koloru różowego (fioletowego?). O tym jednak w kolejnej części (już jutro), gdyż w przeciwnym przypadku ten post stałby się zbyt długi.  

Tabela kategorii produktów

 

Dzisiaj krótka notka o przygotowaniu do podjęcia kolejnego zagadnienia, jakim będzie utworzenie menu. W swojej aplikacji chciałbym mieć możliwość przeglądania produktów dodanych do bazy pod kątem ich przynależności do wybranej kategorii żywieniowej. Menu będzie generowane na podstawie zawartości odpowiedniej tabeli, która rzecz jasna w razie potrzeby może ulec zmianie. Stąd konieczność utworzenia najpierw stosownej tabeli.

Do utworzenia kategorii dla produktów korzystam w dużej mierze z Piramidy Zdrowego Żywienia. Ponadto uzupełniona zostanie ona o wybrane przeze mnie kategorie, które mają na celu usprawnienia procesu przeglądania produktów. Przy pierwszym podejściu proponuje zatem następujące kategorie:

  • Produkty zbożowe
  • Warzywa i owoce
  • Przetwory mleczne
  • Mięso
  • Napoje
  • Słodycze
  • Inne

Co do wyglądu samej tabeli nie ma się zbytnio co rozpisywać, bowiem prostsza chyba nie może być:

USE ShoppingAppDb;
GO

IF OBJECT_ID('dbo.ProductsCategories') IS NOT NULL
    DROP TABLE dbo.ProductsCategories;
   
CREATE TABLE dbo.ProductsCategories
(
    ProductsCategories_ID tinyint IDENTITY(1, 1) not null,
    Name varchar(30) not null,
);
W przypadku, gdy tabeli już istnieje jest ona usuwana i tworzona nowa, która składa się z dwóch kolumn. Pierwsza zawiera unikatowy identyfikator kategorii, który automatycznie ulega inkrementacji po uzupełnieniu danymi. Typ tinyint pozwala na przechowanie do 255 kategorii (zawsze należy w miarę możliwości wybierać najmniejszy typ). Druga zawiera nazwę kategorii, która w dodatku nie może być pusta, gdyż nie miałoby sensu przynależenie do nieznanej kategorii.
 

W kolumnie z identyfikatorem założony został klucz główny, który pozwala na jednoznaczne zidentyfikowanie kategorii:

-- Add primary key
ALTER TABLE dbo.ProductsCategories
ADD CONSTRAINT PK_ProductsCategories PRIMARY KEY(ProductsCategories_ID);
Kolumna z nazwą musi mieć natomiast atrybut unikalności, gdyż duplikowanie się kategorii nie jest pożądane:
 
-- Add unique to Name column
ALTER TABLE dbo.ProductsCategories
ADD CONSTRAINT UQ_Name UNIQUE (Name);
Tak utworzona i zmodyfikowana tabela została wypełniona wypunktowanymi na początku kategoriami, a jej zastosowanie będzie prześledzić w kolejnym poście, do którego przeczytania zachęcam, mam nadzieje, już jutro.

ASP.NET: View state – podstawy

 

Kolejny post dotyczący podstaw ASP.NET. Tym razem przyszedł czas na omówienie mechanizmu, jakim jest View state. Mechanizm ten jest wykorzystywany do przechowywania informacji, które muszą być podtrzymane pomiędzy kolejnymi postbackami. Informacja o przechowywanych danych jest zawarta w jednym lub więcej polach ukrytych (hidden fields) dołączanych do kodu html strony. Warto podkreślić, że View state przeznaczony jest wyłącznie do przechowywania danych zawartych na tej samej stronie. W przypadku, gdy dane muszą być dostępne przy przechodzeniu pomiędzy stronami, konieczne jest skorzystanie z innego mechanizmu, jak np. omawianego przeze mnie już Session state.

Informacje można przechowywać wykorzystując właściwość ViewState dla strony. Właściwość ta udostępnia słownik, zatem dane są identyfikowane poprzez pary klucz-wartość. Co do typów możliwych do przechowywania wymienić można: String, Integer, Boolean itd. Po spis wszystkich możliwości odsyłam do materiału źródłowego http://msdn.microsoft.com/en-us/library/bb386448(v=VS.90).aspx 

Przede wszystkim, chcąc przechowywać dane za pomocą View state, należy zadbać o to, aby w kodzie znalazł się formularz (form) z atrybutem runat=”server”. W przypadku Visual Web Developer 2010 Express formularz taki tworzony jest wraz ze standardowo dostępną stroną główną (Site.Master), a więc nie musimy pamiętać, aby tworzyć go osobno dla każdej ze stron. Z racji tego, że dane w View state są przechowywane poprzez ukryte pola wszelkie zmiany mogą być dokonywane jedynie do czasu wystąpienia zdarzenia PreRenderComplete. Gdy strona zostanie wyrenderowana dokonywanie zmian nie jest już możliwe.

Przykładowy kod wykorzystujący View state:

private string storedTemperature = null;
        
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        //Populate RadioButtonList with proper values
        string[] arr = new string[] { "F", "C" };

        ListItem[] myList = new ListItem[arr.Length];

        for (int i = 0; i < myList.Length; i++)
        {
            //ListItem with text and value properties
            myList[i] = new ListItem(arr[i], arr[i]);
        }

        RadioButtonList1.Items.AddRange(myList);
        RadioButtonList1.AutoPostBack = true;
    }
    else
    {
        ViewState["temperature"] = RadioButtonList1.SelectedValue;
    }
    
    if (ViewState["temperature"] != null)
    {
        storedTemperature = (string)ViewState["temperature"];
        Label1.Text = storedTemperature;
    }
}
W powyższym kodzie przedstawiony został wycinek klasy strony. W momencie kiedy nie występuje PostBack, czyli gdy strona jest wczytywana po raz pierwszy, tworzony jest kontrolka RadioButtonList. Wypełniana ona jest ciągami znakowymi umożliwiającymi wybór na stronie pomiędzy wyświetlaniem temperatury w stopniach Fahrenheita i Celsjusza. Ustawienie właściwości AutoPostBack na true powoduje, że PostBack jest automatycznie wykonywany po wyborze jednej z opcji (nie jest to zachowanie domyślne). Dane do View state można zapisywać podobnie jak pokazałem to dla Session state, czyli poprzez odwołanie do indeksu. Przypisanie wybranej wartości następuje w momencie, gdy stwierdzone zostanie wystąpienie PostBack. Bez znaczenia czy nastąpił PostBack, czy też nie (kod napisany w ten sposób tylko dla demonstracji sposobu sprawdzenia istnienia zmiennej), sprawdzana jest zawartość View state dla odpowiedniej zmiennej. Jeśli zmienna posiada przypisaną wartość to jest ona odczytywana i przypisywana kontrolce Label. Po odczycie zmiennej z View state konieczne jest oczywiście zastosowanie rzutowania.
 
W View state można również przechowywać własne obiekty, z tym jednak zastrzeżeniem, że klasa musi posiadać atrybut [Serializable()]. W przeciwnym przypadku otrzymywany po uruchomieniu aplikacji jest komunikat w stylu: “Type ‚ShoppingApp.Person’ in Assembly ‚ShoppingApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ is not marked as serializable.” Biorąc zatem za przykład utworzoną w jednym z poprzednich postów klasę Person, powinna ona wyglądać następująco:
 
[Serializable()]
    public partial class Person
    {
        protected string firstName;
        protected string lastName;
        ...
    }
}
W tym momencie można przechowywać w View state obiekty własnej klasy:
 
protected void Page_PreRender(object sender, EventArgs e)
{
  Person p = new Person("Walt", "Kowalsky");
  ViewState["Person"] = p;
}
Odczyt następuje w taki sam sposób jak dla pokazanej wcześniej zmiennej temperature.