Formatowanie elementów DataList i Repeater na podstawie danych (C#)

Autor : Scott Mitchell

Pobierz plik PDF

W tym samouczku omówimy przykłady formatowania wyglądu kontrolek DataList i Repeater przy użyciu funkcji formatowania w szablonach lub obsługi zdarzenia DataBound.

Wprowadzenie

Jak pokazano w poprzednim samouczku, lista DataList oferuje wiele właściwości związanych ze stylem, które wpływają na jej wygląd. W szczególności pokazano, jak przypisać domyślne klasy CSS do właściwości DataList s HeaderStyle, ItemStyle, AlternatingItemStylei SelectedItemStyle . Oprócz tych czterech właściwości lista DataList zawiera wiele innych właściwości związanych ze stylem, takich jak Font, ForeColor, BackColori BorderWidth, aby wymienić kilka. Kontrolka Repeater nie zawiera żadnych właściwości związanych ze stylem. Wszelkie takie ustawienia stylu należy wprowadzić bezpośrednio w znacznikach w szablonach repeaterów.

Często jednak sposób formatowania danych zależy od samych danych. Na przykład podczas wyświetlania produktów możemy chcieć wyświetlić informacje o produkcie w jasnoszarym kolorze czcionki, jeśli nie zostanie przerwana, lub możemy zaznaczyć UnitsInStock wartość, jeśli jest to zero. Jak pokazano w poprzednich samouczkach, kontrolki GridView, DetailsView i FormView oferują dwa różne sposoby formatowania ich wyglądu na podstawie danych:

  • Zdarzenie DataBound tworzy procedurę obsługi zdarzeń dla odpowiedniego DataBound zdarzenia, które jest uruchamiane po powiązaniu danych z każdym elementem (dla kontrolki GridView było RowDataBound to zdarzenie; dla elementu DataList i Repeater jest to ItemDataBound zdarzenie). W tej procedurze obsługi zdarzeń tylko powiązane dane można zbadać i podjąć decyzje dotyczące formatowania. Przeanalizowaliśmy tę technikę w samouczku Custom Formatting Based Upon Data (Formatowanie niestandardowe oparte na danych ).
  • Funkcje formatowania w szablonach w przypadku używania elementów TemplateFields w kontrolkach DetailsView lub GridView albo szablonu w kontrolce FormView możemy dodać funkcję formatowania do klasy kodowej strony ASP.NET, warstwy logiki biznesowej lub dowolnej innej biblioteki klas dostępnej z poziomu aplikacji internetowej. Ta funkcja formatowania może akceptować dowolną liczbę parametrów wejściowych, ale musi zwrócić kod HTML do renderowania w szablonie. Funkcje formatowania zostały po raz pierwszy zbadane w samouczku Using TemplateFields w kontrolce GridView .

Obie te techniki formatowania są dostępne za pomocą kontrolek DataList i Repeater. W tym samouczku omówimy przykłady przy użyciu obu technik dla obu kontrolek.

Korzystanie z programu obsługi zdarzeńItemDataBound

Gdy dane są powiązane z elementem DataList, z kontrolki źródła danych lub przez programowe przypisywanie danych do właściwości kontrolki DataSource i wywoływanie jej DataBind() metody, wyzwalane jest zdarzenie DataList DataBinding , wyliczone źródło danych i każdy rekord danych jest powiązany z listą DataList. Dla każdego rekordu w źródle danych element DataList tworzy DataListItem obiekt, który jest następnie powiązany z bieżącym rekordem. Podczas tego procesu lista DataList zgłasza dwa zdarzenia:

  • ItemCreateduruchamia się po utworzeniu DataListItem
  • ItemDataBound jest uruchamiany po powiązaniu bieżącego rekordu z DataListItem

W poniższych krokach opisano proces powiązania danych dla kontrolki DataList.

  1. Zdarzenie DataList jest DataBinding wyzwalane

  2. Dane są powiązane z listą danych

    Dla każdego rekordu w źródle danych

    1. Tworzenie DataListItem obiektu
    2. Wyzwol zdarzenieItemCreated
    3. Wiązanie rekordu z rekordem DataListItem
    4. Wyzwol zdarzenieItemDataBound
    5. Dodawanie elementu DataListItem do kolekcji Items

Po powiązaniu danych z kontrolką Repeater przechodzi ona przez dokładnie tę samą sekwencję kroków. Jedyną różnicą jest to, że zamiast DataListItem tworzonych wystąpień, repeater używa RepeaterItems.

Uwaga

Czytelnik może zauważyć niewielką anomalię między sekwencją kroków, które występują, gdy element DataList i Repeater są powiązane z danymi, a gdy element GridView jest powiązany z danymi. Na końcu procesu powiązania danych element GridView zgłasza DataBound zdarzenie, jednak kontrolka DataList ani Repeater nie ma takiego zdarzenia. Jest to spowodowane tym, że kontrolki DataList i Repeater zostały utworzone z powrotem w przedziale czasu ASP.NET 1.x, zanim wzorzec procedury obsługi zdarzeń przed i po poziomie stał się powszechny.

Podobnie jak w przypadku kontrolki GridView, jedną z opcji formatowania na podstawie danych jest utworzenie programu obsługi zdarzeń dla ItemDataBound zdarzenia. Ta procedura obsługi zdarzeń sprawdzi dane, które zostały właśnie powiązane z DataListItem kontrolką lub RepeaterItem , i wpłynie na formatowanie kontrolki zgodnie z potrzebami.

W przypadku kontrolki DataList zmiany formatowania dla całego elementu można zaimplementować przy użyciu DataListItem właściwości związanych ze stylem s, które obejmują standard Font, , ForeColorBackColor, CssClassi tak dalej. Aby wpłynąć na formatowanie określonych kontrolek sieci Web w szablonie dataList, musimy programowo uzyskać dostęp i zmodyfikować styl tych kontrolek sieci Web. Zobaczyliśmy, jak to zrobić z powrotem w samouczku Custom Formatting Based Upon Data (Formatowanie niestandardowe oparte na danych ). Podobnie jak kontrolka RepeaterItem Repeater, klasa nie ma właściwości związanych ze stylem, dlatego wszystkie zmiany związane ze stylem RepeaterItem wprowadzone w ItemDataBound programie obsługi zdarzeń muszą być wykonywane programowo przez programowe uzyskiwanie dostępu do kontrolek sieci Web i aktualizowanie ich w szablonie.

ItemDataBound Ponieważ technika formatowania elementów DataList i Repeater jest praktycznie identyczna, nasz przykład koncentruje się na korzystaniu z elementu DataList.

Krok 1. Wyświetlanie informacji o produkcie na liście danych

Zanim zaczniemy martwić się o formatowanie, najpierw utwórzmy stronę, która używa elementu DataList do wyświetlania informacji o produkcie. W poprzednim samouczku utworzyliśmy element DataList, którego ItemTemplate każda nazwa produktu, kategoria, dostawca, ilość na jednostkę i cena. Powtórzmy tę funkcję w tym samouczku. W tym celu można utworzyć ponownie obiekt DataList i jego obiektDataSource od podstaw lub skopiować te kontrolki ze strony utworzonej w poprzednim samouczku (Basics.aspx) i wkleić je do strony na potrzeby tego samouczka (Formatting.aspx).

Po zreplikacji funkcji DataList i ObjectDataSource z Basics.aspx do Formatting.aspxpliku poświęć chwilę, aby zmienić właściwość s ID DataList z DataList1 na bardziej opisową ItemDataBoundFormattingExample. Następnie wyświetl element DataList w przeglądarce. Jak pokazano na rysunku 1, jedyną różnicą formatowania między poszczególnymi produktami jest to, że kolor tła zmienia się.

Produkty są wyświetlane w kontrolce DataList

Rysunek 1. Produkty są wyświetlane w kontrolce DataList (kliknij, aby wyświetlić obraz pełnowymiarowy)

W tym samouczku sformatujmy element DataList tak, aby wszystkie produkty o cenie niższej niż 20,00 USD miały zarówno nazwę, jak i cenę jednostkową wyróżnioną kolorem żółtym.

Krok 2. Programowe określanie wartości danych w programie obsługi zdarzeń ItemDataBound

Ponieważ tylko te produkty z ceną poniżej 20,00 USD będą miały zastosowane niestandardowe formatowanie, musimy mieć możliwość określenia ceny każdego produktu. Podczas wiązania danych z elementem DataList lista DataList wylicza rekordy w źródle danych, a dla każdego rekordu tworzy DataListItem wystąpienie, wiążąc rekord źródła danych z rekordem DataListItem. Po powiązaniu danych określonego rekordu z bieżącym DataListItem obiektem zostanie wyzwolone zdarzenie datalist ItemDataBound . Możemy utworzyć procedurę obsługi zdarzeń dla tego zdarzenia, aby sprawdzić wartości danych dla bieżącej DataListItem wartości i, na podstawie tych wartości, wprowadzić wszelkie niezbędne zmiany formatowania.

ItemDataBound Utwórz zdarzenie dla elementu DataList i dodaj następujący kod:

protected void ItemDataBoundFormattingExample_ItemDataBound
    (object sender, DataListItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item ||
        e.Item.ItemType == ListItemType.AlternatingItem)
    {
        // Programmatically reference the ProductsRow instance bound
        // to this DataListItem
        Northwind.ProductsRow product =
            (Northwind.ProductsRow)((System.Data.DataRowView)e.Item.DataItem).Row;
        // See if the UnitPrice is not NULL and less than $20.00
        if (!product.IsUnitPriceNull() && product.UnitPrice < 20)
        {
            // TODO: Highlight the product's name and price
        }
    }
}

Chociaż koncepcja i semantyka obsługi zdarzeń usługi DataList ItemDataBound są takie same jak te, które są używane przez program obsługi zdarzeń GridView RowDataBound w samouczku Custom Formatting Based On Data (Niestandardowe formatowanie oparte na danych ), składnia różni się nieco. ItemDataBound Gdy zdarzenie jest uruchamiane, DataListItem po prostu powiązane dane są przekazywane do odpowiedniej procedury obsługi zdarzeń za pośrednictwem e.Item (zamiast e.Row, podobnie jak w przypadku programu obsługi zdarzeń GridViewRowDataBound). Procedura obsługi zdarzeń elementu DataList jest ItemDataBound uruchamiana dla każdego wiersza dodanego do listy DataList, w tym wierszy nagłówka, wierszy stopki i wierszy separatora. Jednak informacje o produkcie są powiązane tylko z wierszami danych. W związku z tym w przypadku korzystania ze ItemDataBound zdarzenia w celu sprawdzenia danych powiązanych z listą DataList musimy najpierw upewnić się, że pracujemy z elementem danych. Można to zrobić, sprawdzając DataListItem właściwość sItemType, która może mieć jedną z następujących ośmiu wartości:

  • AlternatingItem
  • EditItem
  • Footer
  • Header
  • Item
  • Pager
  • SelectedItem
  • Separator

ItemAlternatingItem``DataListItem Elementy danych dataList i są makijażem elementów danych. Zakładając, że pracujemy z elementem Item lub AlternatingItem, uzyskujemy dostęp do rzeczywistego ProductsRow wystąpienia powiązanego z bieżącym DataListItemelementem . Właściwość DataListItem s DataItem zawiera odwołanie do DataRowView obiektu, którego Row właściwość zawiera odwołanie do rzeczywistego ProductsRow obiektu.

Następnie sprawdzimy ProductsRow właściwość wystąpienia UnitPrice . Ponieważ pole Tabeli UnitPrice Products zezwala na NULL wartości, przed podjęciem próby uzyskania dostępu UnitPrice do właściwości należy najpierw sprawdzić, czy ma NULL wartość przy użyciu IsUnitPriceNull() metody . Jeśli wartość nie NULLjest wartością UnitPrice , sprawdzamy, czy jest mniejsza niż 20,00 USD. Jeśli jest rzeczywiście poniżej 20,00 USD, musimy zastosować formatowanie niestandardowe.

Krok 3. Wyróżnianie nazwy produktu i ceny

Gdy wiemy, że cena produktu jest mniejsza niż $20.00, wszystko, co pozostaje, to podkreślić jego nazwę i cenę. Aby to osiągnąć, musimy najpierw programowo odwołać się do kontrolek Etykieta w ItemTemplate tym, które wyświetlają nazwę i cenę produktu. Następnie musimy wyświetlić żółte tło. Te informacje o formatowaniu można stosować bezpośrednio, modyfikując właściwości Etykiety BackColor (LabelID.BackColor = Color.Yellow); jednak w idealnym przypadku wszystkie kwestie związane z wyświetlaniem powinny być wyrażane za pomocą kaskadowych arkuszy stylów. W rzeczywistości mamy już arkusz stylów, który zapewnia żądane formatowanie zdefiniowane w Styles.css - AffordablePriceEmphasisprogramie , który został utworzony i omówiony w samouczku Niestandardowe formatowanie na podstawie danych .

Aby zastosować formatowanie, wystarczy ustawić dwie właściwości kontrolek CssClass sieci Web etykiet na AffordablePriceEmphasis, jak pokazano w poniższym kodzie:

// Highlight the product name and unit price Labels
// First, get a reference to the two Label Web controls
Label ProductNameLabel = (Label)e.Item.FindControl("ProductNameLabel");
Label UnitPriceLabel = (Label)e.Item.FindControl("UnitPriceLabel");
// Next, set their CssClass properties
if (ProductNameLabel != null)
    ProductNameLabel.CssClass = "AffordablePriceEmphasis";
if (UnitPriceLabel != null)
    UnitPriceLabel.CssClass = "AffordablePriceEmphasis";

Po zakończeniu ItemDataBound obsługi zdarzeń ponownie przejdź do Formatting.aspx strony w przeglądarce. Jak pokazano na rysunku 2, te produkty o cenie poniżej 20,00 USD mają wyróżnioną zarówno nazwę, jak i cenę.

Te produkty mniejsze niż 20,00 USD są wyróżnione

Rysunek 2. Te produkty mniejsze niż 20,00 USD są wyróżnione (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Uwaga

Ponieważ element DataList jest renderowany jako html <table>, jego DataListItem wystąpienia mają właściwości związane ze stylem, które można ustawić w celu zastosowania określonego stylu do całego elementu. Jeśli na przykład chcemy wyróżnić cały element żółty, gdy jego cena była mniejsza niż 20,00 USD, możemy zastąpić kod, który odwołuje się do etykiet i ustawić ich CssClass właściwości następującym wierszem kodu: e.Item.CssClass = "AffordablePriceEmphasis" (zobacz Rysunek 3).

S RepeaterItem , które tworzą kontrolkę Repeater, jednak nie oferują takich właściwości na poziomie stylu. W związku z tym zastosowanie niestandardowego formatowania do repeatera wymaga zastosowania właściwości stylu do kontrolek sieci Web w szablonach repeater, podobnie jak na rysunku 2.

Cały element produktu jest wyróżniony dla produktów poniżej 20,00 USD

Rysunek 3. Cała pozycja produktu jest wyróżniona dla produktów poniżej 20,00 USD (kliknij, aby wyświetlić obraz pełnowymiarowy)

Używanie funkcji formatowania z szablonu

W samouczku Używanie pól szablonów w kontrolce GridView pokazano, jak za pomocą funkcji formatowania w kontrolce GridView TemplateField zastosować formatowanie niestandardowe na podstawie danych powiązanych z wierszami kontrolki GridView. Funkcja formatowania to metoda, którą można wywołać z szablonu i zwraca kod HTML, który ma być emitowany w jego miejscu. Funkcje formatowania mogą znajdować się w klasie ASP.NET s page-behind lub mogą być scentralizowane w plikach klas w App_Code folderze lub w oddzielnym projekcie biblioteki klas. Przeniesienie funkcji formatowania z klasy kodu za ASP.NET strony jest idealne, jeśli planujesz używać tej samej funkcji formatowania na wielu stronach ASP.NET lub w innych ASP.NET aplikacjach internetowych.

Aby zademonstrować funkcje formatowania, podać informacje o produkcie zawierają tekst [DISCONTINUED] obok nazwy produktu, jeśli nie zostanie przerwany. Ponadto zwróćmy uwagę na żółtą cenę, jeśli jest mniejsza niż 20,00 USD (tak jak w przykładzie ItemDataBound obsługi zdarzeń); jeśli cena wynosi 20,00 USD lub wyższa, nie pokazujmy rzeczywistej ceny, ale zamiast tego tekst: Proszę wywołać ofertę cenową. Rysunek 4 przedstawia zrzut ekranu przedstawiający listę produktów z zastosowanymi regułami formatowania.

Zrzut ekranu przedstawiający produkty wymienione w kontrolce DataList z ceną produktów kosztujących ponad 20,00 USD zastąpione tekstem

Rysunek 4. W przypadku drogich produktów cena jest zastępowana tekstem, wywołaj ofertę cenową (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Krok 1. Tworzenie funkcji formatowania

W tym przykładzie potrzebujemy dwóch funkcji formatowania: jednej, która wyświetla nazwę produktu wraz z tekstem [DISCONTINUED], w razie potrzeby, i inną, która wyświetla wyróżnioną cenę, jeśli jest mniejsza niż 20,00 USD, lub tekst: Wywołaj ofertę cenową. Utwórzmy te funkcje w klasie ASP.NET s code-behind i nazwijmy je DisplayProductNameAndDiscontinuedStatus i DisplayPrice. Obie metody muszą zwrócić kod HTML do renderowania jako ciąg, a oba muszą zostać oznaczone Protected (lub Public), aby można je było wywołać z części składni deklaratywnej strony ASP.NET. Kod dla tych dwóch metod jest następujący:

protected string DisplayProductNameAndDiscontinuedStatus
    (string productName, bool discontinued)
{
    // Return just the productName if discontinued is false
    if (!discontinued)
        return productName;
    else
        // otherwise, return the productName appended with the text "[DISCONTINUED]"
        return string.Concat(productName, " [DISCONTINUED]");
}
protected string DisplayPrice(Northwind.ProductsRow product)
{
    // If price is less than $20.00, return the price, highlighted
    if (!product.IsUnitPriceNull() && product.UnitPrice < 20)
        return string.Concat("<span class=\"AffordablePriceEmphasis\">",
                              product.UnitPrice.ToString("C"), "</span>");
    else
        // Otherwise return the text, "Please call for a price quote"
        return "<span>Please call for a price quote</span>";
}

Należy pamiętać, że DisplayProductNameAndDiscontinuedStatus metoda akceptuje wartości productName pól danych i discontinued jako wartości skalarne, natomiast DisplayPrice metoda akceptuje ProductsRow wystąpienie (a nie wartość skalarną unitPrice ). Jedno z podejść będzie działać; Jeśli jednak funkcja formatowania działa z wartościami skalarnymi, które mogą zawierać wartości bazy danych NULL (takie jak UnitPrice; ani ProductName nie Discontinued zezwalaj na NULL wartości), należy zachować szczególną ostrożność w obsłudze tych danych wejściowych skalarnych.

W szczególności parametr wejściowy musi być typu Object , ponieważ wartość przychodząca może być wystąpieniem DBNull zamiast oczekiwanego typu danych. Ponadto należy sprawdzić, czy wartość przychodząca jest wartością bazy danych NULL . Oznacza to, że jeśli chcemy DisplayPrice , aby metoda akceptowała cenę jako wartość skalarną, musielibyśmy użyć następującego kodu:

protected string DisplayPrice(object unitPrice)
{
    // If price is less than $20.00, return the price, highlighted
    if (!Convert.IsDBNull(unitPrice) && ((decimal) unitPrice) < 20)
        return string.Concat("<span class=\"AffordablePriceEmphasis\">",
                              ((decimal) unitPrice).ToString("C"), "</span>");
    else
        // Otherwise return the text, "Please call for a price quote"
        return "<span>Please call for a price quote</span>";
}

Należy pamiętać, że unitPrice parametr wejściowy jest typu Object i że instrukcja warunkowa została zmodyfikowana w celu ustalenia, czy jest, czy unitPrice nie DBNull . Ponadto, ponieważ unitPrice parametr wejściowy jest przekazywany jako Object, musi zostać rzutowany na wartość dziesiętną.

Krok 2. Wywoływanie funkcji formatowania z elementu ItemTemplate elementu datalist

Dzięki funkcjom formatowania dodanym do klasy kodowej strony ASP.NET, wystarczy wywołać te funkcje formatowania z tabeli DataList s ItemTemplate. Aby wywołać funkcję formatowania z szablonu, umieść wywołanie funkcji w składni powiązania danych:

<%# MethodName(inputParameter1, inputParameter2, ...) %>

W kontrolce Etykieta internetowa kontrolka ItemTemplateProductNameLabel DataList wyświetla obecnie nazwę produktu, przypisując jej Text właściwość wynik .<%# Eval("ProductName") %> Aby w razie potrzeby wyświetlić nazwę plus tekst [DISCONTINUED], zaktualizuj składnię deklaratywną, aby zamiast tego przypisywać Text właściwość wartość DisplayProductNameAndDiscontinuedStatus metody. W tym celu musimy przekazać nazwę produktu i wycofać wartości przy użyciu Eval("columnName") składni . Eval Zwraca wartość typu Object, ale DisplayProductNameAndDiscontinuedStatus metoda oczekuje parametrów wejściowych typu String i Boolean, dlatego musimy rzutować wartości zwracane przez Eval metodę do oczekiwanych typów parametrów wejściowych, w następujący sposób:

<h4>
    <asp:Label ID="ProductNameLabel" runat="server"
        Text='<%# DisplayProductNameAndDiscontinuedStatus((string) Eval("ProductName"),
              (bool) Eval("Discontinued")) %>'>
    </asp:Label>
</h4>

Aby wyświetlić cenę, możemy po prostu ustawić UnitPriceLabel właściwość Label Text na wartość zwróconą przez DisplayPrice metodę, podobnie jak w przypadku wyświetlania nazwy produktu i tekstu [DISCONTINUED]. Jednak zamiast przekazywać parametr UnitPrice jako parametr wejściowy skalarny, zamiast tego przekazujemy w całym ProductsRow wystąpieniu:

<asp:Label ID="UnitPriceLabel" runat="server"
    Text='<%# DisplayPrice((Northwind.ProductsRow)
          ((System.Data.DataRowView) Container.DataItem).Row) %>'>
</asp:Label>

Po uruchomieniu wywołań funkcji formatowania poświęć chwilę, aby wyświetlić postęp w przeglądarce. Ekran powinien wyglądać podobnie do rysunku 5, a wycofane produkty, w tym tekst [DISCONTINUED] i te produkty kosztują ponad 20,00 USD po ich cenie zastąpionej tekstem Proszę wezwać do oferty cenowej .

Zrzut ekranu przedstawiający produkty wymienione w kontrolce DataList z ceną produktów kosztujących ponad 20,00 USD zastąpione tekstem

Rysunek 5. W przypadku drogich produktów cena jest zastępowana tekstem, wywołaj ofertę cenową (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Podsumowanie

Formatowanie zawartości kontrolki DataList lub Repeater na podstawie danych można wykonać przy użyciu dwóch technik. Pierwszą techniką jest utworzenie procedury obsługi zdarzeń dla ItemDataBound zdarzenia, która jest uruchamiana, ponieważ każdy rekord w źródle danych jest powiązany z nowym DataListItem lub RepeaterItem. W procedurze obsługi zdarzeń ItemDataBound można zbadać dane bieżącego elementu, a następnie formatowanie można zastosować do zawartości szablonu lub, dla DataListItem s, do całego elementu.

Alternatywnie formatowanie niestandardowe można zrealizować za pomocą funkcji formatowania. Funkcja formatowania to metoda, którą można wywołać z szablonów DataList lub Repeater, które zwracają kod HTML w celu emitowania w jego miejscu. Często kod HTML zwracany przez funkcję formatowania jest określany przez wartości powiązane z bieżącym elementem. Te wartości można przekazać do funkcji formatowania jako wartości skalarne lub przekazując cały obiekt powiązany z elementem (na przykład ProductsRow wystąpienie).

Szczęśliwe programowanie!

Informacje o autorze

Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można do niego dotrzeć pod adresem mitchell@4GuysFromRolla.com. Lub za pośrednictwem swojego bloga, który można znaleźć na stronie http://ScottOnWriting.NET.

Specjalne podziękowania

Ta seria samouczków została przejrzyona przez wielu przydatnych recenzentów. Głównymi recenzentami tego samouczka byli Yaakov Ellis, Randy Schmidt i Liz Shulok. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresemmitchell@4GuysFromRolla.com .