Oddzielanie kodu od tre
ś
ci
W osiemnastu poprzednich rozdziałach książki przedstawionych zostało kilka sposobów na
oddzielanie interfejsu użytkownika stron ASP.NET od kodu który go obsługuje (napisanego w
języku VB.NET). Przykładowo, zostały przedstawione sposoby przenoszenia kodu, który nie jest
bezpośrednio związany z interfejsem użytkownika do obiektów biznesowych, dzięki czemu strony
ASP.NET mogą zawierać wyłącznie kod związany z obsługą prezentacji danych. Co więcej,
wszelkie polecenia SQL można zapisać w bazie danych w formie procedur zachowanych i usunąć
je ze stron ASP.NET (zagadnienia te zostały omówione w rozdziale 12., pt.: „Zastosowanie
zaawansowanych technik obsługi danych”). Wszelkie ustawienia i zmienne można natomiast
zapisać w plikach konfiguracyjnych, takich jak
web.config
.
W tym rozdziale zostanie przedstawionych kilka bardziej zaawansowanych metod separacji kodu
ź
ródłowego od zawartości strony, czyli kodu kontrolującego działanie aplikacji od kodu
odpowiedzialnego za prezentację danych (na przykład: elementów sterujących HTML oraz
internetowych elementów sterujących). Programiści ASP.NET bardzo oczekiwali możliwości
takiej separacji, gdyż dzięki nim można uprościć strony i logicznie zgrupować wykorzystywany w
nich kod. Przecież strony ASP.NET służą wyłącznie do prezentacji interfejsu użytkownika, a
zatem po co miałby być w nich umieszczany kod o innym przeznaczeniu?
W tym rozdziale poznamy także sposoby dostosowywania stron ASP.NET do pochodzenia
użytkownika. Metody te pozwalają na modyfikację zawartości stron ASP.NET zupełnie
niezależnie od wydzielonego kodu stron.
W tym rozdziale przedstawionych zostanie bardzo wiele przykładów, a zatem… zaczynajmy!
W tym rozdziale zostaną omówione następujące zagadnienia:
•
Czym jest kod obsługi formularzy?
•
Jak używać kodu obsługi przy tworzeniu stron ASP.NET.
•
W jaki sposób elementy sterujące użytkownika mogą korzystać z kodu obsługi.
•
Jak określić pochodzenie użytkownika (na podstawie używanego języka).
•
Jak określić informacje o kulturze i regionie użytkownika.
•
W jaki sposób można wydzielić ze stron ASP.NET najczęściej używane łańcuchy
znaków i zapisać je w niezależnych plikach zasobów.
Potrzeba rozdzielania ró
ż
nych rodzajów
kodu
Być może przypominasz sobie z dyskusji przedstawionej w rozdziale 2., że ASP.NET stara się
uprościć życie programistom umożliwiając niezależne grupowanie kodu ASP.NET oraz kodu
HTML. Na przykład, większość (o ile nie cały) kod ASP.NET powinien być umieszczany w
blokach
SCRIPT
na samym początku stron ASP.NET i oddzielony od kodu HTML. Oddzielenie
obu rodzajów kodu zostało przedstawione na rysunku 19.1.
Kod ASP.NET
<%@Page Language="VB" %>
<%@Import Namespace="System.Xml" %>
<%@Import Namespace="System.Xml.XPath" %>
<script runat="server">
sub SelectData(obj as object,e as eventargs)
Dim objDocument as New XPathDocument _
(Server.MapPath("../r11/books.xml"))
Dim objNav as XPathNavigator = objDocument.CreateNavigator
lblMessage.Text = ""
try
dim objIterator as XPathNodeIterator = _
objNav.Select(tbQuery.Text)
While objIterator.MoveNext()
lblMessage.Text += "<" & _
objIterator.Current.Name & "> " & _
objIterator.Current.Value & "<br>"
end while
catch ex As Exception
lblMessage.Text = ex.Message
end try
end sub
</script>
Kod HTML
<html><body>
<form runat="server">
<h2>Zapytania XPath</h2>
<p>
Na przyład:<br>
<b><code>//book[last()]/@ISBN/text()</b></code> lub
<b><code>descendant::book/author/last-name</b></code><p>
Podaj zapytanie XPath:
<asp:Textbox id="tbQuery" runat=server/>
<asp:Button id="btnSubmit" text="Wykonaj zapytanie"
runat=server OnClick="SelectData"/><p>
<asp:Label id="lblMessage" runat=server/>
</form>
</body></html>
Tworzenie kodu w sposób przedstawiony na rysunku 19.1 jest bardzo korzystne i to z wielu
powodów — łatwiej można modyfikować zarówno kod ASP.NET jak i kod HTML, kod źródłowy
strony staje się w ten sposób bardziej logiczny i nie trzeba szukać w nim bloków kodu
wykonywalnego. Niemniej jednak możliwości rozdzielania różnych rodzajów kodu jakie daje
ASP.NET są jeszcze większe. Dzięki wykorzystaniu kodu obsługi oraz plików zasobów można
całkowicie oddzielić kod od zawartości strony. Pierwsze z powyższych rozwiązań umożliwia
zapisywanie kodu w niezależnych plikach, natomiast drugie — zgrupowanie często
wykorzystywanych wartości (takich jak na przykład komunikaty o błędach) i zapisanie ich w
jednym miejscu, niezależnym od strony ASP.NET.
Kod obsługi formularzy
Patrząc na rysunek 19.1 nie trudno zauważyć korzyści płynące z oddzielenia kodu od treści strony.
Ale co by się stało, gdyby można było usunąć całą początkową zawartość pliku
.aspx
? Dzięki
temu istniałaby możliwość faktycznego oddzielenia kod ASP.NET do zawartości strony.
Technologia ASP.NET pozwala na osiągnięcie takiej separacji dzięki wykorzystaniu kodu obsługi
formularzy. Kod obsługi formularzy jest sposobem na całkowite oddzielenie każdego i całego
kodu ASP.NET od kodu interfejsu użytkownika. Teraz zamiast jednego pliku z rysunku 19.1
otrzymujemy dwa pliki przedstawione na rysunku 19.2.
Wył
ą
cznie kod HTML
<%@ Page Inherits="CodeBehind2" src="CodeBehind2.vb" %>
<html><body>
<form runat="server">
<asp:Calendar id="Calendar1" runat="server"
OnSelectionChanged="DateChanged"
Cellpadding="5" Cellspacing="5"
DayHeaderStyle-Font-Bold="True"
DayNameFormat="Short"
Font-Name="Arial" Font-Size="12px"
height="250px"
NextPrevFormat="ShortMonth"
NextPrevStyle-ForeColor="white"
SelectedDayStyle-BackColor="#ffcc66"
SelectedDayStyle-Font-Bold="True"
SelectionMode="DayWeekMonth"
SelectorStyle-BackColor="#99ccff"
SelectorStyle-ForeColor="navy"
SelectorStyle-Font-Size="9px"
ShowTitle="true"
TitleStyle-BackColor="#ddaa66"
TitleStyle-ForeColor="white"
TitleStyle-Font-Bold="True"
TodayDayStyle-Font-Bold="True" />
</form>
Wybrałe
ś
:
<asp:Label id="lblMessage" runat="server"/>
</body></html>
Wył
ą
cznie kod ASP.NET (kod obsługi formularza)
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Public Class CodeBehind2 : Inherits Page
public lblMessage as Label
public Calendar1 as Calendar
Public sub Page_Load(obj as object, e as eventargs)
if not Page.IsPostBack then
Calendar1.SelectedDate = DateTime.Now
lblMessage.Text = Calendar1.SelectedDate. _
ToString("dddd, MMMM dd yyyy")
end if
End Sub
Public sub DateChanged(obj as object, e as eventargs)
if Calendar1.SelectedDates.Count > 1 then
lblMessage.Text = Calendar1.SelectedDates(0). _
ToString("dddd, MMMM dd yyyy") & " do " & _
Calendar1.SelectedDates(Calendar1.SelectedDates. _
Count - 1).ToString("dddd, MMMM dd yyyy")
else
lblMessage.Text = Calendar1.SelectedDate. _
ToString("dddd, MMMM dd yyyy")
end if
End Sub
End Class
Przeanalizujmy jeszcze raz sposób działania ASP.NET, abyśmy lepiej zrozumieli model
programowania wykorzystujący kod obsługi.
Gdy klient po raz pierwszy przesyła żądanie dotyczące jakiejś strony ASP.NET (pliku z
rozszerzeniem
.aspx
), ASP.NET przetwarza tę stronę i analizuje wszystkie wykorzystywane na
niej komponenty (na przykład, elementy sterujące serwera). Następnie ASP.NET tworzy klasę
dynamiczną dla danego pliku
.aspx
. To właśnie tak klasa jest następnie kompilowana,
wykonywana i to ona generuje kod HTML przesyłany następnie do klienta. Musi to być kasa
potomna klasy
System.Web.UI.Page
, która zawiera definicje wykorzystywane przez wszystkie
stron ASP.NET. Cały ten proces jest wykonywany w sposób całkowicie niezauważalny, w
momencie zgłoszenia żądania dotyczącego strony ASP.NET.
Jednak plik
.aspx
nie musi być bezpośrednią klasą potomną klasy
Page
— o ile tylko w
jakikolwiek sposób będzie dziedziczyć po tej klasie, to wszystko będzie w porządku. Oznacza to,
ż
e można stworzyć pewną klasę pośrednią, dziedziczącą po klasie
Page
i zażądać, aby plik
.aspx
dziedziczył po tej klasie pośredniej. Ta nowa klasa pośrednia może udostępniać dowolne
możliwości funkcjonalne, które będą dostępne dla plików
.aspx
. Wzajemne relacje pomiędzy tymi
wszystkimi klasami zostały przedstawione na rysunku 19.3.
Ta nowa klasa pośrednia jest naszym kodem obsługi formularzy. Definiuje ona możliwości
funkcjonalne z których może korzystać strona ASP.NET. Jednak przy tak dużej ilości
wykorzystywanych klas łatwo będzie się można pogubić. W zasadzie wszystko sprowadza się do
tego, iż strona ASP.NET musi być klasą potomną klasy
Page
, lecz od programisty zależy
określenie hierarchii tego pokrewieństwa.
Wprowadzenie klasy kodu obsługi nie daje żadnych oczywistych korzyści. Strona kodu obsługi
nie zawiera żadnych własnych możliwości funkcjonalnych, a zatem dziedzicząc po niej strona
ASP.NET niczego nie zyskuje. Strona kodu obsługi także w żaden sposób nie wspomaga
wykonywania stron ASP.NET. Niemniej jednak wykorzystanie tej strony pozwala na przeniesienie
kodu do klasy pośredniej, dzięki czemu w stronie ASP.NET może pozostać znacznie uproszczony
kod obsługi interfejsu użytkownika.
Wykorzystanie kodu obsługi w stronach ASP.NET
Stworzenie kodu obsługi formularza jest niezwykle proste; cały proces przypomina tworzenie
obiektów biznesowych, z tą różnicą iż niczego nie trzeba kompilować. Niemniej jednak trzeba
przedsięwziąć pewne środki bezpieczeństwa, aby zapewnić że wszystko będzie działać poprawnie.
Listing 19.1 przedstawia typową postać strony ASP.NET prezentującej informacje pobierane z
bazy danych. Za chwilę, na podstawie tej strony wygenerujemy kod obsługi formularza.
Listing 19.1.
Typowa strona ASP.NET zawiera zarówno kod programu jak i kod HTML.
1
<%@ Page Language="VB" %>
2
<%@ Import Namespace="System.Data" %>
3
<%@ Import Namespace="System.Data.OleDb" %>
4
5
<script runat="server">
6
'deklarujemy poł
ą
czenie
7
dim strConnString as string = "Provider=" & _
8
"Microsoft.Jet.OLEDB.4.0;" & _
9
"Data Source=C:\ASPNET\data\banking.mdb"
10
dim objConn as new OleDbConnection(strConnString)
11
12
sub Page_Load(obj as Object, e as EventArgs)
13
if Not Page.IsPostBack then
14
FillDataGrid()
15
end if
16
end sub
17
18
private sub FillDataGrid(Optional EditIndex as integer=-1)
19
'otwieramy poł
ą
czenie
20
dim objCmd as OleDbCommand = new OleDbCommand _
21
("select * from tblUsers", objConn)
22
dim objReader as OleDbDataReader
23
24
try
25
objCmd.Connection.Open()
26
objReader = objCmd.ExecuteReader
27
catch ex as OleDbException
28
lblMessage.Text = "Bł
ą
d pobierania informacji z bazy danych."
29
end try
30
31
DataGrid1.DataSource = objReader
32
DataGrid1.DataBind()
33
34
objReader.Close
35
objCmd.Connection.Close()
36
end sub
37
</script>
38
39
<html><body>
40
<form runat="server">
41
<asp:Label id="lblMessage" runat="server" />
42
43
<asp:DataGrid id="DataGrid1" runat="server"
44
BorderColor="black"
45
GridLines="Vertical"
46
cellpadding="4"
47
cellspacing="0"
48
width="100%"
49
Font-Name="Arial"
50
Font-Size="8pt"
51
HeaderStyle-BackColor="#cccc99"
52
ItemStyle-BackColor="#ffffff"
53
AlternatingItemStyle-Backcolor="#cccccc"
54
AutoGenerateColumns="True" />
55
</asp:DataGrid><p>
56
</form>
57
</body></html>
Analiza
Kod przedstawiony na powyższym listingu nawiązuje połączenie z bazą danych stworzoną w
rozdziale 8, pt.: „Podstawowe wiadomości na temat tworzenia baz danych”. W wierszach od 7. do
9. jest tworzony łańcuch zapytania, a w wierszu 10. obiekt
OleDbConnection
. W procedurze
Page_Load
wywoływana jest procedura
FillDataGrid
, która pobiera informacje z bazy danych
i wiąże je z elementem sterujący
DataGrid
zdefiniowanym w wierszach od 43. do 55. Wykonanie
powyższej strony powoduje wygenerowanie wyników przedstawionych na rysunku 19.4.
Rysunek 19.4.
Prosta strona ASP.NET pobierająca informacje z bazy danych.
A teraz, bazując na kodzie przedstawionym na listingu 19.1, stworzymy kod obsługi formularza.
W pierwszej kolejności należy stworzyć klasę potomną klasy
System.Web.UI.Page
. Oto
przykład szkieletu takiej klasy:
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Data
Imports System.Data.OleDb
Public Class CodeBehind1 : Inherits Page
‘tu zostanie wstawiony kod klasy
End Class
Powyższy szkielet klasy będzie podstawą do usunięcia kodu skryptu ze strony przedstawionej na
listingu 19.1. Należy pamiętać, że tworząc klasę języka VB.NET należy własnoręcznie
zaimportować wszystkie przestrzenie nazw, które w ASP.NET były importowane automatycznie.
Dodatkowo trzeba także zaimportować przestrzenie nazw
System.Data
oraz
System.Data.OleDb
, gdyż tworzona strona ASP.NET będzie z nich korzystać przy
wyświetlaniu danych.
Kolejnym krokiem jest stworzenie wszystkich zmiennych publicznych, których będziemy
potrzebować. Ze względu na naturę kodu obsługi formularzy, proces ten będzie się nieco różnić do
tego, do czego jesteśmy przyzwyczajeni. Przyjrzyjmy się nieco dokładniej temu zagadnieniu.
Klasa kodu obsługi będzie używana do kontroli interfejsu użytkownika (czyli, formularza
internetowego) — zawiera ona całą logikę konieczną do obsługi zdarzeń generowanych przez
interfejs użytkownika. Jednak klasa ta nie zawiera żadnych elementów interfejsu użytkownika —
bez wyjątków są one umieszczane w kodzie strony
.aspx
. A zatem mamy problem. Klasa kodu
obsługi musi kontrolować interfejs użytkownika, który w całości jest definiowany w odrębnym
pliku. W jaki sposób można to zrobić?
Zaraz poznasz powód dla którego strona ASP.NET ma być klasą potomną klasy kodu obsługi.
Otóż jeśli wszystkie konieczne elementy interfejsu użytkownika zastaną zadeklarowane w klasie
kodu obsługi, to plik
.aspx
odziedziczy je. Oznacza to, że kod obsługi stworzy i obsłuży wszystkie
elementy interfejsu użytkownika, lecz nie będzie ich wyświetlać. Za wyświetlenie elementów
interfejsu użytkownika wciąż będzie odpowiedzialna strona ASP.NET. Powyższa idea została
zilustrowana na rysunku 19.5.
Przenieśmy zatem kod z listingu 19.1 do kodu obsługi formularza przedstawionego na listingu
19.2.
Listing 19.2.
Kod obsługi zawiera wszelkie możliwości funkcjonalne związane z obsługą
interfejsu użytkownika (
CodeBehind1.vb
)
1
Imports System
2
Imports System.Web
3
Imports System.Web.UI
4
Imports System.Web.UI.WebControls
5
Imports System.Data
6
Imports System.Data.OleDb
7
8
Public Class CodeBehind1 : Inherits Page
9
'deklarujemy zmienne publiczne, które oddziedziczy
10
'plik .aspx.
11
public lblMessage as Label
12
public DataGrid1 as DataGrid
13
14
'deklarujemy ła
ń
cuch poł
ą
czenia
15
private strConnString as string = "Provider=" & _
16
"Microsoft.Jet.OLEDB.4.0;" & _
17
"Data Source=C:\ASPNET\data\banking.mdb"
18
private objConn as new OleDbConnection(strConnString)
19
20
sub Page_Load(obj as Object, e as EventArgs)
21
if Not Page.IsPostBack then
22
FillDataGrid()
23
end if
24
end sub
25
26
private sub FillDataGrid(Optional EditIndex as integer=-1)
27
'otwieramy poł
ą
czenie
28
dim objCmd as OleDbCommand = new OleDbCommand _
29
("select * from tblUsers", objConn)
30
dim objReader as OleDbDataReader
31
32
try
33
objCmd.Connection.Open()
34
objReader = objCmd.ExecuteReader
35
catch ex as OleDbException
36
lblMessage.Text = "Bł
ą
d przy pobieraniu danych z bazy."
37
end try
38
39
DataGrid1.DataSource = objReader
40
DataGrid1.DataBind()
41
42
objReader.Close
43
objCmd.Connection.Close()
44
end sub
45
46
End Class
Analiza
Zapisz powyższy kod w pliku o nazwie
CodeBehind1.vb
. W wierszach od 1. do 6. są importowane
niezbędne przestrzenie nazw, a w wierszu 8. rozpoczyna się deklaracja klasy kodu obsługi. Zwróć
uwagę, iż nosi ona nazwę
CodeBehind1
i jest klasą potomną klasy
Page
.
Na stronie ASP.NET z listingu 19.1 zostały użyte dwa elementy sterujące serwera — etykieta o
nazwie
lblMessage
oraz element
DataGrid
nazwie
DataGrid1
. W naszej klasie kodu obsługi
elementy te zostały zadeklarowane jako zmienne (odpowiednio w wierszach 10. i 11.). Teraz
strona ASP.NET odziedziczy te elementy oraz wszelkie możliwości funkcjonalne zdefiniowane w
klasie. Pozostała część kodu nie różni się niczym do zawartości strony z listingu 19.1; oczywiście
nie dotyczy to kodu HTML który w pliku
CodeBehind1.vb
nie został umieszczony. Teraz
przyjrzyjmy się kodowi strony
.aspx
, która będzie dziedziczyć po klasie kodu obsługi.
Listing 19.3.
Dziedziczenie po klasie kodu obsługi.
1
<%@ Page Inherits="CodeBehind1" src="CodeBehind1.vb" %>
2
3
<html><body>
4
<form runat="server">
5
<asp:Label id="lblMessage" runat="server" />
6
7
<asp:DataGrid id="DataGrid1" runat="server"
8
BorderColor="black" GridLines="Vertical"
9
cellpadding="4" cellspacing="0"
10
width="100%" Font-Name="Arial"
11
Font-Size="8pt"
12
HeaderStyle-BackColor="#cccc99"
13
ItemStyle-BackColor="#ffffff"
14
AlternatingItemStyle-Backcolor="#cccccc"
15
AutoGenerateColumns="True" />
16
</asp:DataGrid><p>
17
</form>
18
</body></html>
Analiza
Zapisz powyższy fragment kodu w pliku o nazwie
listing1903.aspx
i umieść go w tym samym
folderze, w którym został zapisany plik zawierający klasę kodu obsługi. Za wyjątkiem pierwszego
wiersza powyższy kod niczym nie różni się od fragmentu kodu HTML z listingu 19.1. Jednak w
pierwszym wierszu pojawił się nowy element — dyrektywa
@ Page
z atrybutami
Inherits
oraz
src
. Pierwszy z tych atrybutów —
Inherits
— określa klasę nadrzędną, po której będzie
dziedziczyć strona ASP.NET. W naszym przypadku chcemy, aby klasą nadrzędną strony była
klasa kodu obsługi —
CodeBehind1
. Atrybut
src
zawiera ścieżkę dostępu do pliku kodu obsługi
(oznacza to, iż plik zawierający kod obsługi oraz plik
.aspx
nie muszą być umieszczone w tym
samym folderze). I to wszystko! Wystarczy wyświetlić tę stronę w przeglądarce, a uzyskane
wyniki nie powinny się niczym różnić od wyników przedstawionych na rysunku 19.4.
Elementy sterujące serwera
DataGrid
oraz
Label
z listingu 19.3 są kopiami zmiennych
klasowych (klasy
CodeBehind1
), zadeklarowanych na listingu 19.2. A zatem, dowolny kod
umieszczony wewnątrz klasy kodu obsługi i odwołujący się do tych zmiennych, będzie miał pełen
dostęp do wszystkich właściwości odpowiednich elementów sterujących formularza internetowego
(takich jak tekst wyświetlany przez etykietę).
Przyjrzyjmy się teraz kodowi obsługującemu zdarzenia generowane przez formularz. Tym razem
oba pliki zostaną utworzone od samego początku. Listing 19.4 przedstawia kod strony ASP.NET
definiującej interfejs użytkownika.
Notatka
Nic nie stoi na przeszkodzi, aby elementy interfejsu u
ż
ytkownika oraz logika działania strony
ASP.NET były umieszczone w tym samym pliku. Kod programu nie musi by
ć
w cało
ś
ci
umieszczany w klasie kodu obsługi, a w pliku kodu obsługi mo
ż
na umieszcza
ć
elementy
interfejsu u
ż
ytkownika. Co wi
ę
cej, elementy interfejsu u
ż
ytkownika deklarowane w kodzie
obsługi, mog
ą
by
ć
obsługiwane przez kod zdefiniowany w pliku
.aspx
. Niemniej jednak nie ma
ż
adnego powodu, aby umieszcza
ć
kod w stronie ASP.NET je
ś
li mo
ż
na go zaimplementowa
ć
jako kod obsługi.
Listing 19.4.
Plik
.aspx
bez kodu ASP.NET.
1
<%@ Page Inherits="CodeBehind2" src="CodeBehind2.vb" %>
2
3
<html><body>
4
<form runat="server">
5
<asp:Calendar id="Calendar1" runat="server"
6
OnSelectionChanged="DateChanged"
7
Cellpadding="5" Cellspacing="5"
8
DayHeaderStyle-Font-Bold="True"
9
DayNameFormat="Short"
10
Font-Name="Arial" Font-Size="12px"
11
height="250px"
12
NextPrevFormat="ShortMonth"
13
NextPrevStyle-ForeColor="white"
14
SelectedDayStyle-BackColor="#ffcc66"
15
SelectedDayStyle-Font-Bold="True"
16
SelectionMode="DayWeekMonth"
17
SelectorStyle-BackColor="#99ccff"
18
SelectorStyle-ForeColor="navy"
19
SelectorStyle-Font-Size="9px"
20
ShowTitle="true"
21
TitleStyle-BackColor="#ddaa66"
22
TitleStyle-ForeColor="white"
23
TitleStyle-Font-Bold="True"
24
TodayDayStyle-Font-Bold="True" />
25
</form>
26
Wybrałe
ś
:
27
<asp:Label id="lblMessage" runat="server"/>
28
</body></html>
Powyższy listing zawiera element sterujący serwera
Calendar
oraz etykietę. Choć plik kodu
obsługi nie został jeszcze utworzony, to jednak nazwa klasy oraz ścieżka dostępu do pliku zostały
już podane w dyrektywnie
@ Page
umieszczonej w pierwszym wierszu. W ten sposób już
zawczasu wiadomo jaki plik należy utworzyć. W wierszu 6. określana jest procedura obsługi
zdarzenia
SelectionChanged
kalendarza (elementu sterującego serwera
Calendar
). Należy
także zwrócić uwagę na nazwy obu elementów sterujących —
Calendar1
oraz
lblMessage
.
Próba wyświetlenia tej strony bez wcześniejszego utworzenia kodu obsługi, spowoduje
wystąpienie błędów. Błędy te zostaną zgłoszone z dwóch powodów. Po pierwsze ASP.NET będzie
szukać klasy kodu obsługi podanej w atrybucie
Inherit
dyrektywy
@ Page
lecz nie będzie w
stanie jej znaleźć. Po drugie, do obsługi zdarzenia
SelectionChanged
ma być wykorzystana
procedura
DateChanged
, która nigdzie nie została zdefiniowana. Aby rozwiązać te problemy,
wystarczy stworzyć kod obsługi przedstawiony na listingu 19.5.
Listing 19.5.
Kontrola zdarzeń generowanych przez element sterujący kalendarza
(
CodeBehind2.vb
).
1
Imports System
2
Imports System.Web
3
Imports System.Web.UI
4
Imports System.Web.UI.WebControls
5
6
Public Class CodeBehind2 : Inherits Page
7
public lblMessage as Label
8
public Calendar1 as Calendar
9
10
Public sub Page_Load(obj as object, e as eventargs)
11
if not Page.IsPostBack then
12
Calendar1.SelectedDate = DateTime.Now
13
lblMessage.Text = Calendar1.SelectedDate. _
14
ToString("dddd, MMMM dd yyyy")
15
end if
16
End Sub
17
18
Public sub DateChanged(obj as object, e as eventargs)
19
if Calendar1.SelectedDates.Count > 1 then
20
lblMessage.Text = Calendar1.SelectedDates(0). _
21
ToString("dddd, MMMM dd yyyy") & " do " & _
22
Calendar1.SelectedDates(Calendar1.SelectedDates. _
23
Count - 1).ToString("dddd, MMMM dd yyyy")
24
else
25
lblMessage.Text = Calendar1.SelectedDate. _
26
ToString("dddd, MMMM dd yyyy")
27
end if
28
End Sub
29
End Class
Analiza
Zapisz powyższy fragment kodu w pliku o nazwie
CodeBehind2.vb
. W wierszu 6. deklarowana
jest klasa kodu obsługi o nazwie
CodeBehind2
; co odpowiada nazwie podanej w atrybucie
Inherit
dyrektywy
@ Page
z listingu 19.4. W wierszach 7. i 8. tworzone są dwie zmienne
publiczne —
lblMessage
oraz
Calendar1
. Należy zwrócić uwagę, iż są to te same nazwy które
nadaliśmy elementom sterującym w pliku
.aspx
. Procedura
Page_Load
zapisana w wierszach od
10. do 16. wybiera w kalendarzu dzisiejszą datę i wyświetla stosowny komunikat wykorzystując w
tym celu element sterujący etykiety.
I w końcu, metoda
DataChanged
której kod rozpoczyna się w wierszu 18. służy do obsługi
zdarzeń
SelectionChanged
kalendarza (patrz wiersz 6. listingu 19.4). Metoda ta wyświetla na
stronie datę w rozbudowanym formacie. Jeśli jednocześnie zostanie wybranych kilka dni (co
można sprawdzić za pomocą właściwości
SelectedDates.Count
), to chcemy poinformować
użytkownika o tym fakcie wyświetlając na stronie odpowiedni przedział dat (patrz wiersze od 19.
do 23.). W przeciwnym przypadku wyświetlana jest jedna — wybrana data (patrz wiersze 25. i
26.). Teraz wyświetlenie w przeglądarce strony z listingu 19.4 da rezultaty przedstawione na
rysunku 19.6.
Rysunek 19.6.
Kod obsługi formularza obsługuje zdarzenia generowane na stronie ASP.NET
Teraz możesz już całkowicie oddzielić kod obsługujący formularze internetowe od warstwy
prezentacji danych.
Wykorzystanie kodu obsługi w elementach
steruj
ą
cych u
ż
ytkownika
Wykorzystanie kodu obsługi w elementach sterujących użytkownika różni się nieco od
wykorzystania go do obsługi interfejsu użytkownika stron ASP.NET. Konkretnie rzecz biorąc, w
tym przypadku, kod obsługi musi być klasą potomną klasy
System.Web.UI.UserControl
(a
nie klasy
Page
). Więcej informacji na temat tworzenia i wykorzystania elementów sterujących
użytkownika można znaleźć w rozdziale 6., pt.: „Ciąg dalszy wiadomości na temat tworzenia
formularzy internetowych”. Na listingach od 19.6 do 19.8 przedstawiony został zmodyfikowany
kod aplikacji kalkulatora stworzonej w rozdziale 2., w którym został wykorzystany element
sterujący użytkownika oraz kod obsługi formularza.
Listing 19.6.
Element sterujący użytkownika (
Calculator.ascx
).
1
<%@ Control Inherits="CalculatorControl" src="Calculator.vb" %>
2
3
Liczba 1: <asp:textbox id="tbNumber1" runat="server"/><br>
4
Liczba 2: <asp:textbox id="tbNumber2" runat="server"/><p>
5
<asp:button id="btAdd" runat="server" Text="+"
6
OnClick="btOperator_Click" />
7
<asp:button id="btSubtract" runat="server" Text="-"
8
OnClick="btOperator_Click"/>
9
<asp:button id="btMultiply" runat="server" Text="*"
10
OnClick="btOperator_Click"/>
11
<asp:button id="btDivide" runat="server" Text="/"
12
OnClick="btOperator_Click"/><p>
13
Wynik:
14
<asp:label id="lblMessage" runat="server"/>
Analiza
Zapisz powyższy fragment kodu w pliku o nazwie
Calculator.ascx
. Element sterujący
użytkownika przedstawiony na powyższym listingu jest bardzo prosty. Zawiera on kod HTML
wyświetlający dwa pola tekstowe w których użytkownicy mogą wpisywać liczby, cztery przyciski
umożliwiające wykonywanie podstawowych operacji arytmetycznych oraz etykietę służącą do
wyświetlania wyników. W pierwszym wierszu powyższego kodu, zamiast dyrektywy
@ Page
została umieszczona dyrektywa
@ Control
, jednak składnia jej zapisu jest identyczna — także w
tym przypadku klasa kodu obsługi jest określana przy użyciu atrybutu
Inherits
a nazwa pliku w
jaki został on zapisany — przy użyciu atrybutu
src
. Koniecznie należy zapamiętać nazwy
wszystkich elementów sterujących, gdyż będą one konieczne przy tworzeniu kodu obsługi
formularza. Plik kodu obsługi dla powyższego elementu sterującego użytkownika został
przedstawiony na listingu 19.7.
Listing 19.7.
Klasa kod obsługi (
Calculator.vb
).
1
Imports System
2
Imports System.Web
3
Imports System.Web.UI
4
Imports System.Web.UI.WebControls
5
6
Public Class CalculatorControl : Inherits UserControl
7
public lblMessage as Label
8
public btAdd as Button
9
public btSubtract as Button
10
public btMultiply as Button
11
public btDivide as Button
12
public tbNumber1 as TextBox
13
public tbNumber2 as TextBox
14
15
Sub btOperator_Click(obj as object, e as eventargs)
16
lblMessage.Text = Operate(CType(obj, Button).Text, _
17
tbNumber1.Text, tbNumber2.Text).ToString
18
End Sub
19
20
private function Operate(operator as string, number1 as string,
optional number2 as string = "1") as double
21
select case operator
22
case "+"
23
Operate = CDbl(number1) + CDbl(number2)
24
case "-"
25
Operate = CDbl(number1) - CDbl(number2)
26
case "*"
27
Operate = CDbl(number1) * CDbl(number2)
28
case "/"
29
Operate = CDbl(number1) / CDbl(number2)
30
end select
31
end function
32
End Class
Analiza
Zapisz powyższy kod w pliku o nazwie
Calculator.vb
. Możliwości funkcjonalne
zaimplementowane w tym przykładzie są znacznie bardziej skomplikowane do tych z listingu
19.5, niemniej jednak sposób ich zapisu jest identyczny. W pierwszej kolejności, w wierszach do
1. do 4., są importowane niezbędne przestrzenie nazw. W wierszu 6. rozpoczyna się deklaracja
klasy
CalculatorControl
, będącej klasą potomną klasy
UserControl
(a nie klasy
Page
).
Następnie, w wierszach od 7. do 13. zostały zadeklarowane publiczne zmienne klasowe
odpowiadające elementom sterującym wykorzystywanym w tworzonym elemencie sterującym
użytkownika.
Gdy użytkownik kliknie jeden z przycisków elementu sterującego, zostanie wykonana procedura
obsługi zdarzenia o nazwie
btOperator_Clicked
. Procedura ta wywoła metodę
Operate
pobierającą trzy argumenty — operację jaką należy wykonać oraz dwa operandy. W momencie
przesyłania żądania, przycisk jaki spowodował zgłoszenie zdarzenia będzie reprezentowany przez
zmienną
obj
(patrz wiersz 15.) Przyjrzyjmy się pierwszemu argumentowi wywołania metody
Operate
zapisanemu w wierszu 16.:
Ctype(obj, Button).Text
Powyższe wyrażenie pobiera wartość właściwości
Text
przycisku, który spowodował zgłoszenie
zdarzenia (na przykład, dla przycisku
btAdd
będzie to „+”). Jeśli tą samą czynności mielibyśmy
wykonać w kodzie strony ASP.NET, to wystarczyłoby użyć wyrażenia
obj.Text
, niezależnie od
tego, że klasa
Object
nie dysponuje właściwością o nazwie
Text
. Jednak klasy języka VB.NET
działają inaczej niż strony ASP.NET. Konkretnie rzecz biorąc nie dysponują one możliwością
późnego łączenia.
Nowe wyrażenie
Późne łączenie
(ang.: late binding) oznacza, że zmienne typu
Object
(takie jak
obj
) nie są
przetwarzane aż do czasu wykonania programu. Aż do tego momentu można ich używać do
reprezentacji obiektów dowolnych typów. To właśnie z tego względu można użyć wyrażenia
obj.Text
pomimo faktu, iż klasa
Object
nie dysponuje właściwością o tej nazwie. ASP.NET
zezwoli na wykorzystania wyrażenia
obj.Text
gdyż wie, że w czasie obsługi żądania zmienna
obj
może się stać obiektem klasy
Button
(gdyby się jednak nie stała, to moglibyśmy mieć
poważne problemy).
Gdyby mechanizm późnego łączenia nie był wykorzystywany, to klasa
Object
byłaby od razu
przetwarzana a kompilator zgłosiłby błąd zaraz po określeniu że została podjęta próba uzyskania
dostępu do właściwości której nie ma (klasa
Object
nie posiada bowiem żadnej właściwości o
nazwie
Text
). Właśnie z tego powodu należy sprawić, aby kompilator VB.NET sądził że zmienna
obj
jest przyciskiem jeszcze zanim zostanie przesłane żądanie, gdyż dzięki temu będzie można
wykorzystać w kodzie właściwość
Text
. Metoda
CType
rzutuje jeden typ danych na inny, a
wyrażenie użyte w wierszu 16. rzutuje zmienną typu
Object
na zmienną typu
Button
. Może się
wydawać, że jest to dosyć złożone rozwiązanie, jednak gdy stworzysz więcej stron ASP.NET i
zdobędziesz więcej informacji na temat łączenia, wszystko stanie się znacznie prostsze.
Metoda
Operate
zdefiniowana w wierszach od 20. do 31. analizuje operator przekazany przez
metodę
btOperator_Clicked
przy wykorzystaniu instrukcji wyboru
select case
. W
zależności od przekazanego operatora wykonywane jest odpowiednie działanie. Jego wynik jest
następnie zwracany do metody
btOperator_Clikced
i wyświetlany na stronie WWW.
Na listingu 19.8 przedstawiony został kod wyjątkowo krótkiej strony ASP.NET która
wykorzystuje stworzony przed chwilą element sterujący użytkownika.
Listing 19.8.
Strona ASP.NET wykorzystująca element sterujący użytkownika oraz kod
obsługi formularza.
1
<%@ Page language="VB" %>
2
<%@ Register TagPrefix="ASPNETDK" TagName="Calculator"
src="Calculator.ascx" %>
3
4
<html><body>
5
<form runat="server">
6
<ASPNETDK:Calculator id="Calc1" runat="server"/>
7
</form>
8
</body></html>
Analiza
Powyższa strona jest wyjątkowo prosta. Zapewne pamiętasz, że element sterujący użytkownika
musi zostać zarejestrowany na stronie ASP.NET. Służy do tego dyrektywa
@ Register
, opisana
w szczegółowo w rozdziale 6., pt.: „Ciąg dalszy wiadomości na temat tworzenia formularzy
internetowych”. Po zarejestrowaniu, elementu sterującego użytkownika będzie można używać tak
samo, jak wszelkich innych elementów sterujących (patrz wiersz 5.). Wyświetl tę stronę w
przeglądarce, a następnie spróbuj wpisać jakieś liczby w polach formularza i wykonać jakieś
działania. Wyniki powinny przypominać te, przedstawione na rysunku 19.7.
Rysunek 19.7.
Kod obsługi formularza obsługuje zdarzenia generowane przez element sterujący
użytkownika umieszczony na stronie ASP.NET
Pliki zasobów i lokalizacja
Udając się na wakacje zabieramy walizkę z ubraniami, przyborami toaletowymi oraz innymi
rzeczami osobistymi. Można by także zabrać te wszystkie rzeczy bezpośrednio ze sobą (albo i na
sobie) ale było by to bardzo dziwne i niewygodne (nie wspominając w ogóle o ograniczonej ilości
kieszeni!). Ta walizka jest na wakacjach przedmiotem o pierwszorzędny znaczeniu — bez niej
nasza wycieczka stała by się koszmarem.
ASP.NET pozwala nam na „pakowanie walizki” dla tworzonej aplikacji. Wszelkie informacje
(takie jak zmienne, komunikaty, notatki, rysunki, itp.) można zapisać w pliku zasobu. Strony
ASP.NET bez trudu mogą następnie pobierać zasoby zgromadzone w takim pliku, gdy tylko będą
potrzebne.
Nowe wyrażenie
Pliki zasobów są zazwyczaj wykorzystywane do lokalizowania aplikacji.
Lokalizowanie
jest
procesem polegającym na modyfikacji wyników generowanych przez aplikację tak aby były one
zgodne z różnymi kulturami i językami. Można tworzyć pliki zasobów dla wszystkich kultur jakie
będą korzystać z danej aplikacji. Na przykład, w pliku zasobów można zapisać wiadomość
powitalną. Jeśli aplikację odwiedzi osoba anglojęzyczna komunikat powitalny będzie miał postać
„Hello”, dla użytkowników z Francji lub innych krajów francuskojęzycznych komunikat ten
będzie miał postać „Bonjour”. Zapisując wiadomość powitalną dla każdego z języków w
odrębnym pliku zasobów nie trzeba będzie tworzyć różnych stron ASP.NET dla różnych języków.
Znacznie łatwiej jest stworzyć nowy plik zasobów.
W ASP.NET lokalizacja aplikacji oraz grupowanie zasobów jest bardzo łatwym procesem.
Pozwala on na modyfikację wyników działania aplikacji bez konieczności wprowadzania
jakichkolwiek zmian w kodzie, czyli stanowi jeszcze jedną formę oddzielania kodu od treści. W
kilku kolejnych częściach niniejszego rozdziału przedstawione zostaną sposoby określania jacy
użytkownicy korzystają z aplikacji, metody modyfikacji aplikacji pod ich kątem oraz zapisywania
zasobów w plikach.
Lokalizowanie aplikacji
W idealnym przypadku lokalizowania aplikacji będziemy dążyć do automatycznego określenia
pochodzenia korzystających z niej użytkowników, tak aby można było wykorzystać odpowiednie
języki oraz inne informacje kulturowe. Niestety ze względu na charakter WWW nie ma żadnego
standardowego sposobu na określenie geograficznego położenia osoby odwiedzającej witrynę.
Na szczęście jednak istniej kilka sposobów rozwiązania tego problemu. Można określić główny
język ustawiony w przeglądarce użytkownika. Zazwyczaj, choć oczywiście nie zawsze, ustawienie
to określa język oraz informacje kulturowe, które najbardziej odpowiadają użytkownikowi
przeglądarki; na tej podstawie będzie można określić działanie aplikacji. Odstęp do tych
informacji zapewnia obiekt
Request.UserLanguages
, który gromadzi informacje o językach
przekazane przez przeglądarkę i przechowuje je w formie listy posortowanej zgodnie z
priorytetami określonymi przez użytkownika. Przykład kodu umożliwiającego określenie
głównego języka przedstawiony została na listingu 19.9.
Listing 19.9.
Wykorzystanie obiektu
Request
do określenia językowych preferencji
użytkownika.
1
<%@Page Language="VB" %>
2
<%@Import Namespace="System.Globalization" %>
3
4
<script runat="server">
5
sub Page_Load(obj as object,e as eventargs)
6
lblMessage.Text =Request.UserLanguages(0).ToString
7
end sub
8
</script>
9
10
<html><body>
11
Twoim głównym j
ę
zykiem jest:
12
<asp:Label id="lblMessage" runat="server"/>
13
</body></html>
Analiza
W wierszu 2. importowana jest zupełnie nowa przestrzeń nazw —
System.Globalization
.
Zawiera ona wiele klas, które będą konieczne przy modyfikacji wyników działania aplikacji w
zależności od ustawień językowych i kulturowych. Warto zwrócić uwagę, iż w powyższym
przykładzie nie jest używany żaden obiekt którejkolwiek z klas dostępnych w tej przestrzeni nazw.
Już nie długo będziemy jednak z nich korzystać! W wierszu 6. jest pobierany i wyświetlany
główny język użytkownika. Właściwość
UserLanguages
zwraca tablicę języków wybranych
przez użytkownika (określonych w przeglądarce), posortowaną według priorytetów tych języków
(najczęściej używane języki oraz ich kody zostały przedstawione w tabeli 19.1). Język główny (lub
język o najwyższym priorytecie) można określić przy użyciu wyrażenia
UserLanguages(0)
.
Wyniki wykonania strony z listingu 19.9 zostały przedstawione na rysunku 19.8.
Tabela 19.1.
Najczęściej wykorzystywane języki i ich kody.
Język
Kod
Angielski (Anglia)
en-uk
Angielski (Australia)
en-au
Angielski (Kanada)
en-ca
Angielski (Stany Zjednoczone)
en-us
Arabski (Egipt)
ar-eg
Chiński (Hong-Kong)
zh-hk
Flamandzki (Belgia)
nl-be
Francuski (Francja)
fr
Hiszpańki (tradycyjny)
es
Hiszpański (Meksyk)
es-mx
Japoński
ja
Koreański
ko
Niemiecki (Niemcy)
de
Polski
pl
Portugalski (Portugalia)
pt
Rosyjski (Rosja)
ru
Włoski (Włochy)
it
Rysunek 19.8.
Obiekt
Request
może posłużyć od określenia głównego języka wybranego w
przeglądarce
Teraz, po określeniu głównego języka można na jego podstawie określić informacje kulturowe,
które będą wykorzystywane w aplikacji ASP.NET. W tym celu posłużymy się obiektem klasy
System.Globalization.CultureInfo
. Obiekty tej klasy zawierają kompletne informacje
reprezentujące kulturę określaną przez używany język. Do informacji tych, oprócz języka, należy
także używany kalendarz, nazwy krajów, format zapisu liczb i dat, oraz wiele innych elementów.
Przykład wykorzystania obiektu tej klasy został przedstawiony na listingu 19.10.
Listing 19.10.
Wykorzystanie obiektu klasy
CultureInfo
.
1
<%@ Page Language="VB" %>
2
<%@ Import Namespace="System.Globalization" %>
3
4
<script runat="server">
5
sub Page_Load(obj as object, e as eventargs)
6
dim strLanguage as string = Request.UserLanguages(0).ToString
7
8
lblMessage.Text = "J
ę
zyk główny: " & _
9
strLanguage & "<br>"
10
11
dim objCulture as new CultureInfo(strLanguage)
12
lblMessage.Text += "Pełna nazwa: " & _
13
objCulture.EnglishName & "<br>"
14
lblMessage.Text += "Nazwa rodzima: " & _
15
objCulture.NativeName & "<br>"
16
lblMessage.Text += "Skrót: " & _
17
objCulture.ThreeLetterISOLanguageName & "<br>"
18
lblMessage.Text += "Godzina: " & _
19
DateTime.Now.ToString("D", objCulture) & "<br>"
20
21
end sub
22
</script>
23
24
<html><body>
25
<b>Informacje kulturowe o u
ż
ytkowniku:</b> <p>
26
<asp:Label id="lblMessage" runat="server"/>
27
</body></html>
Analiza
Powyższy kod jest modyfikacją przykładu z listingu 19.9. W wierszu 2. importowana jest
przestrzeń nazw
System.Globalization
. W wierszach 6. i 7. pobierany jest główny język
przeglądarki użytkownika (w identyczny sposób jak na listingu 19.9). Język ten jest następnie
wyświetlany w etykiecie zdefiniowanej w wierszu 26. W wierszu 12., tworzony jest nowy obiekt
klasy
CultureInfo
bazujący na pobranym wcześniej języku głównym
1
. W wierszach od 13. do
20. wyświetlane są różne informacje kulturowe. W celu poprawnego sformatowania daty została
wykorzystana metoda
ToString
akceptująca dwa argumenty — łańcuch znaków określający
sposób wyświetlenia daty oraz obiekt
CultureInfo
zawierający dodatkowe informacje
określające postać daty. Na rysunku 19.9 zostały przedstawione wyniki wykonania powyższej
strony ASP.NET w przypadku gdy głównym językiem wybranym w przeglądarce jest francuski.
Rysunek 19.9.
Wyniki wygenerowane przez stronę zostały dostosowane do ustawień
kulturowych użytkownika i to bez wprowadzania jakichkolwiek modyfikacji w kodzie
Klasa
CultureInfo
udostępnia więcej właściwości, które pozwalają na uzyskanie bardziej
szczegółowych informacji na temat ustawień kulturowych. Najczęściej stosowane właściwości tej
klasy zostały przedstawione w tabeli 19.2.
Tabela 19.2.
Właściwości klasy
CultureInfo
.
Właściwość
Opis
Calendar
Kalendarz używany w danej kulturze (na przykład:
gregoriański, koreański, itp.).
CurrentCulture
Właściwość określa informacje kulturowe ustawione na
serwerze (a nie w przeglądarce użytkownika).
Właściwość ta zwraca obiekt klasy
CultureInfo
.
1
Jest całkiem prawdopodobne, że przy domyślnych ustawieniach przeglądarki, na przykład polskiej wersji
Internet Explorera, powyższy przykład nie będzie działać poprawnie. W przeglądarce może zostać
wyświetlone komunikat o błędzie rozpoczynający się od słów: „Culture "pl" is a neutral culture....” Należy
go rozumieć w ten sposób, iż informacje kulturowe określone przez przeglądarkę nie są wystarczające by na
ich podstawie można było określić sposób formatowania informacji. Rozwiązanie tego problemu sprowadza
się do określenia (w przeglądarce) bardziej szczegółowych informacji na temat wybranego języka głównego,
na przykład zamiast języka
Polski [pl]
, należy podać język
pl-pl
. Po wybraniu języka głównego o takiej
postaci powyższy przykład będzie działać poprawnie.
Właściwość jest przeznaczona wyłącznie do odczytu.
CurrentUICulture
Właściwość pobiera informacje kulturowe używane na
serwerze. Jest ona używana do określania ustawień
kulturowych plików zasobów. (Patrz podrozdział
„Zapisywanie zasobów w plikach”, w dalszej części tego
rozdziału.) Właściwość taj jest przeznaczona wyłącznie
do odczytu.
DateTimeFormat
Zwraca obiekt klasy
DateTimeFormatInfo
określający
sposób formatowania daty i czasu zgodnie z bieżącymi
ustawieniami kulturowymi. Więcej informacji na ten
temat można znaleźć w dokumentacji .NET SDK.
DisplayName
Pełna nazwa kultury reprezentowanej przez dany obiekt
CultureInfo
, w języku określonym przez interfejs
użytkownika.
EnglishName
Pełna nazwa obiektu klasy
CultureInfo
w języku
angielskim.
Name
To samo co właściwość
DisplayName
, z tym iż ta jest
przeznaczona wyłącznie do odczytu.
NativeName
Pełna nazwa obiektu klasy
CultureInfo
w danym
języku.
NumberFormat
Właściwość określa format wyświetlania liczb (czyli
gdzie umieszczać przecinki, punkty dziesiętne, itp.)
Zwraca obiekt klasy
NumberFormatInfo
.
ThreeLetterISOLanguageName
Trzyliterowy kod oznaczający daną kulturę w standardzie
ISO 3166.
ThreeLetterWindowLanguageName
Wersja trzyliterowego oznaczenia kultury stosowana w
systemie Windows.
TwoLetterISOLanguageName
Dwuliterowy kod oznaczający daną kulturę w standardzie
ISO 3166.
Informacje kulturowe dla danej strony ASP.NET można także określić przy użyciu atrybutu
Culture
dyrektywy
@ Page
. Poniższa dyrektywa sprawi, że na stronie zostaną wykorzystane
ustawienia kulturowe dla języka niemieckiego:
<%@ Page Language="VB" Culture="de" %>
W tym przypadku, jeśli na stronie będą wykorzystywane obiekty związane z ustawieniami
kulturowymi, to wykorzystają one ustawienia kulturowe podane w dyrektywie
@ Page
a nie
określone przez serwer. Na przykład, w przypadku wykorzystania ustawień kulturowych dla
języka niemieckiego, wywołanie
DateTime.Now.ToString("D")
spowoduje wyświetlenie
następującej daty:
Montag, 3. Dezember 2001
W przypadku użycia ustawień kulturowych dla języka oznaczonego symbolem
en-us
, ten sam
fragment kodu wygeneruje wyniki:
Monday, December 03, 2001
To bardzo prosty sposób określania ustawień kulturowych jakie mają być wykorzystane na stronie,
o ile są one z góry znane.
Notatka
Wła
ś
ciwo
ść
CultureInfo.CurrentCulture
jest w rzeczywisto
ś
ci „skrótem” do wła
ś
ciwo
ś
ci
System.Threading.CurrentThread.CurrentCulture
. Obie te wła
ś
ciwo
ś
ci zwracaj
ą
obiekt klasy
CultureInfo
, który mo
ż
e zosta
ć
wykorzystany w sposób przedstawiony na
powy
ż
szych przykładach. Ró
ż
nica pomi
ę
dzy nimi polega na tym, i
ż
wła
ś
ciwo
ść
CultureInfo.CurrentCulture
jest przeznaczona wył
ą
cznie do odczytu, natomiast
wła
ś
ciwo
ść
System.Threading.CurrentThread.CurrentCulture
pozwala tak
ż
e na
zapis informacji. Z tego powodu, je
ś
li b
ę
dziesz musiał zmieni
ć
bie
żą
ce ustawienia kulturowe
powiniene
ś
posłu
ż
y
ć
si
ę
t
ą
drug
ą
wła
ś
ciwo
ś
ci
ą
.
Kultura jest wła
ś
ciwo
ś
ci
ą
aktualnie wykonywanego w
ą
tku (innymi słowy — aktualnie
wykonywanego fragmentu bie
żą
cej aplikacji). W przypadku okre
ś
lania ustawie
ń
kulturowych w
ASP.NET s
ą
one okre
ś
lane dla całej aplikacji. Oznacza to,
ż
e te same informacje kulturowe
mo
ż
na pobra
ć
za po
ś
rednictwem bie
żą
cego w
ą
tku jak i z obiektu
CultureInfo
. (W dalszej
cz
ęś
ci
rozdziału
zostanie
przedstawiony
przykład
wykorzystania
obiektu
System.Threading.CurrentThread
.
Oprócz obiektu
CultureInfo
można także wykorzystać obiekt klasy
RegionInfo
zawierający,
między innymi, informacje o symbolu waluty oraz o tym, czy w danym regionie jest
wykorzystywany system metryczny. Sposób użycia obiektów obu tych klas jest bardzo podobny, z
tym, że w przypadku obiektów klasy
RegionInfo
nie można podawać języka w celu określenia
postaci pobieranych informacji — zamiast nich wykorzystywane są skróty nazw krajów. Na
przykład:
US
dla Stanów Zjednoczonych bądź
FR
dla Francji. Przykład wykorzystania obiektów tej
klasy został przedstawiony na listingu 19.11.
Listing 19.11.
Wyświetlanie informacji o regionie z jakiego pochodzi użytkownik.
1
<%@ Page Language="VB" %>
2
<%@ Import Namespace="System.Globalization" %>
3
4
<script runat="server">
5
sub Page_Load(obj as object, e as eventargs)
6
dim objRegion as RegionInfo
7
8
if Page.IsPostBack then
9
objRegion = new RegionInfo(btRegion.Text)
10
else
11
objRegion = RegionInfo.CurrentRegion
12
end if
13
14
lblMessage.Text = "Region: " & objRegion.Name & "<br>"
15
16
lblMessage.Text += "Pełna nazwa: " & _
17
objRegion.EnglishName & "<br>"
18
lblMessage.Text += "Waluta: " & _
19
objRegion.CurrencySymbol & "<br>"
20
lblMessage.Text += "Waluta ISO: " & _
21
objRegion.ISOCurrencySymbol & "<br>"
22
lblMessage.Text += "Skrót: " & _
23
objRegion.ThreeLetterISORegionName & "<br>"
24
lblMessage.Text += "Czy jest u
ż
ywany system metryczny: " _
25
& objRegion.IsMetric
26
end sub
27
</script>
28
29
<html><body>
30
<form runat="server">
31
<b>Twoje ustawienia:</b> <p>
32
<asp:Label id="lblMessage" runat="server"/><p>
33
Zmie
ń
na (i.e. 'US', 'FR', 'JP', etc):
34
<asp:TextBox id="btRegion" runat="server"
35
AutoPostBack=true />
36
</form>
37
</body></html>
Analiza
W momencie pierwszego wyświetlenia powyższej strony, do obiektu
RegionInfo
zostaną
wczytane domyślne informacje o regionie (patrz wiersz 11.). Informacje o aktualnie wybranym
regionie są wyświetlane w wierszach od 14. do 25., między innymi należą do nich symbol i skrót
waluty. Gdy użytkownik wpisze jakieś informacje w polu tekstowym zdefiniowanym w wierszach
34. i 35. strona zostanie automatycznie wyświetlona ponownie (dzięki użyciu atrybutu
AutoPostBack
o wartości
true
). Kod regionu podany przez użytkownika w polu tekstowym jest
następnie wykorzystywany w celu stworzenia nowego obiektu
RegionInfo
(patrz wiersz 9.).
Wygląd strony po wybraniu regionu o kodzie
JP
(czyli Japonii) został przedstawiony na rysunku
19.10.
Rysunek 19.10. Na stronie zostają wyświetlone informacje o wybranym regionie
Najczęściej wykorzystywane właściwości klasy
RegionInfo
zostały przedstawione w tabeli 19.3.
Tabela 19.3.
Właściwości klasy
RegionInfo
.
Właściwość
Opis
CurrencySymbol
Symbol używany do oznaczania wartości monetarnych w
danym regionie.
CurrentRegion
Właściwość pobiera domyślny serwer używany na serwerze
(a nie na komputerze użytkownika). Właściwość zwraca
obiekt klasy
RegionInfo
.
DisplayName
Pełna nazwa regionu identyfikowanego przez obiekt
RegionInfo
w języku określonym przez interfejs
użytkownika.
EnglishName
Nazwa regionu identyfikowanego przez obiekt
RegionInfo
w języku angielskim.
IsMetric
Wartość logiczna określająca czy w danym regionie
wykorzystywany jest system metryczny.
ISOCurrencySymbol
Kod ISO znaku zapisanego we właściwości
CurrencySymbol
. Na przykład, znakowi
$
odpowiada kod
ISO
USD
.
Name
Zawiera te same informacje co właściwość
CurrencySymbol
, lecz jest przeznaczona wyłącznie do
odczytu.
ThreeLetterISORegionName
Trzyliterowy kod oznaczający daną kulturę w standardzie
ISO 3166.
ThreeLetterWindowRegionName
Wersja trzyliterowego oznaczenia kultury stosowana w
systemie Windows.
TwoLetterISORegionName
Dwuliterowy kod oznaczający daną kulturę w standardzie
ISO 3166.
Nowe wyrażenie
Istnieje także możliwość określenia sposobu
kodowania
wyników generowanych przez strony
ASP.NET. Kodowanie, to sposób w jaki znaki są reprezentowane przez komputer; przykładowe
sposoby kodowania to Unicode lub ASCII. Strony ASP.NET domyślnie używają kodowania
Unicode, jednak ustawienie to może się zmieniać w zależności od regionu.
Dostępne są dwa, różne sposoby określania sposobu kodowania wykorzystywanego w aplikacji —
bezpośrednio na stronach ASP.NET bądź też w pliku
web.config
. Na stronach ASP.NET sposób
kodowania określa się przy użyciu atrybutu
ResponseEncoding
dyrektywy
@ Page
; jak na
poniższym przykładzie:
<%@ Page Language="VB" ResponseEncoding="UTF-8" %>
W przypadku określania sposobu kodowania w pliku
web.config
należy to zrobić w następujący
sposób:
<configuration>
<system.web>
<globalization fileEncoding="utf-8" />
</system.web>
</configuration>
Jak na razie przedstawione zostały wyłącznie sposoby zmieniania informacji generowanych przez
obiekty związane z ustawieniami kulturowymi i regionalnymi, a co ze zwykłym tekstem? Jak
można skonwertować zwyczajny tekst, tak aby korzystał z wybranych ustawień językowych?
Niestety nie można tego zrobić w żaden prosty sposób — trzeba po prostu, samodzielnie
przetłumaczyć tekst. Jeśli witryna jest odwiedzana przez osoby posługujące się różnymi językami,
to zazwyczaj będzie to oznaczać konieczność stworzenia odrębnych wersji językowych każdej ze
stron. Na szczęście w ASP.NET nie trzeba się uciekać do aż tak drastycznych rozwiązań — można
bowiem wykorzystać pliki zasobów.
Zapisywanie zasobów w plikach
Pliki zasobów są używane do przechowywania informacji wykorzystywanych w aplikacjach
niezależnie od ich kodu. Istnieje możliwość zastosowania wielu wersji pliku zasobów, dzięki
czemu strony ASP.NET mogą prezentować różne informacje bez konieczności wprowadzania
jakichkolwiek zmian w kodzie. Posługując się przykładem lokalizacji, można by stworzyć wiele
różnych plików zasobów, po jednym dla każdego z ustawień kulturowych wykorzystywanych
przez użytkowników odwiedzających witrynę. Każdy z tych plików zawierałby dokładnie te same
informacje zapisane w różnych językach.
Przeanalizujmy przykład prostej strony ASP.NET, której treść można umieścić w pliku zasobów.
Kod tej strony został przedstawiony na listingu 19.12.
Listing 19.12.
Strona ASP.NET, która posłuży jako przykład wykorzystania plików zasobów.
1
<%@ Page Language="VB" %>
2
3
<script runat="server">
4
sub Page_Load(obj as Object, e as EventArgs)
5
lblMessage.Text = DateTime.Now.ToString("t")
6
end sub
7
</script>
8
9
<html><body>
10
<b>Witamy!</b> Teraz jest:
11
<asp:Label id="lblMessage" runat="server"/><p>
12
13
Ta strona demonstruje sposób wykorzystywania plików zasobów
14
w ASP.NET.<p>
15
16
<font size=1>Nie zapomnij sprawdzi
ć
tego w domu!</font>
17
</body></html>
Powyższy przykład jest bardzo prosty — wyświetla kilka komunikatów tekstowych o stałej treści
oraz bieżącą godzinę. Wyniki wykonania tej strony zostały przedstawione na rysunku 19.11.
Rysunek 19.11. Prosta strona ASP.NET w której można wykorzystać pliki zasobów
Załóżmy, że chcielibyśmy przetłumaczyć tę stronę na język francuski. Oczywiście można by
stworzyć nową wersję strony o tej samej postaci lecz z innymi komunikatami tekstowymi, ale po
co utrudniać sobie życie? Znacznie lepszym rozwiązaniem będzie stworzenie dwóch plików
zasobów. Są to zwyczajne pliki tekstowe, które można utworzyć przy użyciu dowolnego edytora
tekstów. W pierwszej kolejności stwórzmy polską wersję takiego pliku. W tym celu, w edytorze
tekstowym należy wpisać poniższy tekst:
[strings]
Powitanie=Witamy!
Czas=Teraz jest:
Tresc=Ta strona demonstruje sposób wykorzystywania plików zasobów w ASP.NET
Stopka=<font size=1>Nie zapomnij sprawdzi
ć
tego w domu!</font>
Zapisz ten fragment tekstu w pliku o nazwie
data.pl.txt
(za chwilę wyjaśnię dlaczego nazwa pliku
ma akurat taką postać). Utworzony plik zawiera jedną sekcję, w której zostały zapisane wszystkie
wykorzystywane łańcuchy znaków. Informacje zostały zapisane w formie par nazwa-wartość:
Powitamie=Witamy!
Powitanie
jest nazwą zasobu do którego będziemy się odwoływać w kodzie strony ASP.NET,
natomiast
Witamy!
jest wartością jaka zostanie wyświetlona na stronie wynikowej. Poniżej
przedstawiona została francuskojęzyczna wersja pliku zasobów:
[strings]
Powitanie=Bonjour!
Czas=L'heure maintenent est:
Tresc=Cette page demonstrate utiliser files de resource avec ASP.NET
Stopka=<font size=1>N'oublie pas essayez de faire ceci chez soi!</font>
Powyższy tekst zapisz w pliku o nazwie
data.fr.txt
. (Tworząc oba pliki tekstowe należy pamiętać,
aby każdy z łańcuchów znaków został zapisany w osobnym wierszu. Jeśli w łańcuchu znaków
znajdą się znaki nowego wiersza, to podczas generacji właściwych plików zasobów pojawią się
błędy.) Warto zwrócić uwagę, iż w obu plikach zostały użyte te same nazwy kluczy. Nazwy te
muszą być identyczne, gdyż w przeciwnym przypadku ASP.NET nie będzie w stanie odszukać
zasobów.
ASP.NET nie jest jednak w stanie korzystać z plików zasobów w ich obecnej postaci. Trzeba je
zatem skonwertować do postaci, którą ASP.NET będzie rozumieć. Do tego celu służy program
narzędziowy
resgen.exe
(nazywany także generatorem zasobów). Program ten konwertuje
tekstowe pliki zasobów i na ich podstawie generuje pliki z rozszerzeniem
.resources
. A zatem,
wyświetl okno wiersza poleceń i przejdź do folderu, w którym zapisałeś stworzone wcześniej pliki
.txt
. Następnie wydaj poniższe polecenie (i nie zapomnij nacisnąć klawisza
Enter
):
resgen data.pl.txt
resgen data.fr.txt
Program powinien wyświetlić następujące wyniki:
Read in 4 resources from 'data.pl.txt'
Writing resource file... Done.
Po wykonaniu powyższych poleceń w folderze pojawią się dwa nowe pliki —
data.pl.resources
oraz
data.fr.resources
. Z tych plików może już korzystać zarządca zasobów ASP.NET. A zatem,
użyjmy tych zasobów na stronie ASP.NET!
Za obsługę wszystkich zasobów w ASP.NET odpowiada obiekt klasy
System.Resources.ResourceManager
. Obiekt taki jest w stanie odnaleźć plik zasobów
odpowiadający ustawieniom kulturowym wykorzystywanym przez użytkownika i pobrać zapisane
w nim zasoby. Sposób użycia tego obiektu został przedstawiony na listingu 19.13.
Listing 19.13.
Pobieranie informacji z plików zasobów przy wykorzystaniu obiektu
ResourceManager
.
1
<%@ Page Language="VB" %>
2
<%@ Import Namespace="System.Globalization" %>
3
<%@ Import namespace="System.Resources" %>
4
<%@ Import namespace="System.Threading" %>
5
6
<script runat="server">
7
sub Page_Load(obj as object, e as eventargs)
8
dim objRM as ResourceManager
9
dim strLanguage as string = _
10
Request.UserLanguages(0).ToString
11
dim objCulture as new CultureInfo(strLanguage)
12
Thread.CurrentThread.CurrentCulture = _
13
new CultureInfo(strLanguage)
14
Thread.CurrentThread.CurrentUICulture = _
15
new CultureInfo(strLanguage)
16
17
objRM = ResourceManager. _
18
CreateFileBasedResourceManager("data", _
19
Server.MapPath("."), Nothing)
20
21
lblGreeting.Text = objRM.GetString("Powitanie")
22
lblTime.Text = objRM.GetString("Czas") & " " & _
23
DateTime.Now.ToString("t")
24
lblBlurb.Text = objRM.GetString("Tresc")
25
lblDisclaimer.Text = objRM.GetString("Stopka")
26
27
objRM.ReleaseAllResources
28
end sub
29
</script>
30
31
<html><body>
32
<b><asp:Label id="lblGreeting" runat="server"/></b>
33
<asp:Label id="lblTime" runat="server"/><p>
34
35
<asp:Label id="lblBlurb" runat="server"/><p>
36
37
<asp:Label id="lblDisclaimer" runat="server"/>
38
</body></html>
Analiza
W pierwszej kolejności należy zwrócić uwagę na wykorzystanie dwóch dodatkowych przestrzeni
nazw, importowanych w wierszach 3. i 4. Przestrzeń nazw
System.Resources
jest konieczna
aby można było korzystać z obiektu
ResourceManager
. Także przestrzeń nazw
System.Threading
jest niezbędna (w dalszej części rozdziału wyjaśnię dlaczego). W wierszu 8.,
w standardowy sposób, deklarowany jest nowy obiekt
ResourceManager
. W wierszach 9. i 10.
określany jest główny język wybrany w przeglądarce (główny język jest określany w taki sam
sposób jak w przykładzie przedstawionym na listingu 19.9.).
W wierszach do 12. do 15., na podstawie wybranego języka głównego, określane są ustawienia
kulturowe. Pamiętasz zapewne, że informacje kulturowe można podawać przy wykorzystaniu
właściwości
System.Threading.CurrentThread.CurrentCulture
, która pozwala na
pobranie lub zapis obiektu klasy
CultureInfo
(była o tym mowa we wcześniejszej części
rozdziału, w podrozdziale pt.: „Lokalizowanie aplikacji”). W wierszu 12. tworzony jest nowy
obiekt klasy
CultureInfo
zawierający informacje kulturowe wybrane na podstawie określonego
wcześniej głównego języka używanego w przeglądarce. Wszystkie obiekty, które dysponują
możliwością lokalizacji, będą teraz wykorzystywały wybrane informacje kulturowe (na przykład,
po wybraniu informacji kulturowych dla języka angielskiego, godzina będzie wyświetlana w
formie 10:16 PM, a nie 22:16).
Niemniej jednak sam fakt zmiany ustawień kulturowych nie sprawi, że ASP.NET pobierze dane z
odpowiednich plików zasobów. W tym celu, oprócz wybrania odpowiednich informacji
kulturowych, należy także przypisać odpowiednią wartość właściwości
System.Threading.CurrentThread.CurrentUICulture
(patrz wiersze 14. i 15.). To
właśnie na podstawie tej właściwości ASP.NET określa jaki plik zasobów należy użyć.
Odczytanie zasobów z pliku realizowane jest przez metodę
CreateFileBasedResourceManager
, której wywołanie zostało zapisane w wierszach od 17. do
19. Metoda ta wymaga podania trzech argumentów — początkowej części (prefiksu) nazwy
plików zasobów, ścieżki dostępu do tych plików oraz obiektu którego należy użyć do
przetworzenia informacji pobranych z pliku zasobów (przy czym ten trzeci argument jest
opcjonalny). Wartość drugiego argumentu określana jest przy użyciu metody
Server.MapPath
,
która zwraca fizyczną ścieżkę dostępu do folderu w jakim są przechowywane pliki zasobów.
(Więcej informacji na temat metody
MapPath
można znaleźć w rozdziale 4., pt.: „Stosowanie
obiektów ASP.NET w językach C# i VB.NET”, w podrozdziale „Obiekt HttpServerUtility”.) W
naszym przypadku nie jest potrzebny żaden obiekt służący do przetwarzania danych pobieranych z
pliku zasobów, a zatem jako trzeci argument wywołania metody
CreateFileBasedResourceManager
należy przekazać wartość
Nothing
.
Przypomnij sobie, że nazwy plików zasobów mają następującą postać —
data.kultura.resources
.
Metoda
CreateFileBasedResourceManager
poszukuje plików o nazwach pasujących do
schematu
prefiks.kultura.resources
. A zatem, prefiks podany w wywołaniu tej metody musi
odpowiadać początkowej części nazw plików zasobów. W naszym przypadku, w razie wybrania
ustawień kulturowych dla języka
pl
(polskiego) metoda będzie poszukiwać pliku o nazwie
data.pl.resources
, natomiast w razie wybrania ustawień kulturowych dla języka
fr
(francuskiego)
będzie ona poszukiwać pliku
data.fr.resources
. Prefiks służy do logicznego grupowania plików
zasobów.
Następnie, w wierszach do 21. do 25. wywoływana jest metoda
GetString
obiektu
ResourceManager
, która pobiera pary klucz-wartość zapisane w pliku zasobów. Pobrane
łańcuchy znaków są wyświetlane na stronie wynikowej przy użyciu czterech etykiet (elementów
sterujących
Label
), zdefiniowanych w wierszach do 32. do 35. Otwierając plik zasobów
zarządzający nimi mechanizm blokuje dostęp do nich, dzięki czemu żadne inne aplikacje nie będą
w stanie ich zmienić. Na rysunkach 19.12 oraz 19.13 zostały przedstawione wyniki wykonania
strony z listingu 19.13, w przypadku wyboru ustawień kulturowych dla języków
pl-PL
oraz
fr-
FR
.
Rysunek 19.12. W przypadku wybrania ustawień kulturowych dla języka
pl-PL
ASP.NET
pobiera dane z pliku zasobów
data.pl.resources
Rysunek 19.13. W przypadku wybrania ustawień kulturowych dla języka
fr-FR
ASP.NET
pobiera dane z pliku zasobów
data.fr.resources
Notatka
Warto pami
ę
ta
ć
,
ż
e informacje kulturowe mo
ż
na tak
ż
e okre
ś
li
ć
przy u
ż
yciu atrybut
Culture
dyrektywy
@ Page
:
<%@ Page Language="VB" Culture="fr-FR" %>
Je
ś
li jednak zastosujesz takie rozwi
ą
zanie w ostatnim przykładzie, to zasoby nie zostan
ą
pobrane z odpowiedniego pliku. Problem polega na tym, i
ż
informacje kulturowe s
ą
okre
ś
lana
na podstawie głównego j
ę
zyka u
ż
ywanego w przegl
ą
darce (okre
ś
lanego w wierszu 9.), a nie na
podstawie atrybutu dyrektywy
@ Page
. Aby rozwi
ą
za
ć
ten problem, wiersze do 9. do 11. listingu
19.13 nale
ż
y zast
ą
pi
ć
nast
ę
puj
ą
cym fragmentem kodu:
dim strLanguage as string = CultureInfo.CurrentCulture.ToString
Podpowied
ź
Zazwyczaj w całej aplikacji powinny by
ć
wykorzystywane te same informacje kulturowe, dlatego
mo
ż
esz zastanowi
ć
si
ę
nad wykorzystaniem rozwi
ą
zania polegaj
ą
cego na podaniu tych
informacji w metodzie
Application_BeginRequest
definiowanej w pliku
global.asax
. W ten
sposób informacje kulturowe b
ę
d
ą
poprawnie ustawiane przed rozpocz
ę
ciem obsługi ka
ż
dego
żą
dania odbieranego na serwerze.
Innym rozwi
ą
zaniem mogłoby by
ć
stworzenie obiektu
ResourceManager
i zapisanie go jako
zmiennej aplikacyjnej w metodzie
Application_OnStart
. W ten sposób mo
ż
na unikn
ąć
konieczno
ś
ci
tworzenia
obiektów
ResourceManager
podczas
obsługi
ka
ż
dego
otrzymywanego
żą
dania. Na przykład, do metody
Application_OnStart
mo
ż
na by doda
ć
poni
ż
szy fragment kodu:
Application("RM") = New ResourceManager("data", _
Server.MapPath("."), Nothing)
Powy
ż
szy fragment kodu tworzy obiekt
ResourceManager
dost
ę
pny dla całej aplikacji. Dost
ę
p
do niego mo
ż
na uzyska
ć
za pomoc
ą
wyra
ż
enia
Application("RM")
.
To nie jest ASP!
Dążenie od oddzielenia kodu od treści stron WWW i czerpania wynikających z tego korzyści nie
jest niczym nowym w technologii ASP.NET. W rzeczywistości programiści ASP dążyli do tego
celu już od dłuższego czasu, zwłaszcza w klasycznej technologii ASP bazującej na wykorzystaniu
bloków kodu wykonywalnego a nie bloków deklarowania kodu. Brak możliwości separacji kodu
od treści sprawiał, iż kod ASP musiał się przeplatać ze zwyczajnym kodem HTML. To z kolei
niezwykle utrudniało testowanie i modyfikację stron ASP, gdyż programiści musieli szukać
błędów na całej stronie. Dzięki wykorzystaniu bloków deklarowania kodu problem ten niemal
całkowicie znikną, gdyż cały kod jest umieszczany w jednym miejscu — na samym początku
strony ASP.NET.
Programiści korzystający z tradycyjnej technologii ASP musieli oddzielać kod od treści stron przy
użyciu mechanizmów dołączania zawartości plików zewnętrznych (SSI). W takich dołączanych
plikach można było umieszczać całkowicie dowolny kod, w tym także kod HTML, dlatego też
często w nich umieszczano fragmenty interfejsu użytkownika wraz z ich obsługą. Mechanizmy
dołączania plików są wciąż dostępne lecz w stronach ASP.NET znacznie częściej korzysta się
aktualnie z elementów sterujących użytkownika lub kodu obsługi formularzy. Oba te rozwiązania
— zarówno elementy sterujące użytkownika, jak i kod obsługi formularzy — dają znacznie
większe możliwości programistycznej kontroli nad tworzonym kodem niż technologia SSI, w
której możliwości takie praktycznie nie istniały.
Zupełnie nowym elementem ASP.NET są pliki zasobów. Programiści którzy mieli kontakt z
językami takimi jak Visual Basic (VB) lub C++ mogli się już z nimi spotkać. Pliki zasobów były
w tych językach bardzo często wykorzystywane, zresztą z tych samych powodów, dla których
aktualnie są one dostępne także w ASP.NET — głównie chodzi tu o możliwość oddzielania kodu
od treści.
W klasycznej technologii ASP nie było żadnych możliwości oddzielenia od stron ASP informacji
wykorzystywanych przy lokalizowaniu aplikacji. Z tego względu, bardzo często trzeba było
tworzyć wiele wersji tej samej strony. Co więcej, wcześniejsze wersje technologii ASP nie
pozwalały na modyfikowanie informacji kulturowych i regionalnych.
Wszystkie te modyfikacje sprawiają, że technologia ASP.NET jest znacznie bardziej
wszechstronna niż jej poprzednie wersje. Aktualnie programiści mają znacznie więcej możliwości
związanych z rozmieszczaniem kodu na stronach.