70-562 Debugging an ASP.NET Application

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Debugowanie witryny sieci Web może być trudnym procesem ze względu choćby na to, że zazwyczaj klient i serwer są na różnych maszynach. Ponad to może być również rozdzielona baza danych, dane w sesji, ciasteczkach itd. No i po tak strasznym wstępie można w końcu powiedzieć, że debugowanie dla Visual Studio a raczej narzędzi które udostępnia nie jest jakimś większym problem ;)

Konfiguracja debugowania w ASP.NET

Standardowo w Visual Studio do debugowania możemy użyć breakpoints, watch window, error information itd. Najpierw jednak wypadałoby skonfigurować nasze środowisko do tego procesu. Istnieją dwa miejsca gdzie możemy to zrobić: właściwości projektu strony lub web.config. 

Aktywacja debugera ASP.NET

Pierwszym krokiem jest umożliwienie debugowania w ASP.NET we właściwościach strony. Jest to domyślnie włączone ale może się zdarzyć, że z jakiś przyczyn np. chcielibyśmy to zmienić. Aby to zrobić musimy wykonać następujące czynności:

  1. Kliknij PPM na projekt strony w Solution Explorer
  2. Wybieramy Property Pages
  3. Następnie z listy wybieramy Start Option
  4. I widzimy dostępne okno (jak poniżej) w którym możemy np. wyłączyć debugowanie dla ASP.NET

111

Konfiguracja debugera

Drugim krokiem może być ograniczanie debugowania np. tylko dla poszczególnych stron. Domyślnie jest to wyłączone. Włączając debugowanie oczywiście tracimy na wydajności strony. Ponad to podczas wystąpienia błędy dostaniemy o nim pełna informacje poza środowiskiem Visual Studio co może być niebezpieczne i potencjalnie wykorzystane. Dlatego debugowanie możemy sobie włączyć na poszczególnych stornach ale jest to zalecane tylko w czasie tworzenia i rozwoju projektu. Aby włączyć debugowanie dla całej witryny należy umieścić w web.configu następującą sekcje:

   1: <configuration>
   2:     <system.web>
   3:         <compilation debug="true">
   4:         </compilation>
   5:     <system.web>
   6: </configuration>

 

Ale czasami jest chcielibyśmy ograniczyć debugowanie indywidualnie dla poszczególnych stron. W tym wypadku musimy umieścić następujący kod w dyrektywie @Page

   1: <%@ Page Debug="true" ... %>

 

Definiowanie własnych błędów

Jest wielce prawdopodobne, że przy wysypaniu się na czymś naszej strony chcielibyśmy pokazać jakiś swój własny komunikat niż ten domyślnie ustawiony w środowisku. Również gdzie ewentualnie można znaleźć pomoc itd.

Konfiguracja niestandardowej strony błędu na poziomie witryny

Możemy ustawić naszą niestandardową stronę błędu umieszczając w web.congifu sekcję <customErrors>. Element ten ma dwa atrybuty mode i defaultRedirect

   1: <configuration>
   2:     <system.web>
   3:             <customErrors defaultRedirect="SiteErrorPage.aspx" mode="RemoteOnly">
   4:             </customErrors>
   5:     <system.web>
   6: </configuration>

 

mode=”RemoteOnly” oznacza, włączenie custom error tylko dla zdalnych klientów.  Atrybut DefaultRedirect jest używany do wskazania ścieżki do domyślnej strony błędu.

Konfiguracja strony błędu dla specyficznego błędu

Możliwe jest ustawienie różnych storn dla specyficznego rodzaju kodu błędu. Zapewnia to większą komunikacje z użytkownikiem. Np. użytkownik dostaje kod 403 i my przekierowujemy go na stronę gdzie jest ładnie i zgrabnie wyjaśnione użytkownikowi, że np. nie ma dostępu do danego zasobu bądź strony. Chcąc to zrobić musimy umieść w web.configu następującą sekcje:

   1: <configuration>
   2:     <system.web>
   3:         <customErrors defaultRedirect="SiteErrorPage.aspx" mode="RemoteOnly">
   4:             <error statusCode="403" redirect="RestrictedAccess.aspx" />
   5:         </customErrors>
   6:     <system.web>
   7: </configuration>

 

Zdalne debugowanie

Nie zawsze jest tak, że chcemy debugować kod na lokalnej stacji itd. Czasami może zaistnieć potrzeba zrobienia tego zdalnie. Proces uruchomiania zdalnego debugowania ułatwia nam Remote Debugging Monitor (Msvsmon.exe). Musimy uruchomić to narzędzie jeśli mamy zamiar debugować. U nas na dysku mając domyślnie zainstalowanego VS możemy szukać tego narzędzia w:

Program Files\Microsoft Visual Studio9.0\Common7\IDE\Remote Debugger\x64

Po włączeniu go mamy cały interfejs który m.in informuje nas o zdarzeniach podczas debugowania.

222

 

 

W TK jest opisane o autoryzacji takiego zdalnego debugowania, i jego konfiguracji. Odsyłam do tego chętnych.

Debugowanie skryptów po stronie klienta

Na koniec chciałbym jeszcze wspomnieć o tym, że jest możliwe debugowanie skryptów. Jest to przydatne jeżeli dużo piszemy w javascript. Na początek musimy włączyć w przeglądarce możliwość debugowania. W tym celu odpalamy przeglądarkę IE, wybieramy Narzędzia--->Opcje internetowe---->Zaawansowane i tam musimy “odhaczyć” wyłącz debugowanie skryptu.

333

I teraz możemy normalnie korzystać z debugowania w skryptach.

To tyle na dzisiaj. Jak co tydzień życzę Wam miłego weekendu i zapraszam w poniedziałek ;)

Tagi: , , , ,

70-562: Working with Custom Web Server Controls

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Tworzenie własnej Web Server Control

Niestandardowa kontrolka serwerowa może dziedziczyć z kontrolki WebServer. Musi zawierać kod który pozwoli ją renderować bądź może podziedziczyć to z innej kontrolki. Zazwyczaj są dwa podejścia do tworzenia takich kontrolek. Pierwsze z nich to podziedziczyć po WebControl która da nam podstawowy zestaw funkcjonalności. Obejmuje to również takie właściwości jak np. BackColor, ForeColor, Font, Height, i Width. Drugi częściej stosowany sposób to podziedziczyć po istniejącej już kontrolce i rozwijać jej funkcjonalność. Niezależnie od podejścia należy wziąć pod uwagę wielokrotne/ponowne użycie kontrolki. Jeśli nasza kontrolka będzie wykorzystywana w wielu witrynach należałoby ją utworzyć jako osobną .dll .

Dziedziczenie z istniejącej kontrolki

Jak przed chwilą wspomniałem typowym sposobem na tworzenie własnej kontrolki jest podziedziczenie po istniejącej już i rozszerzenie jej funkcjonalności. Powiedzmy, że chcemy stworzyć kontrolkę która będzie nam udostępniała TextBox i będziemy mogli ustawiać szerokość. W kodzie byśmy realizowali to następująco:

   1: //C#
   2: using System;
   3: using System.Web.UI;
   4: using System.Web.UI.WebControls;
   5: public class LabeledTextBox : TextBox
   6: {
   7: public string PromptText { get; set; }
   8: public int PromptWidth { get; set; }
   9: protected override void Render(HtmlTextWriter writer)
  10: {
  11: writer.Write(
  12: @"<span style="”display:inline-block;width:{0}px”">{1}&nbsp;</span>",
  13: PromptWidth, PromptText);
  14: base.Render(writer);
  15: }
  16: }

 

Potem żeby wykorzystać naszą kontrolkę postępujemy tak jak poniżej:

   1: //C#
   2: protected void Page_Init(object sender, EventArgs e)
   3: {
   4: int width = 150;
   5: MyUserControls.LabeledTextBox prompt1 = new
   6: MyUserControls.LabeledTextBox();
   7: prompt1.PromptText = “Enter Name:";
   8: prompt1.PromptWidth = width;
   9: form1.Controls.Add(prompt1);
  10: LiteralControl br = new LiteralControl("<br />");
  11: form1.Controls.Add(br);
  12: MyUserControls.LabeledTextBox prompt2 = new
  13: MyUserControls.LabeledTextBox();
  14: prompt2.PromptText = “Enter Address:";
  15: prompt2.PromptWidth = width;
  16: form1.Controls.Add(prompt2);
  17: }

 

W magiczny sposób otrzymamy:

11

Dziedziczenie bezpośrednio z klasy WebControl

Tutaj musimy pamiętać aby koniecznie aby nadpisać metodę Render. Na potrzeby tego działu powiedzmy, że chcemy mieć kontrolkę która wyświetla logo oraz nazwę firmy. Realizacje tego przedsięwzięcia ukazuje poniższy kod:

   1: //C#
   2: using System;
   3: using System.Web.UI;
   4: using System.Web.UI.WebControls;
   5: public class LogoControl : WebControl
   6: {
   7: public LogoControl()
   8: {
   9: }
  10: public string LogoUrl
  11: {
  12: get { return _logoUrl; }
  13: set { _logoUrl = value; }
  14: }
  15: private string _logoUrl;
  16: public string CompanyName
  17: {
  18: get { return _companyName; }
  19: set { _companyName = value; }
  20: }
  21: private string _companyName;
  22: protected override void Render(HtmlTextWriter writer)
  23: {
  24: writer.WriteFullBeginTag(“div”);
  25: writer.Write(@"<img src=""{0}"" /><br />", LogoUrl);
  26: writer.Write(CompanyName + "<br />");
  27: writer.WriteEndTag(“div”);
  28: }
  29: }

 

Dodawanie kontrolki do ToolBox’a

W poprzednim przykładzie dodanie kontrolki odbywało się dynamicznie co odpowiednio zapisaliśmy w kodzie. My jednak jesteśmy przyzwyczajeni, że mamy zbiór gotowych kontrolek, przeciągamy je na formatkę i gotowe ;) Nic nie stoi na przeszkodzie aby dodać naszą kontrolkę do ToolBox’a . Jednak aby można było to zrobić nasza kontrolka musi być w dll. Kiedy mam tak przygotowaną kontrolkę klikamy w toolboxie PPM i wybieramy Choose Items i dalej jasne ;) Powiedzmy, że mieliśmy wcześniej przygotowaną dll MyUserControls, to po dodaniu jej widzimy zawarte w niej i wcześniej opisywane kontrolki:

22

Widzimy, że obok nazwy mamy dodaną jakąś domyślną ikonkę. Nic nie stoi na przeszkodzie aby ustawić własną. Do tego należy wykorzystać atrybut ToolboxBitmap z przestrzeni nazw System.Drawing . Ten atrybut określa mapę bitową 16 × 16 pikseli.

   1: //C#
   2: [ToolboxBitmap(typeof(LabeledTextBox),
   3: “MyUserControls.LabeledTextBox.bmp”)]
   4: public class LabeledTextBox : TextBox

 

Aby jeszcze dopracować naszą kontrolkę przydałaby sie domyślna właściwość. Tą możemy określić atrybutem DefaultProperty z przestrzeni nazw System.ComponentModel .

   1: //C#
   2: [DefaultProperty(“PromptText”)]
   3: public class LabeledTextBox : TextBox

Tworzenie własnego wyglądu dla kontrolki

Naturalnie kontrolki z ToolBoxa mają już jakiś domyślny wygląd. Możemy podejrzeć je w Design View i pracować z nimi za pomocą okna Properties. Może zaistnieć naturalna potrzeba, że chcielibyśmy zmienić domyślny wygląd naszej kontrolki, ba nawet może się zdarzyć, że musimy pewne specyficzne rzeczy poustawiać  żeby kontrolka była widoczna.

Aby to zrobić musimy zacząć od dodania do naszej kontrolki referencji do System.Design.dll . Następnie tworzymy nową klasę która dziedziczy po klasie ControlDesigner. W niej nadpisujemy metodę GetDesignTimeHtml w której jest HTML użyty później jako wygląd naszej kontrolki. Jako przykład, pomyślmy, że mamy kontrolkę która jest “pochodną” LabelTextBoxa i przypomina nam o ustawieniu tekstu do niej. Jeśli  jest ustawiony “PromptText” to zostanie wyświetlony domyślny (bazowy) widok. Oto kod:

   1: namespace MyUserControls
   2: {
   3: public class LabeledTextBoxDesigner : ControlDesigner
   4: {
   5: private LabeledTextBox _labeledTextBoxControl;
   6: public override string GetDesignTimeHtml()
   7: {
   8: if (_labeledTextBoxControl.PromptText.Trim().Length == 0)
   9: return "<div style='color: Gray'>[Define PromptText]</div>";
  10: else
  11: return base.GetDesignTimeHtml();
  12: }
  13: public override void Initialize(IComponent component)
  14: {
  15: _labeledTextBoxControl = (LabeledTextBox)component;
  16: base.Initialize(component);
  17: return;
  18: }
  19: }
  20: }

Następnie musimy dodać do naszej klasy z kontrolką atrybut DesignerAttribute :

   1: //C#
   2: [Designer(“MyUserControls.LabeledTextBoxDesigner, MyUserControls”)]
   3: public class LabeledTextBox : TextBox

I teraz efekt pracy, jeśli PromptyText nie jest ustawiony kontrolka będzie wyglądała następująco:

33

Tworzenie kontrolki złożonej

Możemy stworzyć własną tkz. kontrolkę złożona. Aby to uczynić musimy stworzyć klasę która podziedziczy po klasie CompositeControl. W nowej klasie konieczne jest przysłonięcie metody CreateChildControls. W niej m.in ustawiamy wszystkie potrzebne właściwości. Ponieważ każda “child controls” wie jak radzić sobie z takimi mechanizmami jak ViewState, PostBack my oszczędzamy sobie czasu i nerwy na pisanie dodatkowego kodu. Jak zrobić naszą własną kontrolkę gdzie mielibyśmy nazwę użytkownika, hasło oraz przycisk Submit? Kod oraz wygląd przykładowy poniżej:

44

   1: //C#
   2: using System;
   3: using System.ComponentModel;
   4: using System.Web.UI;
   5: using System.Web.UI.WebControls;
   6: using System.Drawing;
   7: public class UserPasswordControl : CompositeControl
   8: {
   9: public event System.EventHandler Submitted;
  10: public string UserName
  11: {
  12: get
  13: {
  14: TextBox txt = (TextBox)FindControl(“UserName”);
  15: return txt.Text;
  16: }
  17: set
  18: {
  19: TextBox txt = (TextBox)FindControl(“UserName”);
  20: txt.Text = value;
  21: }
  22: }
  23: public string Password
  24: {
  25: get
  26: {
  27: TextBox pwd = (TextBox)FindControl(“Password”);
  28: return pwd.Text;
  29: }
  30: set
  31: {
  32: TextBox pwd = (TextBox)FindControl(“Password”);
  33: pwd.Text = value;
  34: }
  35: }
  36: protected override void CreateChildControls()
  37: {
  38: Panel pnl = new Panel();
  39: TextBox txtUserName = new TextBox();
  40: TextBox txtPassword = new TextBox();
  41: Button btnSubmit = new Button();
  42: btnSubmit.Click += new EventHandler(btnSubmit_Click);
  43: //start control buildup
  44: Controls.Add(pnl);
  45: //add user name row
  46: pnl.Controls.Add(new LiteralControl("<table><tr><td>"));
  47: pnl.Controls.Add(new LiteralControl(“User Name:"));
  48: pnl.Controls.Add(new LiteralControl("</td><td>"));
  49: pnl.Controls.Add(txtUserName);
  50: pnl.Controls.Add(new LiteralControl("</td></tr>"));
  51: //add password row
  52: pnl.Controls.Add(new LiteralControl("<tr><td>"));
  53: pnl.Controls.Add(new LiteralControl(“Password:"));
  54: pnl.Controls.Add(new LiteralControl("</td><td>"));
  55: pnl.Controls.Add(txtPassword);
  56: pnl.Controls.Add(new LiteralControl("</td></tr>"));
  57: //add submit button row
  58: pnl.Controls.Add(new LiteralControl(
  59: @"<tr><td colspan="”2”" align="”center”" >"));
  60: pnl.Controls.Add(btnSubmit);
  61: pnl.Controls.Add(new LiteralControl("</td></tr></table>"));
  62: //set up control properties
  63: pnl.Style.Add(“background-color”, “silver”);
  64: pnl.Style.Add(“width”, “275px”);
  65: txtUserName.ID = “UserName”;
  66: txtUserName.Style.Add(“width”, “170px”);
  67: txtPassword.ID = “Password”;
  68: txtPassword.TextMode = TextBoxMode.Password;
  69: txtPassword.Style.Add(“width”, “170px”);
  70: btnSubmit.Text = “Submit”;
  71: }
  72: void btnSubmit_Click(object sender, EventArgs e)
  73: {
  74: if (Submitted != null) Submitted(this, e);
  75: }
  76: }
  77: //C#
  78: using System;
  79: using System.Web.UI;
  80: using System.Web.UI.WebControls;
  81: public partial class UserPasswordControlTest : System.Web.UI.Page
  82: {
  83: protected void Page_Init(object sender, EventArgs e)
  84: {
  85: UserPasswordControl p = new MyUserControls.UserPasswordControl();
  86: p.Style.Add(“position”, “absolute”);
  87: p.Style.Add(“left”, “25px”);
  88: p.Style.Add(“top”, “50px”);
  89: form1.Controls.Add(p);
  90: p.Submitted += new EventHandler(p_Submitted);
  91: }
  92: void p_Submitted(object sender, EventArgs e)
  93: {
  94: UserPasswordControl p = (UserPasswordControl)sender;
  95: Response.Write(“User: " + p.UserName + " Pass: " + p.Password);
  96: }
  97: }

To tyle na dzisiaj. Myślę, że tworzenie własnych kontrolek to bardzo ciekawy temat. W tym artykule są oczywiście podstawy podstaw ale warto rozszerzyć wiedzę własną w tym zakresie czego sobie i Wam życzę ;) Miłego weekendu!

Tagi: , , ,

70-562: Creating and Consuming XML Web Services

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

ASP.NET udostępnia prosty model dla klienta używającego Web Services. Generowany jest obiekt proxy kiedy wykorzystujemy referencje do Web Services. Obiekt proxy zajmuje się serializacją, wiadomościami SOAP i związanymi z nimi procesami. Poniżej rysunek z TK modelu XML Web Service w ASP.NET :

1

Tworzenie Web Service w ASP.NET

Tworząc XML Web Services w ASP.NET dziedziczymy po klasie System.Web.Services.WebService. Klasa ta zapewnia wrapper dla kodu usługi. Spójrzmy na schemat relacji klas przy tworzeniu Web Service:

2

Każda z tych klas kontroluje prace naszej usługi webowej. Jak widzimy aby uzyskać dostęp do standardowych funkcji ASP.NET musimy podziedziczyć po klasie WebService. Atrybuty klasa pozwalają oznaczyć część naszego kodu który jest związany z XML Web Service. Elementy oznaczone jako te przeznaczone do stosowania przez usługę są identyfikowane automatycznie przez ASP.NET. Również klasa ta “umie” deserializować wnioski, wywoływać usługę czy też serializować odpowiedzi. Obsługuje również prace z SOAP, XML, Web Services Description Language (WSDL) i związanych z nimi standardów usług internetowych.

Projekt Usługi Web ASP.NET

Web Service definiujemy w pliku .asmx. Plik ten może być dodawany bezpośrednio do istniejącej witryny sieci Web. Jest to przydatne kiedy nasze witryna oprócz bycia samą w sobie stroną www będzie potrzebowała korzystać z usług. Oczywiście możemy stworzyć stronę www która składa się z samych usług.

Tak jak strony www, web service jest wyrażony przy pomocy URL. Co w praktyce oznacza adres w stylu http://mojadomena/mojausluga.asmx. W samym pliku .asmx jest tylko informacja, że kod obsługujący stronę znajduje się w oddzielnym pliku. Załóżmy, że chcemy dodać usługę w której mamy metody, udostępniające nam prace z Autorami na jakiejś przykładowej bazie danych. Możemy zacząć od stworzenia pliku Authors.asmx. W pliku tym zawieramy dyrektywę @ WebService która wskazuje rzeczywiste miejsce kodu usługi. W kodzie wygląda to dokładnie tak:

   1: //C#
   2: <%@ WebService Language="C#" CodeBehind="Authors.asmx.cs" Class="PubsServices.Authors"
   3: %>

 

Klasa WebServiceAttribute

Stworzenie pliku .asmx plus udostępnienie publicznej metody oznaczonej jako [WebMethod] (o czym za chwile) jest wystarczające do określenia usługi w środowisku ASP.NET. Istnieje jednak wiele innych klas które mogą zapewnić dodatkową funkcjonalność. Jedną z nich jest tytułowa WebServiceAttribute. Może ona być wykorzystana do dostarczenia informacji o usłudze sieci Web. Informacje te są wykorzystywane po stronie klienckiej, podczas odwołania się do usługi.

Możemy podawać zarówno przestrzeń nazw i opis naszej usługi sieciowej przez stosowanie atrybutu WebService do naszej klasy. Przestrzeń nazw określona dla naszej usługi, natomiast opisem jest zwykły tekst. Visual Studio do czasu określenia właściwej domeny używa tempuri.org .Przykład dodawania takiego atrybutu dla klasy Authors poniżej:

   1: //C#
   2: [WebService(Description = "Services related to published authors",
   3: Namespace = "http://tempuri.org/")]
   4: public class Authors

 

Klasa WebService

Klasa WebService może stanowić klasę bazową do stworzenia XML Web Services w ASP.NET. Klasa ta jest podobna do klasy Page w stronach www. Umożliwia ona dostęp do takich obiektów jak Application czy Session. Na początku podkreśliłem, że klasa WebService może stanowić klasę bazową…może ale nie musi ;) Tak naprawdę powinniśmy po niej dziedziczyć wtedy kiedy jest nam potrzebna funkcjonalność aplikacji ASP.NET. Poniżej kod który pokazuje zapis klasy z dziedziczeniem:

   1: //C#
   2: [WebService(Description = "Services related to published authors",
   3: Namespace = "http://tempuri.org/")]
   4: public class Authors : System.Web.Services.WebService

 

Klasa WebMethodAttribute

Nasza usługa udostępnia metody a każda z tych metod stanowi pewną obudowaną funkcjonalność. W klasie metody te oznaczamy atrybutem [WebMethod]. Spowoduje to, że daną metodę chcemy “udostępnić światu”. Można przypisać atrybut [WebMethod] bez żadnych dodatkowych parametrów i to po prostu nam określi metodę jako metodę usługi sieci Web. Jednak mamy szereg dostępnych parametrów które potrafią dodać pewną funkcjonalność. Na przykład enableSessionState (określa czy metoda ma w jakiś sposób współpracować z session state, do wyboru true/false) czy bufferResponse (określa czy odpowiedź ma być buforowana z powrotem do klienta). Poniżej jest kod który wykorzystuje parametr cacheDuration (określa ile czasu mają być przechowywane dane w pamięci podręcznej) :

   1: //C#
   2: [WebMethod(CacheDuration=300)]
   3: public DataTable GetAuthorTitles(string authorId)
   4: { ... }

 

Używanie Web Service w ASP.NET

Usługę Webową możemy uruchomić praktycznie w każdej aplikacji która jest w stanie stworzyć połączenie HTTP. Nawet w aplikacji konsolowej ;)  Aby rozpocząć korzystanie z usługi, takowa musi już gdzieś istnieć. Może ona istnieć gdzieś na serwerze, w naszym projekcie, gdziekolwiek. W projekcie który mam wybieramy w solution explorer PPM na projekt i wybieramy Add Web Reference. Otworzy się nam okno gdzie ustawiamy m.in adres URL, wybieramy pliki typu .asmx i ustawiamy nazwę dla naszej referencji. Poniżej zrzut ekranu okna w którym jest pokazane przykładowe dodanie usługi Authors.asmx :

3 

Widzimy albo i nie ;) , że usługa ta udostępnia nam dwie metody GetAuthor(); i GetAuthorTitles(); Co one miały robić w domyśle autora nie ważne ;)

Wywoływanie Web Service

Jest to tak proste, jak pisanie kodu w celu wywołania metody. Na przykład, następujący kod pokazuje jak wywołać metodę GetAuthor() :

   1: //C#
   2: PubServices.Authors pubs = new PubServices.Authors();
   3: PubServices.Author auth = pubs.GetAuthor();
   4: Label1.Text = auth.FirstName + " " + auth.LastName;

 

Można również wiązać wywołania usługi. Jako przykład użyjemy wcześniej wspomnianej metody GetAuthorsTitles() która zwracała DataTable dlatego aby powiązać dane np. z GridView wystarczy odpowiednio go zdefiniować:

   1: <asp:ObjectDataSource runat="server"
   2: ID="ObjectDataSourceAuthors"
   3: TypeName="PubsServices.Authors">
   4: SelectMethod="GetAuthorTitles"
   5: <SelectParameters>
   6: <asp:QueryStringParameter
   7: Name="authorId"
   8: QueryStringField="auId"
   9: Type="String" />
  10: </SelectParameters>
  11: </asp:ObjectDataSource>
  12: <asp:GridView ID="GridView1" runat="server"
  13: DataSourceID="ObjectDataSourceAuthors">
  14: </asp:GridView>

 

Łączenie się z usługa przy pomocy skryptów z wykorzystaniem AJAX

Możemy użyć wbudowany w ASP.NET AJAX funkcjonalności aby wywołać usługę webową przy użyciu javascript po stronie klienta. Jednak trzeba pamiętać o pewnych rzeczach przy konfiguracji takowego rozwiązania. Ważne jest np. aby nasza “strona kliencka” była w tej samej domenie co pliki .asmx! Poniższe kroki powinny pomóc nam przy konfiguracji:

  • oznacz metody których będziesz chciał w taki sposób używać parametrem ScriptServiceAttribute.
  • “zarejestruj” na stronie ScriptHandlerFactory. Możesz to zrobić wewnątrz elementu <system.web> <httpHandlers> w pliku Web.config.
  • dodaj ScirptManager do naszej strony. Jest on wymagany dla każdych stron używających AJAX, tutaj dodatkowo wewnątrz jego trzeba dodać referencje do usług (ServiceReference).
  • dodaj metody javascript do strony.
  • można również dodać inne metody javascript którę obsłużą callback

Dla większego zobrazowania przykład- otóż wyobraź sobie, że masz usługę która pobiera temperaturę w stopniach Farenheita a zwraca w Celsjuszach. Kod takiej metody w usłudze wyglądałby następująco:

   1: //C#
   2: [System.Web.Script.Services.ScriptService]
   3: [WebService(Namespace = "http://tempuri.org/")]
   4: public class TempConversion : System.Web.Services.WebService
   5: {
   6: [WebMethod]
   7: public float GetCelsius(float temperature)
   8: {
   9: return (5 / 9) * (temperature - 32);
  10: }
  11: }

 

Następnie musimy się upewnić, że jest dodany w web.configu ScriptHandlerFactory. Oto kod:

   1: <httpHandlers>
   2: <remove verb="*" path="*.asmx" />
   3: <add verb="*" path="*.asmx" validate="false"
   4: type="System.Web.Script.Services.ScriptHandlerFactory" />
   5: </httpHandlers>

 

Pozostaje nam stworzyć stronę po stronie klienckiej. Pamiętajmy o dodaniu ScriptManagera i dodaniu do niego referencji:

   1: <asp:ScriptManager runat="server" ID="ScriptManager1">
   2: <Services>
   3: <asp:ServiceReference
   4: path="TempConversion.asmx" />
   5: </Services>
   6: </asp:ScriptManager>

 

Następnym krokiem jest dodanie metod javascript:

   1: <script type="text/javascript">
   2: function GetCelsius()
   3: {
   4: var val = document.getElementById("TextBoxTemp");
   5: TempConversion.GetCelsius(val.value, FinishCallback);
   6: }
   7: function FinishCallback(result)
   8: {
   9: var results = document.getElementById("LabelResults");
  10: results.innerHTML = result;
  11: }
  12: </script>

 

I na końcu oczywiście pola tekstowe do wpisywania wartości i wyniku oraz przycisk wywołujący metodę:

   1: <asp:TextBox ID="TextBoxTemp" runat="server"></asp:TextBox>&nbsp;
   2: <asp:Label ID="LabelResults" runat="server" Text=""></asp:Label>
   3: <br />
   4: <input id="Button1" type="button" value="Calculate" onclick="GetCelsius()" />

 

To tyle w tym temacie. Traning Kit porusza również ważny temat zabezpieczenia naszej usługi. Ale to pozostawiam Wam jako prace domową na weekend. Moment na doczytanie ;) Do poniedziałku! :)

Tagi: , , , ,

70-562: Working with XML data

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Jak wiadomo .NET ma szeroko pojęte wsparcie dla XML. Implementacja XML to wydajność, niezawodności i skalowalność a w połączeniu z ADO.NET możliwość korzystania z XML jako źródła danych.

Klasy XML

Klasy XML są dostępne w System.Xml.dll czyli potrzebujemy dyrektywy using System.Xml. System.Data.dll rozszerza wspomnianą przestrzeń o chociażby klasę XmlDataDocument. Chciałbym teraz po krótce przyjrzeć się podstawowym klasom XML w .NET Frameworku. Każda z tych klas oferuje różne stopnie funkcjonalności, dlatego ważne jest aby odpowiednio przeanalizować swój problem i wybrać odpowiednią z nich. Poniższy rysunek pokazuje ogólnie obiekty o których będziemy mówić:

1

XmlDocument i XmlDataDocument

Klasy te możemy wykorzystywać np. do nawigacji i edycji węzłów XML. XmlDataDocument dziedziczy po XmlDocument i reprezentuje relacyjne dane. XmlDataDocument może przedstawiać swoje dane jako DataSet zarówno jako relacyjny i nierelacyjny widok danych. Klasy te dostarczają wiele metod które są opisane i ładnie zebrane w TK w ładnej tabelce. Większość z nich jest intuicyjna i po samej nazwie możemy wnioskować do czego służą.

XPathDocument

Klasa służy tylko do odczytu z pamięci podręcznej XMLDocument, wykorzystywana do bardzo szybkich zapytań XPath.

XmlConvert

Klasa ta zawiera wiele statycznych metod konwersji pomiędzy typami XSD a zawartymi w CLR. Klasa te jest szczególnie ważna podczas pracy ze źródłami danych, które umożliwiają nazwy które nie są dozwolone w XML. Jeśli mamy kolumnę w bazie danych która nazywa się List Price to próba stworzenia np. atrybutu o takich nazwie wyrzuci nam wyjątek. XMLConnvert zakoduje nam spacje na _0x0020_ dzięki czemu otrzymamy nazwę List_x0020_Price którą jest już prawidłową nazwą i możemy ją odkodować używając metode XmlConvert.DecodeName. Również klasa ta posiada wiele metod statycznych które potrafią konwertować ciągi znaków na typy numeryczne.

XPathNavigator

Klasa ta zapewnia skuteczną nawigację w dokumencie xml używając do tego XPath. Klasa ta wspiera Extensible Stylesheet Language Transformations (XSLT).

XmlNodeReader

Klasa ta umożliwia dostęp do danych a dokładnie mówiąc wejście w dowolny węzeł pliku XML.

XmlTextReader

Klasa udostępnia podstawowy dostęp do danych. Nie zapisuje wyników swojej pracy w pamięci podręcznej. XmlTextReader nie wykonuje walidacji dokumentu, ale sprawdza dane XML pod kątem poprawności “uformowania” ich.

XmlTextWriter

Pozwala zapisywać dane do w postaci XML do pliku zapewniając przy tym, że będą zgodne ze standardem W3C XML 1.0. Klasa ta zwiera wsparcie dla przestrzeni nazw i rozwiązywania problemów z nimi związanymi.

XmlReader

Klasa służy również do odczytu i waliduje dane zgodnie z DTD, XDR lub XDS. Sam konstruktor oczekuje źródła pliku “sprawdzonych” danych.

XslTransform

Klasa ta umożliwia przekształcenie dokumentu XML z wykorzystaniem arkusza stylów XSL. Obsługuje ona składnie w wersji 1.0 i oferuje dwie metody Load i Transform.

Praca z dokumentem XML

Z pewnością istnieje wiele metod pracy z dokumentem XML w .NET Frameworku. Dzisiaj pokażemy sobie w jaki sposób można wykonać podstawowy zapis, odczyt, wyszukiwanie danych. Teraz multum przykładów, kodu zawartego w TK który pokazuję realizacje tych i wielu innych zadań.

Tworzenie nowego dokumentu

Aby stworzyć dokument XML musimy zacząć od utworzenia obiektu XMLDocument. Zawiera on m.in metodę typu CreateElement i CreateAttribute które pozwalają nam tworzyć poszczególne węzły. Spójrzmy na poniższy kod, który wraz z komentarzami mówi sam za siebie ;)

   1: //C#
   2: protected void Button1_Click(object sender, EventArgs e)
   3: {
   4: //Declare and create new XmlDocument
   5: XmlDocument xmlDoc = new XmlDocument();
   6: XmlElement el;
   7: int childCounter;
   8: int grandChildCounter;
   9: //Create the xml declaration first
  10: xmlDoc.AppendChild(
  11: xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null));
  12: //Create the root node and append into doc
  13: el = xmlDoc.CreateElement("myRoot");
  14: xmlDoc.AppendChild(el);
  15: //Child Loop
  16: for (childCounter = 1; childCounter <= 4; childCounter++)
  17: {
  18: XmlElement childelmt;
  19: XmlAttribute childattr;
  20: //Create child with ID attribute
  21: childelmt = xmlDoc.CreateElement("myChild");
  22: childattr = xmlDoc.CreateAttribute("ID");
  23: childattr.Value = childCounter.ToString();
  24: childelmt.Attributes.Append(childattr);
  25: //Append element into the root element
  26: el.AppendChild(childelmt);
  27: for (grandChildCounter = 1; grandChildCounter <= 3; grandChildCounter++)
  28: {
  29: //Create grandchildren
  30: childelmt.AppendChild(xmlDoc.CreateElement("GrandChild"));
  31: }
  32: }
  33: //Save to file
  34: xmlDoc.Save(MapPath("XmlDocumentTest.xml"));
  35: Label lbl = GetLabel(275, 20);
  36: lbl.Text = "XmlDocumentTest.xml Created";
  37: }

 

Kod ten wygeneruje następujący dokument XML:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <myRoot>
   3: <myChild ID="1">
   4: <GrandChild />
   5: <GrandChild />
   6: <GrandChild />
   7: </myChild>
   8: <myChild ID="2">
   9: <GrandChild />
  10: <GrandChild />
  11: <GrandChild />
  12: </myChild>
  13: <myChild ID="3">
  14: <GrandChild />
  15: <GrandChild />
  16: <GrandChild />
  17: </myChild>
  18: <myChild ID="4">
  19: <GrandChild />
  20: <GrandChild />
  21: <GrandChild />
  22: </myChild>
  23: </myRoot>
Parsowanie dokumentu przy użyciu DOM i XPathNavigator

Oba przykłady pokazują jak rekurencyjnie analizować dokument XML. Jednak trzeba pamiętać, że XPathNavigator udostępnia nam szereg metod z których możemy dodatkowo korzystać.

   1: //C#
   2: Label lbl = new Label();
   3: protected void Button2_Click(object sender, EventArgs e)
   4: {
   5: lbl = GetLabel(275, 20);
   6: XmlDocument xmlDoc = new XmlDocument();
   7: xmlDoc.Load(MapPath("XmlDocumentTest.xml"));
   8: RecurseNodes(xmlDoc.DocumentElement);
   9: }
  10: public void RecurseNodes(XmlNode node)
  11: {
  12: //start recursive loop with level 0
  13: RecurseNodes(node, 0);
  14: }
  15: public void RecurseNodes(XmlNode node, int level)
  16: {
  17: string s;
  18: s = string.Format("{0} <b>Type:</b>{1} <b>Name:</b>{2} <b>Attr:</b> ",
  19: new string('-', level), node.NodeType, node.Name);
  20: foreach (XmlAttribute attr in node.Attributes)
  21: {
  22: s += string.Format("{0}={1} ", attr.Name, attr.Value);
  23: }
  24: lbl.Text += s + "<br>";
  25: foreach (XmlNode n in node.ChildNodes)
  26: {
  27: RecurseNodes(n, level + 1);
  28: }
  29: }
   1: //C#
   2: protected void Button3_Click(object sender, EventArgs e)
   3: {
   4: lbl = GetLabel(275, 20);
   5: XmlDocument xmlDoc = new XmlDocument();
   6: xmlDoc.Load(MapPath("XmlDocumentTest.xml"));
   7: XPathNavigator xpathNav = xmlDoc.CreateNavigator();
   8: xpathNav.MoveToRoot();
   9: RecurseNavNodes(xpathNav);
  10: }
  11: public void RecurseNavNodes(XPathNavigator node)
  12: {
  13: //start recursive loop with level 0
  14: RecurseNavNodes(node, 0);
  15: }
  16: public void RecurseNavNodes(XPathNavigator node, int level)
  17: {
  18: string s = null;
  19: s = string.Format("{0} <b>Type:</b>{1} <b>Name:</b>{2} <b>Attr:</b> ",
  20: new string('-', level), node.NodeType, node.Name);
  21: if (node.HasAttributes)
  22: {
  23: node.MoveToFirstAttribute();
  24: do
  25: {
  26: s += string.Format("{0}={1} ", node.Name, node.Value);
  27: } while (node.MoveToNextAttribute());
  28: node.MoveToParent();
  29: }
  30: lbl.Text += s + "<br>";
  31: if (node.HasChildren)
  32: {
  33: node.MoveToFirstChild();
  34: do
  35: {
  36: RecurseNavNodes(node, level + 1);
  37: } while (node.MoveToNext());
  38: node.MoveToParent();
  39: }
  40: }

 

Wyszukiwanie danych przy użyciu DOM

DOM wspiera metody GetElementByID i the GetElementsByTagName do wyszukiwania danych w dokumencie XML. GetElementByID lokalizuje element na podstawie jego unikatowego ID. Spójrzmy na plik DTD zdefiniowany następująco:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <!DOCTYPE myRoot [
   3: &lt;!ELEMENT myRoot ANY>
   4: <!ELEMENT myChild ANY>
   5: <!ELEMENT myGrandChild EMPTY>
   6: <!ATTLIST myChild
   7: ChildID ID #REQUIRED
   8: >
   9: ]>
  10: <myRoot>
  11: <myChild ChildID="ref-1">
  12: <myGrandChild/>
  13: <myGrandChild/>
  14: <myGrandChild/>
  15: </myChild>
  16: <myChild ChildID="ref-2">
  17: <myGrandChild/>
  18: <myGrandChild/>
  19: <myGrandChild/>
  20: </myChild>
  21: <myChild ChildID="ref-3">
  22: <myGrandChild/>
  23: <myGrandChild/>
  24: <myGrandChild/>
  25: </myChild>
  26: <myChild ChildID="ref-4">
  27: <myGrandChild/>
  28: <myGrandChild/>
  29: <myGrandChild/>
  30: </myChild>
  31: </myRoot>

 

Każde “dziecko” ma swoje Id i powiedzmy że chcemy znaleźć element o ID=ref-3. Realizuje to poniższy kod:

   1: //C#
   2: protected void Button4_Click(object sender, EventArgs e)
   3: {
   4: lbl = GetLabel(275, 20);
   5: string s;
   6: //Declare and create new XmlDocument
   7: XmlDocument xmlDoc = new XmlDocument();
   8: xmlDoc.Load(MapPath("XmlSample.xml"));
   9: XmlNode node;
  10: node = xmlDoc.GetElementById("ref-3");
  11: s = string.Format("<b>Type:</b>{0} <b>Name:</b>{1} <b>Attr:</b>",
  12: node.NodeType, node.Name);
  13: foreach (XmlAttribute a in node.Attributes)
  14: {
  15: s += string.Format("{0}={1} ", a.Name, a.Value);
  16: }
  17: lbl.Text = s + "<br>";
  18: }

Do powyższego zadania również użyteczna może być metoda SelectSingleNode. Świetnie się nadaje do tego celu i spójrzmy na realizacje tego samego problemy z wykorzystaniem wspomnianej metody:

   1: //C#
   2: protected void Button5_Click(object sender, EventArgs e)
   3: {
   4: lbl = GetLabel(275, 20);
   5: string s;
   6: //Declare and create new XmlDocument
   7: XmlDocument xmlDoc = new XmlDocument();
   8: xmlDoc.Load(MapPath("XmlSample.xml"));
   9: XmlNode node;
  10: node = xmlDoc.SelectSingleNode("//myChild[@ChildID='ref-3']");
  11: s = string.Format("<b>Type:</b>{0} <b>Name:</b>{1} <b>Attr:</b>",
  12: node.NodeType, node.Name);
  13: foreach (XmlAttribute a in node.Attributes)
  14: {
  15: s += string.Format("{0}={1} ", a.Name, a.Value);
  16: }
  17: lbl.Text = s + "<br>";
  18: }

 

Aby zwrócić węzły o danych nazwach możemy wykorzystać metodę GetElementsByTagName. Powiedzmy, że chcemy zwrócić dane z węzłów o nazwie myGrandChild:

   1: //C#
   2: protected void Button6_Click(object sender, EventArgs e)
   3: {
   4: lbl = GetLabel(275, 20);
   5: string s;
   6: //Declare and create new XmlDocument
   7: XmlDocument xmlDoc = new XmlDocument();
   8: xmlDoc.Load(MapPath("XmlSample.xml"));
   9: XmlNodeList elmts;
  10: elmts = xmlDoc.GetElementsByTagName("myGrandChild");
  11: foreach (XmlNode node in elmts)
  12: {
  13: s = string.Format("<b>Type:</b>{0} <b>Name:</b>{1}",
  14: node.NodeType, node.Name);
  15: lbl.Text += s + "<br>";
  16: }
  17: }

 

Ale nie jest to jedyny sposób realizacji tego zdania. Równie dobrze możemy wykorzystać do tego celu SelectNodes w następujący sposób (w sumie różnica w jednej linijce):

   1: //C#
   2: protected void Button7_Click(object sender, EventArgs e)
   3: {
   4: lbl = GetLabel(275, 20);
   5: string s;
   6: //Declare and create new XmlDocument
   7: XmlDocument xmlDoc = new XmlDocument();
   8: xmlDoc.Load(MapPath("XmlSample.xml"));
   9: XmlNodeList elmts;
  10: elmts = xmlDoc.SelectNodes("//myGrandChild");
  11: foreach (XmlNode node in elmts)
  12: {
  13: s = string.Format("<b>Type:</b>{0} <b>Name:</b>{1}",
  14: node.NodeType, node.Name);
  15: lbl.Text += s + "<br>";
  16: }
  17: }

 

GetElementByTag ogranicza się do nazwy, natomiast SelectNodes pozwala nam na większą elastyczność w poszukiwaniu węzła.

Zapisywanie do pliku przy użyciu XmlTextWriter

Co tu się dużo rozpisywać. Poniższy przykład tworzy listę pracowników i zapisuje dwóch z nich na ta listę. Kod realizujący to zadanie  efekt poniżej:

   1: //C#
   2: protected void Button10_Click(object sender, EventArgs e)
   3: {
   4: XmlTextWriter xmlWriter = new
   5: XmlTextWriter(MapPath("EmployeeList.xml"),
   6: System.Text.Encoding.UTF8);
   7: xmlWriter.Formatting = Formatting.Indented;
   8: xmlWriter.Indentation = 5;
   9: xmlWriter.WriteStartDocument();
  10: xmlWriter.WriteComment("XmlTextWriter Test Date: " +
  11: DateTime.Now.ToShortDateString());
  12: xmlWriter.WriteStartElement("EmployeeList");
  13: //New Employee
  14: xmlWriter.WriteStartElement("Employee");
  15: xmlWriter.WriteAttributeString("EmpID", "1");
  16: xmlWriter.WriteAttributeString("LastName", "JoeLast");
  17: xmlWriter.WriteAttributeString("FirstName", "Joe");
  18: xmlWriter.WriteAttributeString("Salary", XmlConvert.ToString(50000));
  19: xmlWriter.WriteElementString("HireDate",
  20: XmlConvert.ToString(DateTime.Parse("1/1/2003"),
  21: XmlDateTimeSerializationMode.Unspecified));
  22: xmlWriter.WriteStartElement("Address");
  23: xmlWriter.WriteElementString("Street1", "123 MyStreet");
  24: xmlWriter.WriteElementString("Street2", "");
  25: xmlWriter.WriteElementString("City", "MyCity");
  26: xmlWriter.WriteElementString("State", "OH");
  27: xmlWriter.WriteElementString("ZipCode", "12345");
  28: //Address
  29: xmlWriter.WriteEndElement();
  30: //Employee
  31: xmlWriter.WriteEndElement();
  32: //New Employee
  33: xmlWriter.WriteStartElement("Employee");
  34: xmlWriter.WriteAttributeString("EmpID", "2");
  35: xmlWriter.WriteAttributeString("LastName", "MaryLast");
  36: xmlWriter.WriteAttributeString("FirstName", "Mary");
  37: xmlWriter.WriteAttributeString("Salary", XmlConvert.ToString(40000));
  38: xmlWriter.WriteElementString("HireDate",
  39: XmlConvert.ToString(DateTime.Parse("1/2/2003"),
  40: XmlDateTimeSerializationMode.Unspecified));
  41: xmlWriter.WriteStartElement("Address");
  42: xmlWriter.WriteElementString("Street1", "234 MyStreet");
  43: xmlWriter.WriteElementString("Street2", "");
  44: xmlWriter.WriteElementString("City", "MyCity");
  45: xmlWriter.WriteElementString("State", "OH");
  46: xmlWriter.WriteElementString("ZipCode", "23456");
  47: //Address
  48: xmlWriter.WriteEndElement();
  49: //Employee
  50: xmlWriter.WriteEndElement();
  51: //EmployeeList
  52: xmlWriter.WriteEndElement();
  53: xmlWriter.Close();
  54: Response.Redirect("EmployeeList.xml");
  55: }

 

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <!--XmlTextWriter Test Date: 8/16/2006-->
   3: <EmployeeList>
   4: <Employee EmpID="1" LastName="JoeLast" FirstName="Joe" Salary="50000">
   5: <HireDate>2003-01-01T00:00:00</HireDate>
   6: <Address>
   7: <Street1>123 MyStreet</Street1>
   8: <Street2 />
   9: <City>MyCity</City>
  10: <State>OH</State>
  11: <ZipCode>12345</ZipCode>
  12: </Address>
  13: </Employee>
  14: <Employee EmpID="2" LastName="MaryLast" FirstName="Mary" Salary="40000">
  15: <HireDate>2003-01-02T00:00:00</HireDate>
  16: <Address>
  17: <Street1>234 MyStreet</Street1>
  18: <Street2 />
  19: <City>MyCity</City>
  20: <State>OH</State>
  21: <ZipCode>23456</ZipCode>
  22: </Address>
  23: </Employee>
  24: </EmployeeList>

 

Co jest fajne XMLTextWriter posiada właściwości opowiadające za formatowanie i wcięcia.

Odczytywanie z pliku przy pomocy XmlTextReader

Klasa XmlTextReader jest używana do odczytywania danych węzeł po węźle. Wadą używania tej klasy jest to, że odczyt jest typu “forward-only”. Poniższy kod pokazuje informacje o każdym węźle oczywiście na podstawie wcześniejszych danych:

   1: //C#
   2: protected void Button11_Click(object sender, EventArgs e)
   3: {
   4: lbl = GetLabel(275, 20);
   5: XmlTextReader xmlReader = new
   6: XmlTextReader(MapPath("EmployeeList.xml"));
   7: while (xmlReader.Read())
   8: {
   9: switch( xmlReader.NodeType)
  10: {
  11: case XmlNodeType.XmlDeclaration:
  12: case XmlNodeType.Element:
  13: case XmlNodeType.Comment:
  14: {
  15: string s;
  16: s = String.Format("{0}: {1} = {2}<br>",
  17: xmlReader.NodeType,
  18: xmlReader.Name,
  19: xmlReader.Value);
  20: lbl.Text += s;
  21: break;
  22: }
  23: case XmlNodeType.Text:
  24: {
  25: string s;
  26: s = String.Format(" - Value: {0}<br>",
  27: xmlReader.Value);
  28: lbl.Text += s;
  29: break;
  30: }
  31: }
  32: if (xmlReader.HasAttributes)
  33: {
  34: while (xmlReader.MoveToNextAttribute())
  35: {
  36: string s;
  37: s = String.Format(" - Attribute: {0} = {1}<br>",
  38: xmlReader.Name, xmlReader.Value);
  39: lbl.Text += s;
  40: }
  41: }
  42: }
  43: xmlReader.Close();
  44: }

 

Modyfikowanie dokumentu

Usunięcie węzła realizuje dwa zadania…znaleźć węzeł i go usunąć. Dodanie węzła to utworzenie go, znalezienie odpowiedniego miejsca i wstawienie go tam. Jak to zrealizować pokazuje poniższy przykład:

   1: //C#
   2: protected void Button12_Click(object sender, EventArgs e)
   3: {
   4: lbl = GetLabel(275, 20);
   5: //Declare and load new XmlDocument
   6: XmlDocument xmlDoc = new XmlDocument();
   7: xmlDoc.Load(MapPath("XmlSample.xml"));
   8: //delete a node
   9: XmlNode node;
  10: node = xmlDoc.SelectSingleNode("//myChild[@ChildID='ref-3']");
  11: node.ParentNode.RemoveChild(node);
  12: //create a node and add it
  13: XmlElement newElement =
  14: xmlDoc.CreateElement("myNewElement");
  15: node = xmlDoc.SelectSingleNode("//myChild[@ChildID='ref-1']");
  16: node.ParentNode.InsertAfter(newElement, node);
  17: xmlDoc.Save(MapPath("XmlSampleModified.xml"));
  18: Response.Redirect("XmlSampleModified.xml");
  19: }

Tyle na dzisiaj ;) Zasypałem Was kodem ale widocznie w TK uznali, że na przykładzie najlepiej to wszystko będzie widoczne. W TK również jest wspomniane o LINQ to XML. Możecie sobie doczytać o tych podstawach a i myślę, że warto rozszerzyć swoją wiedze o tym mechanizmie który wydaję mi się jest o wiele bardziej przejrzysty i “czystszy” w kodzie ;)

Tagi: , , , , ,

70-562 Creating Client Scripts with the AJAX Clientside Library

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Dzisiaj porozmawiamy sobie o podstawach javascript, o bibliotece Microsoft AJAX, pokażemy jak użyć jej po stronie klienta do kontroli serwera :)

Tworzenie własnego skryptu

Istnieją trzy podstawowe “modele” definiowania skryptów:

  • zdefiniować kod skryptu po stronie klienta na witrynie sieci WEB, lub atrybut odniesienia do gotowego pliku (.js) .
  • użyć klasy ClientScriptManager do dynamicznego definiowania skytpów.
  • użyć kontrolki ScriptManager do zarejestrowania javascipt na naszej stronie. 

Każdy ze sposobów opiszemy sobie poniżej.

Dodawanie bloków skryptu do naszej strony ASP.NET

Jak wspomniałem wcześniej w asp.net tradycyjnie na stronie możemy umieścić kod javascript na stronie bądź dołączyć plik (.js) tak jak to chyba najczęściej się stosuje. Korzystamy z tego sposobu kiedy nie musimy dynamicznie tworzyć skryptu na podstawie procesów zachodzących na stronie. Poniżej przykład strony która ukrywa wartość lub ja pokazuje w zależności od kliknięć w przycisk. Jako mega ciekawostkę mogę powiedzieć, że na IE skrypt ten działa na Firefox już nie :/ Ale to już odwieczny ból z css i js…trzeba wszędzie przetestować i dostosować ;) Ok wrzucam poniżej kod + skrypt:

   1: <body style="font-family: Verdana;">
   2: <form id="form1" runat="server">
   3: <div>
   4: <div style="width: 200px; background-color: Blue; color: White;
   5: border-style: solid; border-width: thin; border-color: Blue">
   6: <div style="float: left; vertical-align: middle; margin-top: 3px;">
   7: Element Title
   8: </div>
   9: <div style="float: right; vertical-align: middle">
  10: <input id="ButtonCollapse" type="button" value="Close"
  11: onclick="Collapse()" />
  12: </div>
  13: </div>
  14: <div id="DivCollapse" style="width: 200px; height: 200px;
  15: border-style: solid; border-width: thin; border-color: Blue">
  16: <div style="margin-top: 20px; text-align: center;">
  17: Content area ...
  18: </div>
  19: </div>
  20: </div>
  21: </form>
  22: </body>

 

   1: <head id="Head1" runat="server">
   2: <title>Script Block</title>
   3: <script language="javascript" type="text/javascript">
   4: function Collapse()
   5: {
   6: if (DivCollapse.style.display == "")
   7: {
   8: DivCollapse.style.display = "none";
   9: document.forms[0].ButtonCollapse.value = "Open";
  10: }
  11: else
  12: {
  13: DivCollapse.style.display = "";
  14: document.forms[0].ButtonCollapse.value = "Close";
  15: }
  16: }
  17: </script>
  18: </head>

 

Powyższy przykład pokazuje kod osadzony na stronie. Ale kiedy istnieje konieczność wykorzystania tego samego skryptu na wielu stronach należy zapisać go do w pliku .js a następnie na każdej z nich dołączyć go w następujący sposób:

   1: <script type="text/javascript" src="SiteScripts.js"></script>

W ten sposób nie powielamy kod i zyskujemy na wydajności gdyż skrypt jest buforowany przez przeglądarkę.

Dynamiczne dodawanie skryptów

Musimy być przygotowani na to, że zaistnieje konieczność tworzenia dynamicznie skryptu, którego kod determinują działania wykonywane na stronie. Do tego celu możemy użyć klasy ClientScriptManager. Aby dodać skrypt musimy mieć go zdefiniowanego w pliku lub jako string. Następnie wywołujemy metodę RegisterClientScriptBlock obiektu ClientScript do której przekazujemy typ, klucz który jednoznacznie identyfikuje skrypt, sam skrypt oraz na koniec wartość typu boolean która określa czy jest potrzeba generowania tagów skryptu czy też nie.

Załóżmy, że chcieli byśmy umożliwić użytkownikowi na stronie sprawdzenie poziomu trudności wpisanego hasła. Poniższy kod realizuje to zadanie na życzenie użytkownika, czyli jeżeli zaznaczy chcekboxa wygeneruje się dynamicznie kod javascript. Treść html:

   1: <body style="font-family: Verdana">
   2: <form id="form1" runat="server">
   3: <div>
   4: Enter Password<br />
   5: <asp:TextBox ID="TextBox1" runat="server" Width="250"></asp:TextBox>
   6: &nbsp;&nbsp;<span id="passwordStrength"></span>
   7: <br />
   8: <asp:CheckBox ID="CheckBox1" runat="server"
   9: Text="Turn on password strength checking" AutoPostBack="true" />
  10: </div>
  11: </form>
  12: </body>

Oraz skrypt:

   1: //C#
   2: public partial class DynamicScriptC : System.Web.UI.Page
   3: {
   4: protected void CheckBox1_CheckedChanged(object sender, EventArgs e)
   5: {
   6: if (CheckBox1.Checked)
   7: {
   8: string passFunc = "function CheckPassword() {";
   9: passFunc += @"var passLen = document.forms[0].TextBox1.value.length;";
  10: passFunc += @" if (passLen < 4) {";
  11: passFunc += @" document.getElementById(""passwordStrength"").";
  12: passFunc += @"innerText = ""weak"";";
  13: passFunc += @" document.getElementById(""passwordStrength"").";
  14: passFunc += @"style.color = ""red"";}";
  15: passFunc += @" else if (passLen < 6) {";
  16: passFunc += @" document.getElementById(""passwordStrength"").";
  17: passFunc += @"innerText = ""medium"";";
  18: passFunc += @" document.getElementById(""passwordStrength"").";
  19: passFunc += @"style.color = ""blue"";}";
  20: passFunc += @" else if (passLen > 9) {";
  21: passFunc += @" document.getElementById(""passwordStrength"").";
  22: passFunc += @"innerText = ""strong"";";
  23: passFunc += @" document.getElementById(""passwordStrength"").";
  24: passFunc += @"style.color = ""green"";}}";
  25: //register the script
  26: Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
  27: "CheckPasswordScript", passFunc, true);
  28: //add an event to the text box to call the script
  29: TextBox1.Attributes.Add("onkeyup", "CheckPassword()");
  30: }
  31: else
  32: {
  33: //remove the event from the text box
  34: TextBox1.Attributes.Remove("onkeyup");
  35: }
  36: }
  37: }

 

 

Użycie kontrolki ScriptManager

Kontrolka ta jest używana do ASP.NET AJAX Extension. Automatycznie rejestruje odpowiednie pliki skryptów zdefiniowane przez bibliotekę MS AJAX. Ponadto możemy dodać własne skrypty, deklaratywnie oraz programowo. Poniżej krótkie przykłady:

   1: <asp:ScriptManager ID="ScriptManager1" runat="server">
   2: <Scripts>
   3: <asp:ScriptReference Name="AppScripts.js" />
   4: </Scripts>
   5: </asp:ScriptManager>

 

   1: //C#
   2: ScriptReference sr = new ScriptReference("AppScripts.js");
   3: ScriptManager1.Scripts.Add(sr);

 

Tworzenie własnego Client Callbacks

W poprzedniej lekcji dowiedzieliśmy się co nieco o update panelach. Dzięki niemu możemy aktualizować jakiś fragment na stronie. Kontrolka ta tworzy proste wywołanie zwrotne. Ale może się zdarzyć, że będziemy potrzebować większej kontroli nad sposobem połączenia klienta z serwerem i vice versa. W tym wypadku pozostaje nam napisać własne asynchroniczne wywołanie zwrotne. Aby utworzyć stronę z własnym kodem callback musimy wykonać następujący standardowy zbiór instrukcji. Po pierwsze, musimy określić kod po stronie serwera. Aby to zrobić, należy wykonać następujące kroki:

  • zaimplementować System.Web.UI.ICallbackEventHandler na naszej stronie asp.net. Ten interfejs po stronie serwera pozawala zarówno odbierać jak i zwracać dane.
  • zaimplementować metodę RaiseCallbackEvent interfejsu ICallbackEventHandler. Metoda taj jest wywoływana przez klienta. Można go używać do otrzymywania wartości parametru od klienta.
  • zaimplementować metode GetCallbackResult interfejsu ICallbackEventHandler. Metoda ta wykorzystywana jest do zwrócenia wartości po przetwarzaniu przez serwer.

Gdy już mam kod po stronie serwera musimy teraz napisać stronę kliencką.

  • zaimplementować GetCallbackEventReference do przetwarzania wyników pochodzących z serwera.
  • wykorzystanie metody o której wczesniej była mowa RegisterClientScriptBlock
  • ostatnia potrzebna funkcja aby stworzyć własne wywołanie jest generowana przez środowisko asp.net przy wywoływaniu GetCallbackEventReference

Załóżmy, że mamy na stronie kontrolkę DropDownList. Chcemy połączyć się z serwerem podczas wyboru jakiegoś elementu z listy przez użytkownika ale nie chcemy przeładowywać całej strony. Następnie przetworzyć dane na serwerze i zwrócić wynik na stronę. No to do dzieła ;) Musimy na początku zaimplementować ICallbackEventHandler i RaiseCallbackEvent.

   1: public partial class Default2 :
   2: System.Web.UI.Page, System.Web.UI.ICallbackEventHandler

 

   1: //C#
   2: string _callbackArgs;
   3: public void RaiseCallbackEvent(string eventArgument)
   4: {
   5: _callbackArgs = eventArgument;
   6: }

 

W RaiseCallbackEvent przekażemy stringa który będzie po prostu nazwą oferty z naszej listy rozwijanej. Oraz możemy zaimplementować GetCallbackResult metodę która zwróci nam wybraną przez nas ofertę ;)

   1: //C#
   2: public string GetCallbackResult()
   3: {
   4: return _callbackArgs;
   5: }

 

Po stronie klienta musimy napisać skrypt, “zarejestrować” go i oczywiście zrobić odpowiedni szkielet strony dla naszych potrzeb:

   1: //C#
   2: protected void Page_Load(object sender, EventArgs e)
   3: {
   4: //register the name of the client-side function that will
   5: // be called by the server
   6: string callbackRef = Page.ClientScript.GetCallbackEventReference(
   7: this, "args", "ClientCallbackFunction", "");
   8: //define a function used by the client to call the server
   9: string callbackScript = "function MyServerCall(args)" +
  10: "{" + callbackRef + "; }";
  11: //register the client function with the page
  12: Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
  13: "MyServerCall", callbackScript, true);
  14: }
   1: <script type="text/javascript">
   1:  


   2: function ClientCallbackFunction(args)


   3: {


   4: LabelMessage.innerText = args;


   5: }

</script>
   2: <asp:DropDownList ID="DropDownListChoice"
   3: runat="server"
   4: OnChange="MyServerCall(DropDownListChoice.value)">
   5: <asp:ListItem>Choice 1</asp:ListItem>
   6: <asp:ListItem>Choice 2</asp:ListItem>
   7: <asp:ListItem>Choice 3</asp:ListItem>
   8: </asp:DropDownList>
   9: <br /><br />
  10: <asp:Label ID="LabelMessage" runat="server"></asp:Label>

 

Praca z biblioteką ASP.NET AJAX

Praca z javascript, brak obiektowość itd. nie jest proste szczególnie przy mocno rozbudowanym oprogramowaniu. MIcrosoft stara się tworzyć bibliotekę która obudowuje javascript przez co praca z nim staje sie przyjemniejsza. Wykorzystując asp.net ajax na stronie musimy pamiętać o dołączeniu na stronę ScriptManager który zarządza które pliki trzeba dołączyć itd. Bez dołączenia ScriptManagera bądź jawnego dodania go w inny sposób biblioteka ms ajax nie zadziała.

Przez takie obudowanie javascriptu przez Microsoft zyskujemy nowe możliwości, zalety. Jakie? Np. wspieranie obiektowości, tryb debugowania, pełna kompatybilność, klasy bazowe i wiele wiele innych. W training kit są opisane pokrótce te zalety oraz przestrzenie nazw z jakich możemy skorzystać i co się w nich znajduje…mowa tutaj m.in o Global, Sys, Sys.Net, Sys.UI…. Ja jednak chciałbym teraz chwile zatrzymać się na obiektowości.

Javascript ma wsparcie dla podstawowych klas, funkcji, typów danych, operatorów i tym podobne. Microsoft Ajax Library pozwala nam dodatkowo skorzystać z dobrodziejstw interfejsów, dziedziczenia, zdarzeń, czy nawet refleksji. Nasuwa się pytanie czy potrzebne są nam te wszystkie elementy.  Oczywiście nie zawsze ale przy rozbudowanej kontroli klienta, rozbudowanej aplikacji jak najbardziej.

Klasy (konstruktor, pola, właściwości i metody)

Składnią do utworzenia klasy z biblioteki jest PrzestrzeńNazwy.Klasa. Również można przypisać nazwę klasy do funkcji. Załóżmy, że chcemy dodać klasę do wcześniej określonej przestrzeni nazw Contoso.Utilities. Nazwijmy ją nazwą mówiącą samą za siebie ChangePasswordValidator. Oto kod:

   1: //define class name (as function), create constructor, and
   2: // set class-level field values
   3: Contoso.Utilities.ChangePasswordValidator =
   4: function(requirePasswordsNotMatch, requireNumber)
   5: {
   6: Contoso.Utilities.ChangePasswordValidator.initializeBase(this);
   7: this.RequirePasswordsNotMatch = requirePasswordsNotMatch;
   8: this.RequireNumber = requireNumber;
   9: this._passwordRuleViolations = new Array();
  10: }

Widzimy tutaj utworzoną klasę przy pomocy funkcji której parametry tworzą konstruktor. Po określeniu klasy i jej konstruktora wypadało by rozbudować ją o jakieś pola, właściwości czy metody.

Pola:

   1: //define class contents (fields, properties, methods)
   2: Contoso.Utilities.ChangePasswordValidator.prototype =
   3: {
   4: //declare fields
   5: RequirePasswordsNotMatch: Boolean,
   6: RequireNumber: Boolean,
   7: ...

 

Jeśli chodzi o właściwości do javascript nie wspiera sam w sobie takich właściwości o jakich myślimy. Ale nic nie stoi na przeszkodzie żebyśmy sami stworzyli metody których działanie imitowało by właściwości.

   1: //properties
   2: set_currentPassword: function(value)
   3: {
   4: this._currentPassword = value;
   5: },
   6: set_changeToPassword: function(value)
   7: {
   8: this._changeToPassword = value;
   9: },
  10: get_passwordRuleViolations: function()
  11: {
  12: return this._passwordRuleViolations;
  13: },

 

I przykład implementacji metod:

   1: //methods
   2: CheckPasswordStrength: function(password)
   3: {
   4: var strPass = new String(password.toString());
   5: if (strPass.length < 4)
   6: {
   7: return Contoso.Utilities.PasswordStrength.Weak;
   8: }
   9: else if (strPass.Length < 7)
  10: {
  11: return Contoso.Utilities.PasswordStrength.Medium;
  12: }
  13: else
  14: {
  15: return Contoso.Utilities.PasswordStrength.Strong;
  16: }
  17: },
  18: AllowPasswordChange: function()
  19: {
  20: var pass1 = new String(this._currentPassword);
  21: var pass2 = new String(this._changeToPassword);
  22: //use new, extended Array type
  23: var ruleViolations = new Array();
  24: //min length rule
  25: if (pass2.length < 5)
  26: {
  27: Array.add(ruleViolations, 'Password too short.');
  28: }
  29: //check if passwords match
  30: if (this.RequirePasswordsNotMatch)
  31: {
  32: if (pass1 == pass2)
  33: {
  34: Array.add(ruleViolations, 'Passwords cannot match.');
  35: }
  36: }
  37: //contains numbers
  38: if (this.RequireNumber)
  39: {
  40: if (pass2.match(/\d+/) == null)
  41: {
  42: Array.add(ruleViolations, 'Password must include a number.');
  43: }
  44: }
  45: //reset rule violations property
  46: this._passwordRuleViolations = ruleViolations;
  47: //determine if change allowed
  48: if (ruleViolations.length > 0)
  49: {
  50: return false;
  51: }
  52: else
  53: {
  54: return true;
  55: }
  56: }
  57: }

Jak użyć stworzonej przez nas przed chwilą klasy?

Dodajemy ScriptManager’a do naszej strony i ustawiamy referencje do naszego pliku .js

   1: <asp:ScriptManager ID="ScriptManager1" runat="server">
   2: <Scripts>
   3: <asp:ScriptReference path="ContosoUtilities.js" />
   4: </Scripts>
   5: </asp:ScriptManager>

A następnie wykorzystywać wszystkie poprzednio zdefiniowane metody:

 

   1: <script language="javascript" type="text/javascript">
   1:  


   2: //call constructor


   3: var validator =


   4: new Contoso.Utilities.ChangePasswordValidator(true, true, true);


   5: //check the password strength


   6: strength = validator.CheckPasswordStrength("password");


   7: switch (strength)


   8: {


   9: case Contoso.Utilities.PasswordStrength.Weak:


  10: alert("Weak");


  11: break;


  12: case Contoso.Utilities.PasswordStrength.Medium:


  13: alert("Medium");


  14: break;


  15: case Contoso.Utilities.PasswordStrength.Strong:


  16: alert("Strong");


  17: break;


  18: }


  19: //set properties


  20: validator.set_currentPassword("password");


  21: validator.set_changeToPassword("pas2");


  22: //call methods


  23: if (validator.AllowPasswordChange())


  24: {


  25: alert("Password may be changed");


  26: }


  27: else


  28: {


  29: var violations = validator.get_passwordRuleViolations();


  30: alert("Rule violations: " + violations.length);


  31: for (i = 0; i < violations.length; i++)


  32: {


  33: alert("Rule violation " + i + " = " + violations[i]);


  34: }


  35: }

</script>

Na koniec chciałbym jeszcze chwile się zatrzymać na cyklu strony. Znajomość zdarzeń zachodzących po kolei może się nam przydać aby je np. przechwycić i odpowiednio zareagować :) Na szczęści cykl życia klienckiej części kodu jest bardzo podobny do tej serwerowej. Dotyczy to load, unload, i disposing. Oczywiście aby z tego skorzystać potrzebujemy naszego znanego kolegi ScriptManager ;) Aby “zarejestrować” zdarzenie musimy użyć następującej składni:

   1: Sys.Application.add_load(PageLoad);
   2: function PageLoad(sender)
   3: {
   4: //page-load code goes here
   5: }

Biblioteka również pozwala nam wyrzucić nasza obsługę zdarzeń:

   1: Sys.Application.remove_load(PageLoad);

 

 

Oczywiście możemy korzystać z innych zdarzeń. Dla przykładu zdarzenia z klasy PageRequestManager z przestrzeni nazw Sys.WebForms. Klasa ta służy do częściowej aktualizacji strony i asynchronicznego wywołania PostBack. Obejmuje ona następujące zdarzenia:

  • initializeRequest – jeszcze przed rozpoczęciem asynchronicznego PostBack
  • beginRequest – kiedy PostBack jest wysyłany na serwer
  • pageLoading – podczas powrotu z serwera postback’a
  • pageLoaded – kiedy content został już odtworzony po postBack
  • endRequest – po zakończeniu PostBack

To tyle o czym chciałem powiedzieć. Chciałbym podkreślić, że w TK na koniec rozdziału jest np. pokazane jak stworzyć własny komponent ajaxowy, czy też stworzenie do nich pewnych zachowań. Warto to sobie doczytać w wolnej chwili. Życzę Wesołych Świat, czas trochę odpocząć ;) Do poniedziałku ;)

Tagi: , , , ,

70-562: Using Client-Side State Management

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Śledzenie stanu użytkownika, przechowywanie jego danych jest bardzo ważnym elementem w naszych aplikacjach. Nie jest to oczywiście problem tylko aplikacji webowych ale również “okienkowych” ;) My jednak dzisiaj skupimy się na rozwiązaniach dla pierwszego typu oprogramowania ;) A konkretnie omówimy sobie następujące mechanizmy: View state,  Hidden fields, Cookies oraz Query string.

Są dwa sposoby na zarządzanie informacjami. Wszystko możemy “zwalić” po stronie klienta albo przechowywać na serwerze. Podstawową różnice i ładnie to zobrazowuje poniższy rysunek:

1

Korzyści wynikające z przechowywania informacji po stornie klienta są następujące:

Lepsza skalowalność – kiedy informacje trzymamy na serwerze to każde, żądanie klienta obciąża pamięć serwera. Jeśli serwer ma jednocześnie obsłużyć setki użytkowników na raz, to staję się to wielkim ograniczeniem. Zrzucając to na klienta pozawalamy serwerowi obsłużyć więcej wniosków.

Obsługa wielu serwerów www – po stronie klienta żądanie może być obsłużone przez różne serwery. W tym przypadku klient zawiera informacje które są potrzebne dla serwerów aby obsłużyć to żądanie. Jeżeli byśmy nie chcieli korzystać z rozwiązania po stronie klienta to jeżeli klient chciałby się przełączyć pomiędzy serwerami w połowie sesji to ten “nowy serwer” nie musi mieć dostępu do naszych danych bo te są przecież zapisane na innym serwerze.

Natomiast trzymanie informacji po stronie serwera niesie za sobą następujące korzyści:

Większe/Lepsze bezpieczeństwo – informacje przechowywane u klienta mogą zostać przechwycone lub złośliwie zmodyfikowane. Dlatego nie poleca się trzymania po stronie klienta poufnych danych takich jak hasło, nadawania uprawnień czy statusu uwierzytelniania.

Zmniejszenie przepustowości – przechowywanie sporych informacji po stronie klienta i wysyłanie ich w tę i z powrotem może zwiększyć wykorzystanie pasma i czasu ładowania strony, potencjalnie zwiększyć koszt i ograniczyć skalowalność.

Wybór sposobu zarządzania i przechowywania informacjami, informacjami o stanie oczywiście należy do nas samych a powyższe argumenty powinny nam pomóc w wyborze. Bo wszystko jest sztuką kompromisu :) Również należy wziąć pod uwagę, że przedstawione powyżej sposoby nie są jedynymi i istnieje wybór tylko albo albo. Przejdźmy teraz do omawiania mechanizmów które pomagają nam w zarządzaniu stanem aplikacji i przechowywaniem informacji.

View State

View state jest domyślnym mechanizmem używanym w ASP.NET do przechowywania informacji pomiędzy kolejnymi żądaniami. Kiedy użytkownik wywołuje post back to wysyłany jest również view state. ASP.NET wykorzystuje view state do ponownego ustawienia wartości kontrolek. Najpierw jednak sprawdza czy któraś z tych wartości nie jest zmieniona w stosunku z przed post-back. Również załóżmy, że jest problem z obsłużeniem żądania na serwerze, wtedy trzeba zwrócić “pierwotne” wartości. W html-u stan kontrolek jest sprowadzony do ciągu znaków który wygląda następująco:

   1: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
   2: value="/wEPDwULLTEzNjkxMzkwNjRkZAVvqsMGC6PVDmbCxBlPkLVKNahk" />

Jak widać dane są zakodowane, skompresowane co zapewnia większe bezpieczeństwo niż same ukryte pola. Teraz należało by powiedzieć, jak zabezpieczyć view state, jak z nim pracować itd.

View State Security Considerations

Musimy mieć świadomość, że view state jest polem ukrytym ale jego “wstępnie” zakodowana wartość jest jak najbardziej do odtworzenia. Dlatego jeśli mają zostać przesłane jakieś poufne informacje należy użyć właściwości ViewStateEncryptionMode która zaszyfruje nam dane. Aby włączyć szyfrowanie dla całej witryny musimy w web.configu dodać następujący fragment:

   1: <configuration>
   2: <system.web>
   3: <pages viewStateEncryptionMode="Always"/>
   4: </system.web>
   5: </configuration>

Alternatywnie możemy zrobić to na poszczególnych stronach dodając:

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_
   2: Default" ViewStateEncryptionMode="Always"%>

 

 

Sam view state należy uznać za najbardziej bezpieczną metodę przechowywania i zarządzania danymi po stronie klienta. Samo szyfrowanie jest wystarczająco bezpieczne ale jednak jest to po stornie klienta…a co serwer to serwer ;)

Wyłączanie View State

Możemy wyłączyć view state. Pamiętajmy, że jest on domyślnie włączony a nie zawsze potrzebny. Zbyt wielka ilość przechowywanych danych powoduje spadek wydajności, dane są przesyłane tam i z powrotem pomiędzy przeglądarką a serwerem.  Stąd możemy wyłączyć na danej stornie view state ustawiając właściwość Control.EnableViewState na false.

Odczytywanie i zapisywanie danych do View State

Zdarza się, że chcemy zachować pomiędzy kolejnymi żądaniami niestandardowe dane. Tutaj po raz kolejny przychodzi z pomocą bezpieczny i w miarę wydajny view state. Odczyt i zapis takich danych jest bardzo prosty, spójrzmy na poniższy przykład:

   1: //C#
   2: //writing to view state
   3: this.ViewState.Add("MyData", "some data value");
   4: //read from view state
   5: string myData = (string)ViewState["MyData"];

 

Dane zapisane w view state nie są pamiętane przy każdym żądaniu. Dotyczą tylko pojedynczego post-backa danej strony. Dlatego mechanizm ten jest przydatny do przechowywania tymczasowych danych.

Nie jesteśmy również ograniczeni do przechowywania tylko ciągu znaków. W view state możemy również przechować obiekt. Przykład zachowania obiektu DateTime poniżej:

   1: //C#
   2: //check if ViewState object exists, and display it if it does
   3: if (ViewState["lastVisit"] != null)
   4: Label1.Text = ((DateTime)ViewState["lastVisit"]).ToString();
   5: else
   6: Label1.Text = "lastVisit ViewState not defined.";
   7: //define the ViewState object for the next page view
   8: ViewState["lastVisit"] = DateTime.Now;

 

Hidden fields

Jak wiem view state również jest ukrytym polem. Lecz tam wartość jest wstępnie szyfrowana. Tutaj ukryte pole tyczy się tylko tego, że nie widzimy bezpośrednio wartości na stornie ale jest ona ogólnie dostępna w kodzie źródłowym. Stąd klient może nią dowolnie manipulować. Pole to służy również tylko do przechowywania danych tymczasowych.

Cookies

Często potrzebujemy “śledzić” użytkownika, weryfikować dane od pierwszego wejścia po kolejne żądania, wejścia na stronę itd. Tutaj z pomocą przychodzą nam tzw. cookies czyli małe ilości danych które możemy sobie zapisać po stronie klienta. Mogą to być trwałe dane zapisane w pliku tekstowym. Dane te mają przetrwać po zamknięciu przeglądarki do ponownego jej otworzenia itp. Również cookies możemy zapisać w pamięci podręcznej przeglądarki ale dane te są tracone wraz z jej zamknięciem. Dla krótkiego zobrazowania sobie na jakiej zasadzie to działa wrzucam poniższy rysunek:

2

Ciasteczka są najbardziej elastycznym i niezawodnym sposobem na przechowywanie danych po stornie klienta. Ciasteczka ustawia się często na długo okres żywotności, jednak użytkownik może w każdym momencie sam je usunąć. A my tym samym tracimy wszystkie, być może dla nas ważne ustawienia ;)

Odczytywanie i zapis ciasteczek

Jest to zadania bardzo proste. Wywołujemy metodę Response.Cookies.Add w której przekazujemy obiekt HttpCookie który przechowuje kolekcje nazwa <—> dane. Coś na zasadzie słownika :)

   1: Response.Cookies.Add(New HttpCookie("userId", userId))

Odczyt takiej danej:

   1: Request.Cookies("userId").Value
Zapisywanie wielu wartości w cookie

Rozmiar pliku cookie zależy od przeglądarki ale maksymalnie może mieć 4 KB. Ponadto, zazwyczaj można zapisać do 20 plików cookie na stronę. Jeśli chcemy obejść ten 20 pliczków można zastosować sztuczkę dzięki której do jednej wartości przypiszemy wiele kluczy:

   1: //C#
   2: Response.Cookies["info"]["visit"].Value = DateTime.Now.ToString();
   3: Response.Cookies["info"]["firstName"].Value = "Tony";
   4: Response.Cookies["info"]["border"].Value = "blue";
   5: Response.Cookies["info"].Expires = DateTime.Now.AddDays(1);

Query Strings

Query strings są powszechnie stosowane do przechowywania wartości zmiennych które np. identyfikują dany kontekst strony. Query strings są zawsze dołączone na końcu linka począwszy od znaku zapytania ?, następnie jest nazwa zmiennej i po znaku równości = wartość. Możemy umieścić wiele zmiennych, oddzielamy je wtedy znakiem &. Przykład takich linków:

http://support.microsoft.com/Default.aspx?kbid=315233

http://search.microsoft.com/results.aspx?mkt=en-US&setlang=en-US&q=hello+world

Jest to prosty sposób na przekazanie wartości pomiędzy kolejnymi żądaniami ale wielkim ograniczeniem jest tutaj jawność przesyłanych danych. 

To tyle jeśli chodzi o dzisiejszy artykuł. Miłego weekendu.

Tagi: , , ,

70-562: Exploring specialized server controls

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Dzisiaj powiemy sobie o podstawowych kontrolkach (tytuł mógłby mylić :D ) których możemy użyć na naszej stronie. Dokładnie będą to: Literal, Table, TableRow, TableCell, Image, ImageButton, ImageMap, Calendar, FileUpload, Panel, MultiView, View, Wizard.

Kontrolka Literal

Kontrolka ta jest bardzo podobna do kontroli Label, ponieważ odpowiada za wyświetlanie statycznego tekstu na stronie. Nie dziedziczy ona po WebControl. Kontrolka ta nie wprowadza praktycznie żadnej dodatkowej funkcjonalności nawet nie jest żadnym znacznikiem html dzięki czemu m.in nie możemy do niej zastosować żadnego stylu. Spójrzmy na krótki przykład użycia:

   1: //C#
   2: protected void Page_Load(object sender, EventArgs e)
   3: {
   4: Literal1.Text =
   5: @"This is an <font size=7>example</font><script>alert(""Hi"");</script>";
   6: Literal2.Text =
   7: @"This is an <font size=7>example</font><script>alert(""Hi"");</script>";
   8: Literal3.Text =
   9: @"This is an <font size=7>example</font><script>alert(""Hi"");</script>";
  10: Literal1.Mode = LiteralMode.Encode;
  11: Literal2.Mode = LiteralMode.PassThrough;
  12: Literal3.Mode = LiteralMode.Transform;
  13: }

Widzimy tutaj, że kontrolka ta może zostać uruchomiana w różnych trybach np. Encode spowoduje, że treść jest wyświetlana ze wszystkimi znacznikami i skryptami czyli tak jak Bóg stworzył a już PassThrough spowoduje, że treść zostanie zinterpretowana przez przeglądarkę.

Kontrolki Table, TableRow i TableCell

Słynne tabelki używane pewnie od początku istnienia html możemy używać nie tylko do wyświetlania danych tabelarycznych ale również są pomocne przy układzie grafiki na stornie itd. Każdemu kto choć trochę zna html’a znane są znaczniki <table>, <tr> i <td>. W asp.net tworzymy i zarządzamy tabelami bez tych znaczników. Tutaj również możemy wyświetlać przy pomocy tabel, statyczne informacje na stronie ale prawdziwą zaletą w asp jest to, że możemy tworzyć dynamicznie tabele w runtime. Kiedy mamy zamiar tylko wyświetlać statyczne informacje rozważymy używanie klasycznego html’a. Oczywiście jeżeli tych danych mamy więcej, potrzebujemy mieć większej kontroli nad nimi to użyjemy do tego celu innych kontrolek typu Repeater czy GridView.

Jak dodać dynamicznie wiersze i kolumny do kontrolki Table? Służą oczywiście do tego TableCell i TableRow. Sam Visual Studio wspiera stworzenie tabeli poprzez “wyklikanie" ale co jeśli byśmy chcieli, żeby podczas ładowania strony stworzyła nam się tabelka. Spójrzmy na poniższy kod który mam nadzieje nie wymaga komentarza.

   1: //C#
   2: protected void Page_PreInit(object sender, EventArgs e)
   3: {
   4: Table1.BorderWidth = 1;
   5: for (int row = 0; row < 5; row++)
   6: {
   7: TableRow tr = new TableRow();
   8: for (int column = 0; column < 3; column++)
   9: {
  10: TableCell tc = new TableCell();
  11: tc.Text = string.Format("Row:{0} Cell:{1}", row, column);
  12: tc.BorderWidth = 1;
  13: tr.Cells.Add(tc);
  14: }
  15: Table1.Rows.Add(tr);
  16: }
  17: }

 

Kontrolka Image

Tutaj podobnie ja w tabelkach jeżeli na stornie potrzebujemy coś więcej niż jeden statyczny obrazek to zamiast znacznika <img> użyjemy kontrolki Image. Pozwala ona zarządzać danym obrazkiem lub obrazkami choć suma sumarum kontrolka ta zostanie zrenderowana jako znacznik <img>. Najważniejszą właściwością jej jest ImageUrl która wskazuje ścieżkę do pliku. Również wartą uwagi jest właściwość AlternateText która ustawia określony tekst zamiast obrazka bo np. przeglądarka nie obsługuje obrazków czy też właściwość ImageAlign która dostosowuje obraz w stosunku do innych elementów na stornie. Dodatkowo ustawiając właściwość DescriptionUrl ustawiamy w znaczniku atrybut longdesc a używając GenerateEmptyAlternateText wstawiamy atrybut alt=. Przykład wczytania obrazka podczas ładowania strony oraz ustawienie podstawowych właściwosci pokazuje poniższy kod:

   1: //C#
   2: protected void Page_Load(object sender, EventArgs e)
   3: {
   4: Image1.ImageUrl = "images/whale.jpg";
   5: Image1.DescriptionUrl = "WhaleImageDescription.htm";
   6: Image1.AlternateText = "This is a picture of a whale";
   7: }
Kontrolka ImageButton

Podstawowa różnicą jaka się od razu narzuca jest to, że Image w przeciwieństwie do ImageButton nie obsługuje zdarzenia Click. To daje nam oczywiście o wiele większe możliwości. Kontrolka ta dziedziczy bezpośrednio z Image i jest ona zrenderowana przez przeglądarkę do znacznika <input type=”image”>. Dziedzicząc po Image ma ona te same właściwości, które nakreśliłem poprzednio. Spójrzmy na praktyczny przykład wykorzystania tej kontrolki. Kod poniżej po kliknięciu w obrazek ustawia jego alternatywny tekst na współrzędne kliknięcia.

   1: //C#
   2: public partial class ImageButton_Control : System.Web.UI.Page
   3: {
   4: protected void Page_Load(object sender, EventArgs e)
   5: {
   6: ImageButton1.ImageUrl = "images/redblue.jpg";
   7: ImageButton1.AlternateText =
   8: "This is a button. The left side is red. The right is blue.";
   9: }
  10: protected void ImageButton1_Click(object sender, ImageClickEventArgs e)
  11: {
  12: ImageButton1.AlternateText =
  13: string.Format("Button Clicked at {0},{1}", e.X, e.Y);
  14: }
  15: }
Kontrolka ImageMap

Znamy już różnice podstawową pomiędzy kontrolkami Image a ImageButton. Co więc nowego wnosi nam kontrolka ImageMap w stosunku do ImageButton? To, że na obrazek dzielimy na pewne obszary (hot spoty) i w zależności gdzie klikniemy możemy dostać inną akcje. Kontrolka ta również dziedziczy z Image a co za tym idzie ma po niej to co najlepszy (czytaj wcześniej opisane właściwości :) ). W kodzie źródłowym jest ona zdefiniowana jako <asp:ImageMap> z zagnieżdżonymi hot spotami. 

Hot spoty mogą być zdefiniowane jako: CircleHotSpot, RectangleHotSpot, i Polygon-HotSpot. Sam hot spot ma również takie właściwości jak AlternateText, NavigateUrl itp. Dodatkowo ma również metodę HotSpotMode która definiuje zachowanie po kliknięci w dany “gorący obszar”. Jakie one mogą być? Np. PostBack, Navigate. Może być również tryb Inactive który oznacza, że nie ma być wykonane nic po kliknięciu w wybrany obszar.

Poniższy kod pokazuje jak w kodzie można wykorzystać tą kontrolkę i hot spoty. Jest na stornie Label i do niego po kliknięciu w dany obszar zostaje przypisany kolor prostokąta. Spójrzmy na kod i zrzut ze storny:

   1: //C#
   2: public partial class HotSpotControl : System.Web.UI.Page
   3: {
   4: protected void Page_Load(object sender, EventArgs e)
   5: {
   6: ImageMapStopLight.ImageUrl = "images/stoplight.jpg";
   7: ImageMapStopLight.AlternateText = "Stoplight picture";
   8: ImageMapStopLight.HotSpotMode = HotSpotMode.PostBack;
   9: RectangleHotSpot redHotSpot = new RectangleHotSpot();
  10: redHotSpot.Top = 0;
  11: redHotSpot.Bottom = 40;
  12: redHotSpot.Left = 0;
  13: redHotSpot.Right = 40;
  14: redHotSpot.PostBackValue = "RED";
  15: ImageMapStopLight.HotSpots.Add(redHotSpot);
  16: RectangleHotSpot yellowHotSpot = new RectangleHotSpot();
  17: yellowHotSpot.Top = 41;
  18: yellowHotSpot.Bottom = 80;
  19: yellowHotSpot.Left = 0;
  20: yellowHotSpot.Right = 40;
  21: yellowHotSpot.PostBackValue = "YELLOW";
  22: ImageMapStopLight.HotSpots.Add(yellowHotSpot);
  23: RectangleHotSpot greenHotSpot = new RectangleHotSpot();
  24: greenHotSpot.Top = 81;
  25: greenHotSpot.Bottom = 120;
  26: greenHotSpot.Left = 0;
  27: greenHotSpot.Right = 40;
  28: greenHotSpot.PostBackValue = "GREEN";
  29: ImageMapStopLight.HotSpots.Add(greenHotSpot);
  30: }
  31: protected void ImageMapStopLight_Click(object sender, ImageMapEventArgs e)
  32: {
  33: Label1.Text = "You clicked the " + e.PostBackValue + " rectangle.";
  34: }
  35: }

1

Kontrolka Calendar

Nazwa kontrolki mówi sama za siebie :) Umożliwia ona wyświetlenie na stornie web kalendarza. Pozwala ona wybierać jedną datę lub serie dat. Nie trzeba chyba mówić jak bardzo przydatna jest taka kontrolka. Jeśli chodzi o sprawy techniczne to dziedziczy on bezpośrednio z WebControl a w kodzie źródłowym zapisujemy ją <asp:Calendar>. Jednak w konsekwencji na stornie jeśli podejrzymy kod źródłowy zauważymy, że jest to najzwyklejsza w świecie tabelka (<table>) z wbudowanym javascript.

Kontrolka ta ma multum właściwości i nawet mi przez myśl nie przeszło, żeby je tutaj opisywać…w TK są dwie strony :) Poza tym celem artykułów jest nakreślenie tematyki a nie tłumaczenie TK ;) Chciałbym mimo wszystko powiedzieć o jednej właściwości z którą na pewno się spotkamy używając kalendarza a mianowicie SelectionMode. Określa ona czy możemy zaznaczyć jedną date, kilka a może w ogóle. Do wyboru mamy:

Day- pozwala wybrać tylko jeden dzień

DayWeek – pozwala wybrać dzień lub cały tydzień

DayWeekMonth – pozwala wybrać dzień, tydzień a nawet cały miesiąc

None – nie pozwala wybrać żadnej daty.

Poniżej obrazek pokazuje wszystkie właściwości a co za tym idzie możliwości tak wydawało by się skromnej kontrolki:

2

Poniżej przykład kodu z TK z użyciem kontrolki kalendarza:

   1: //C#
   2: public partial class CalendarCSharp : System.Web.UI.Page
   3: {
   4: Hashtable _scheduleData;
   5: protected void Page_Load(object sender, EventArgs e)
   6: {
   7: _scheduleData = GetSchedule();
   8: Calendar1.Caption = "Personal Schedule";
   9: Calendar1.FirstDayOfWeek = FirstDayOfWeek.Sunday;
  10: Calendar1.NextPrevFormat = NextPrevFormat.ShortMonth;
  11: Calendar1.TitleFormat = TitleFormat.MonthYear;
  12: Calendar1.ShowGridLines = true;
  13: Calendar1.DayStyle.HorizontalAlign = HorizontalAlign.Left;
  14: Calendar1.DayStyle.VerticalAlign = VerticalAlign.Top;
  15: Calendar1.DayStyle.Height = new Unit(75);
  16: Calendar1.DayStyle.Width = new Unit(100);
  17: Calendar1.OtherMonthDayStyle.BackColor = System.Drawing.Color.Cornsilk;
  18: Calendar1.TodaysDate = new DateTime(2009, 2, 1);
  19: Calendar1.VisibleDate = Calendar1.TodaysDate;
  20: }
  21: private Hashtable GetSchedule()
  22: {
  23: Hashtable schedule = new Hashtable();
  24: schedule["2/9/2009"] = "Vacation Day";
  25: schedule["2/18/2009"] = "Budget planning meeting @ 3:00pm";
  26: schedule["2/24/2009"] = "Dinner plans with friends @ 7:00pm";
  27: schedule["2/27/2009"] = "Travel Day";
  28: schedule["3/5/2009"] = "Conf call @ 1:00pm";
  29: schedule["3/10/2009"] = "Meet with art director for lunch";
  30: schedule["3/27/2009"] = "Vacation Day";
  31: return schedule;
  32: }
  33: protected void Calendar1_SelectionChanged(object sender, EventArgs e)
  34: {
  35: LabelAction.Text = "Selection changed to: "
  36: + Calendar1.SelectedDate.ToShortDateString();
  37: }
  38: protected void Calendar1_VisibleMonthChanged(object sender,
  39: MonthChangedEventArgs e)
  40: {
  41: LabelAction.Text = "Month changed to: " + e.NewDate.ToShortDateString();
  42: }
  43: protected void Calendar1_DayRender(object sender,
  44: DayRenderEventArgs e)
  45: {
  46: if (_scheduleData[e.Day.Date.ToShortDateString()] != null)
  47: {
  48: Literal lit = new Literal();
  49: lit.Text = "<br />";
  50: e.Cell.Controls.Add(lit);
  51: Label lbl = new Label();
  52: lbl.Text = (string)_scheduleData[e.Day.Date.ToShortDateString()];
  53: lbl.Font.Size = new FontUnit(FontSize.Small);
  54: e.Cell.Controls.Add(lbl);
  55: }
  56: }
  57: }
Kontrolka FileUpload

Kontrolka pozwala wysłać na serwer pojedynczy plik. Ścieżkę do pliku możemy wpisać ręcznie bądź wybrać ją z dysku klikając na przycisk Przeglądaj. Kontrolka ta dziedziczy z WebControl i na stornie jest dokładnie <input type="file">. Samo wybranie pliku nie wywołuje PostBacka, dopiero po wciśnięciu w przycisk wyślij (który sami musimy zdefiniować) plik zostaje umieszczony na serwerze. Możemy oczywiście ograniczyć rodzaj wysyłanych plików, jego wielkość itd. Dla przykładu maksymalny rozmiar pliku jest określony przez MaxRequestLength atrybutu httpRuntime w web.config.

Przykład kodu oraz zrzut ekranu zastosowania tej kontrolki, poniżej:

   1: //C#
   2: protected void Button1_Click(object sender, EventArgs e)
   3: {
   4: if (FileUpload1.HasFile)
   5: {
   6: Label1.Text = "File Length: "
   7: + FileUpload1.FileBytes.Length
   8: + "<br />"
   9: + "File Name: "
  10: + FileUpload1.FileName
  11: + "<br />"
  12: + "MIME Type: "
  13: + FileUpload1.PostedFile.ContentType;
  14: FileUpload1.SaveAs(
  15: MapPath("~/Uploads/" + FileUpload1.FileName));
  16: }
  17: else
  18: {
  19: Label1.Text = "No file received.";
  20: }
  21: }

3

Kontrolka Panel

Kontrolka ta służy do grupowania innych kontrolek. Czasami potrzebujemy taki kontener kontrolek, który chcemy żeby był np. widoczny w pewnych okolicznościach dla użytkownika. Technicznie kontrolka ta dziedziczy z WebControl a w kodzie źródłowym jest to element <asp:Panel>. Ostatecznie jednak w przeglądarce otrzymujemy diva :)

Poniżej przykład kodu z Panelem. Po kliknięciu w przycisk zmienia się stan widoczności kontrolki:

   1: <body>
   2: <form id="form1" runat="server">
   3: <div>
   4: <asp:Button ID="ButtonShowHide" runat="server" Text="Login: hide form"
   5: width="200" onclick="ButtonShowHide_Click"/>
   6: <asp:Panel ID="Panel1" runat="server" BackColor="Beige" Width="200">
   7: <asp:Label ID="Label1" runat="server" Text="User name: "></asp:Label>
   8: <br />
   9: <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
  10: <br />
  11: <asp:Label ID="Label2" runat="server" Text="Password: "></asp:Label>
  12: <br />
  13: <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
  14: <br />
  15: <asp:Button ID="ButtonLogin" runat="server" Text="Login" />
  16: </asp:Panel>
  17: </div>
  18: </form>
  19: </body>
   1: //C#
   2: protected void ButtonShowHide_Click(object sender, EventArgs e)
   3: {
   4: Panel1.Visible = !Panel1.Visible;
   5: if (Panel1.Visible)
   6: {
   7: ButtonShowHide.Text = "Login: hide form";
   8: }
   9: else
  10: {
  11: ButtonShowHide.Text = "Login: show form";
  12: }
  13: }

 

Kontrolki Multiview i View

Tak jak Panel kontrolka MultiView służy do grupowania kontrolek. Kontrolki View muszą znajdować się w Multiview. Poszczególne widoki możemy ukrywać kiedy korzystamy z innego co umożliwia nam np. zrobienie kreatora. Kontrolka ta dziedziczy z klasy Control w kodzie źródłowym jest reprezentowana jako <asp:MultiView> a View który musi być wewnątrz jako <asp:View>. W przeglądarce nie jest generowany jakiś specjalny odpowiednik html.  

Przykładowy kod wykorzystania kontrolek, strony z widokiem do niej oraz przykład ogólny strony gdzie możemy wykorzystać te kontrolki poniżej:

4

 

   1: //C#
   2: public partial class ViewControl : System.Web.UI.Page
   3: {
   4: protected void Page_Load(object sender, EventArgs e)
   5: {
   6: MultiView1.ActiveViewIndex = 0;
   7: }
   8: protected void Button_Command(object sender, CommandEventArgs e)
   9: {
  10: switch (e.CommandName)
  11: {
  12: case "Step1Next":
  13: MultiView1.ActiveViewIndex = 1;
  14: break;
  15: case "Step2Back":
  16: MultiView1.ActiveViewIndex = 0;
  17: break;
  18: case "Step2Next":
  19: MultiView1.ActiveViewIndex = 2;
  20: break;
  21: case "Step3Back":
  22: MultiView1.ActiveViewIndex = 1;
  23: break;
  24: case "Finish":
  25: //hide control from user to simulate save
  26: MultiView1.ActiveViewIndex = -1;
  27: break;
  28: }
  29: }
  30: }

 

5

Kontrolka Wizard

Jest to w pewnym stopniu odpowiednik kontrolki MultiView. Pozwala ona na stworzenie kreatora gdzie możemy w pewien logiczny sposób podzielić pewne fragmenty informacji, a na koniec zapisać wynik naszej pracy. Są wbudowane określenia które determinują jakie przyciski mają być widoczne na poszczególnym kroku.

WizardStepType.Auto – jest to domyślne ustawienie które generują przycisk w oparciu o lokalizacje w kolekcji

WizardStepType.Complete – jest to ostatni krok do wyświetlenia nie generujący przycisków

WizardStepType.Finish – generuje przycisk Finish i Previous

WizardStepType.Start – jest to wyjściowy krok i generuje przycisk Next

WizardStepType.Step – generuje przyciski Next i Previous

Przykład kodu i wyglądu poszczególnych kroków poniżej:

6

   1: //C#
   2: public partial class WizardCSharp : System.Web.UI.Page
   3: {
   4: protected void Page_Load(object sender, EventArgs e)
   5: {
   6: if (!IsPostBack)
   7: {
   8: Wizard1.ActiveStepIndex = 0;
   9: }
  10: }
  11: protected void Wizard1_FinishButtonClick(object sender,
  12: WizardNavigationEventArgs e)
  13: {
  14: Wizard1.Visible = false;
  15: Response.Write("Finished<br />" + Label1.Text);
  16: }
  17: protected void Wizard1_NextButtonClick(object sender,
  18: WizardNavigationEventArgs e)
  19: {
  20: if (Wizard1.WizardSteps[e.NextStepIndex].Title == "Summary")
  21: {
  22: Label1.Text = String.Empty;
  23: foreach (WizardStep ws in Wizard1.WizardSteps)
  24: {
  25: foreach (Control c in ws.Controls)
  26: {
  27: if (c is CheckBox)
  28: {
  29: CheckBox cb = (CheckBox)c;
  30: if (cb.Checked)
  31: {
  32: Label1.Text += cb.Text + "<br />";
  33: }
  34: }
  35: }
  36: }
  37: }
  38: }
  39: }

 

Kontrolka Xml

Kontrolka ta jest wykorzystywana do wyświetlania danych które mamy zapisane w formacie XML. Szczególnie jest przydatna kiedy chcemy dokonać transformacji na Extensible Stylesheet Language (XSL).

Przykłady poniżej pokazują jak wygląda XML, wygenerowany XSL i jak to prosto zrealizować w kodzie:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <ProductList>
   3: <Product Id="1A59B" Department="Sporting Goods" Name="Baseball" Price="3.00" />
   4: <Product Id="9B25T" Department="Sporting Goods" Name="Tennis Racket" Price="40.00" />
   5: <Product Id="3H13R" Department="Sporting Goods" Name="Golf Clubs" Price="179.00" />
   6: <Product Id="7D67A" Department="Clothing" Name="Shirt" Price="12.00" />
   7: <Product Id="4T21N" Department="Clothing" Name="Jacket" Price="45.00" />
   8: </ProductList>

 

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <xsl:stylesheet version="1.0"
   3: xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   4: xmlns:msxsl="urn:schemas-microsoft-com:xslt"
   5: xmlns:labs="http://labs.com/mynamespace">
   6: <xsl:template match="/">
   7: <html>
   8: <head>
   9: <title>Product List</title>
  10: </head>
  11: <body>
  12: <center>
  13: <h1>Product List</h1>
  14: <xsl:call-template name="CreateHeading"/>
  15: </center>
  16: </body>
  17: </html>
  18: </xsl:template>
  19: <xsl:template name="CreateHeading">
  20: <table border="1" cellpadding="5">
  21: <tr >
  22: <th bgcolor="yellow">
  23: <font size="4" >
  24: <b>Id</b>
  25: </font>
  26: </th>
  27: <th bgcolor="yellow">
  28: <font size="4" >
  29: <b>Department</b>
  30: </font>
  31: </th>
  32: <th bgcolor="yellow">
  33: <font size="4" >
  34: <b>Name</b>
  35: </font>
  36: </th>
  37: <th bgcolor="yellow">
  38: <font size="4" >
  39: <b>Price</b>
  40: </font>
  41: </th>
  42: </tr>
  43: <xsl:call-template name="CreateTable"/>
  44: </table>
  45: </xsl:template>
  46: <xsl:template name="CreateTable">
  47: <xsl:for-each select="/ProductList/Product">
  48: <tr>
  49: <td align="center">
  50: <xsl:value-of select="@Id"/>
  51: </td>
  52: <td align="center">
  53: <xsl:value-of select="@Department"/>
  54: </td>
  55: <td>
  56: <xsl:value-of select="@Name"/>
  57: </td>
  58: <td align="right">
  59: <xsl:value-of select="format-number(@Price,'$#,##0.00')"/>
  60: </td>
  61: </tr>
  62: </xsl:for-each>
  63: </xsl:template>
  64: </xsl:stylesheet>

 

   1: //C#
   2: public partial class XmlControlVb : System.Web.UI.Page
   3: {
   4: protected void Page_Load(object sender, EventArgs e)
   5: {
   6: Xml1.DocumentSource = "App_Data/ProductList.xml";
   7: Xml1.TransformSource = "App_Data/ProductList.xsl";
   8: }
   9: }

 

7

Dzięki za uwagę, do poniedziałku!!!

Tagi: , , ,