Udostępnij za pośrednictwem


Implementowanie interfejsu użytkownika typu lista/szczegóły przy użyciu kontrolerów i widoków

autor: Microsoft

Pobierz plik PDF

Jest to krok 4 bezpłatnego samouczka aplikacji "NerdDinner" , który zawiera instrukcje dotyczące tworzenia małej, ale kompletnej aplikacji internetowej przy użyciu ASP.NET MVC 1.

Krok 4 pokazuje, jak dodać kontroler do aplikacji, która korzysta z naszego modelu, aby zapewnić użytkownikom środowisko nawigacji listy danych/szczegółów na kolacje w naszej witrynie NerdDinner.

Jeśli używasz ASP.NET MVC 3, zalecamy skorzystanie z samouczków Wprowadzenie With MVC 3 lub MVC Music Store.

NerdDinner — krok 4: kontrolery i widoki

W przypadku tradycyjnych struktur internetowych (klasycznych platform ASP, PHP, ASP.NET Web Forms itp.) przychodzące adresy URL są zwykle mapowane na pliki na dysku. Na przykład: żądanie adresu URL, takiego jak "/Products.aspx" lub "/Products.php", może zostać przetworzone przez plik "Products.aspx" lub "Products.php".

Internetowe struktury MVC mapować adresy URL na kod serwera w nieco inny sposób. Zamiast mapować przychodzące adresy URL na pliki, zamiast mapować adresy URL na metody w klasach. Te klasy są nazywane "kontrolerami" i są odpowiedzialne za przetwarzanie przychodzących żądań HTTP, obsługę danych wejściowych użytkownika, pobieranie i zapisywanie danych oraz określanie odpowiedzi na potrzeby wysyłania z powrotem do klienta (wyświetlanie kodu HTML, pobieranie pliku, przekierowywanie do innego adresu URL itp.).

Teraz, gdy utworzyliśmy podstawowy model dla naszej aplikacji NerdDinner, następnym krokiem będzie dodanie kontrolera do aplikacji, która z niej korzysta, aby zapewnić użytkownikom środowisko nawigacji po listach danych/szczegółach dla kolacji w naszej witrynie.

Dodawanie kontrolera DinnersController

Zaczniemy od kliknięcia prawym przyciskiem myszy folderu "Controllers" w naszym projekcie internetowym, a następnie wybrania polecenia menu Dodaj kontroler> (można również wykonać to polecenie, wpisując Ctrl-M, Ctrl-C):

Zrzut ekranu przedstawiający okno Eksplorator rozwiązań z folderem Controllers oraz wyróżnionymi na niebiesko elementami menu Dodaj i Kontroler.

Spowoduje to wyświetlenie okna dialogowego "Dodawanie kontrolera":

Zrzut ekranu przedstawiający okno dialogowe Dodawanie kontrolera z polem Nazwa kontrolera wypełnione tekstem Kontroler kolacji.

Nadamy nowej kontrolerowi nazwę "DinnersController" i klikniemy przycisk "Dodaj". Program Visual Studio doda plik DinnersController.cs w katalogu \Controllers:

Zrzut ekranu przedstawiający okno Eksplorator rozwiązań z wyróżnionym plikiem Dinner Controllers dot c s na niebiesko.

Otworzy również nową klasę DinnersController w edytorze kodu.

Dodawanie metod akcji Index() i Details() do klasy DinnersController

Chcemy umożliwić odwiedzającym korzystanie z naszej aplikacji w celu przeglądania listy nadchodzących kolacji i umożliwienia im kliknięcia dowolnej kolacji na liście, aby wyświetlić szczegółowe informacje o niej. W tym celu opublikujemy następujące adresy URL z naszej aplikacji:

Adres URL Cel
/Kolacje/ Wyświetlanie listy HTML nadchodzących kolacji
/Dinners/Details/[id] Wyświetl szczegółowe informacje o określonej kolacji wskazanej przez parametr "id" osadzony w adresie URL — który będzie zgodny z identyfikatorem DinnerID kolacji w bazie danych. Na przykład: /Dinners/Details/2 wyświetli stronę HTML ze szczegółami dotyczącymi kolacji, której wartość DinnerID wynosi 2.

Opublikujemy początkowe implementacje tych adresów URL, dodając dwie publiczne "metody akcji" do klasy DinnersController, jak poniżej:

public class DinnersController : Controller {

    //
    // HTTP-GET: /Dinners/

    public void Index() {
        Response.Write("<h1>Coming Soon: Dinners</h1>");
    }

    //
    // HTTP-GET: /Dinners/Details/2

    public void Details(int id) {
        Response.Write("<h1>Details DinnerID: " + id + "</h1>");
    }
}

Następnie uruchomimy aplikację NerdDinner i użyjemy przeglądarki, aby je wywołać. Wpisanie w adresie URL "/Dinners/" spowoduje uruchomienie metody Index() i wyśle z powrotem następującą odpowiedź:

Zrzut ekranu przedstawiający okno odpowiedzi wygenerowane na podstawie uruchamiania aplikacji NerdDinner z tekstem Coming Soon: Dinners (Nadchodzące wkrótce: kolacje).

Wpisanie adresu URL "/Dinners/Details/2" spowoduje uruchomienie metody Details() i wysłanie z powrotem następującej odpowiedzi:

Zrzut ekranu przedstawiający okno odpowiedzi wygenerowane podczas uruchamiania aplikacji NerdDinner z tekstem Szczegóły kolacji I D: 2.

Być może zastanawiasz się — jak ASP.NET MVC wiedział, aby utworzyć naszą klasę DinnersController i wywołać te metody? Aby zrozumieć, że przyjrzyjmy się, jak działa routing.

Opis routingu MVC ASP.NET

ASP.NET MVC zawiera zaawansowany aparat routingu adresów URL, który zapewnia dużą elastyczność w kontrolowaniu sposobu mapowania adresów URL na klasy kontrolerów. Pozwala nam to całkowicie dostosować sposób, w jaki ASP.NET MVC wybiera klasę kontrolera do utworzenia, którą metodę wywołać na niej, a także skonfigurować różne sposoby automatycznego analizowania zmiennych z adresu URL/ciągu zapytania i przekazywania do metody jako argumentów parametrów. Zapewnia elastyczność, aby całkowicie zoptymalizować witrynę pod kątem optymalizacji aparatu wyszukiwania (optymalizacja aparatu wyszukiwania), a także opublikować dowolną strukturę adresów URL z poziomu aplikacji.

Domyślnie nowe projekty ASP.NET MVC są dostarczane ze wstępnie skonfigurowanym zestawem reguł routingu adresów URL, które zostały już zarejestrowane. Dzięki temu możemy łatwo rozpocząć pracę z aplikacją bez konieczności jawnego konfigurowania niczego. Domyślne rejestracje reguł routingu można znaleźć w klasie "Application" naszych projektów — którą można otworzyć, klikając dwukrotnie plik "Global.asax" w katalogu głównym naszego projektu:

Zrzut ekranu przedstawiający okno Eksplorator rozwiązań z wyróżnioną kropką globalną a x w kolorze niebieskim i okręgowym na czerwono.

Domyślne reguły routingu MVC ASP.NET są rejestrowane w metodzie "RegisterRoutes" tej klasy:

public void RegisterRoutes(RouteCollection routes) {

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",                                       // Route name
        "{controller}/{action}/{id}",                    // URL w/ params
        new { controller="Home", action="Index",id="" }  // Param defaults
    );
}

"trasy. Wywołanie metody MapRoute()" powyżej rejestruje domyślną regułę routingu, która mapuje przychodzące adresy URL na klasy kontrolera przy użyciu formatu ADRESU URL: "/{controller}/{action}/{id}" — gdzie "kontroler" jest nazwą klasy kontrolera, aby utworzyć wystąpienie klasy kontrolera, "action" jest nazwą metody publicznej do wywołania na niej, a "id" jest opcjonalnym parametrem osadzonym w adresie URL, który można przekazać jako argument do metody. Trzeci parametr przekazany do wywołania metody "MapRoute()" jest zestawem wartości domyślnych do użycia dla wartości kontrolera/akcji/identyfikatora w przypadku, gdy nie są obecne w adresie URL (Controller = "Home", Action="Index", Id="").

Poniżej znajduje się tabela, która pokazuje, jak różne adresy URL są mapowane przy użyciu domyślnej reguły trasy "/{controllers}/{action}/{id}"route:

Adres URL Klasa kontrolera Action, metoda Przekazane parametry
/Dinners/Details/2 DinnersController Details(id) id=2
/Dinners/Edit/5 DinnersController Edit(id) id=5
/Dinners/Create DinnersController Create() Nie dotyczy
/Kolacje DinnersController Index() Nie dotyczy
/W: strona główna HomeController Index() Nie dotyczy
/ HomeController Index() Nie dotyczy

W ostatnich trzech wierszach są wyświetlane wartości domyślne (Controller = Home, Action = Index, Id = "") używane. Ponieważ metoda "Index" jest zarejestrowana jako domyślna nazwa akcji, jeśli jej nie określono, adresy URL "/Dinners" i "/Home" powodują wywołanie metody akcji Index() w klasach kontrolera. Ponieważ kontroler "Home" jest zarejestrowany jako kontroler domyślny, jeśli nie został określony, adres URL "/" powoduje utworzenie HomeController i metodę akcji Index() na nim do wywołania.

Jeśli nie podobają Ci się te domyślne reguły routingu adresów URL, dobrą wiadomością jest to, że można je łatwo zmienić — po prostu edytuj je w metodzie RegisterRoutes powyżej. Jednak w przypadku naszej aplikacji NerdDinner nie zmienimy żadnych domyślnych reguł routingu adresów URL — zamiast tego użyjemy ich tak, jak jest.

Korzystanie z repozytorium DinnerRepository z elementu DinnersController

Teraz zastąpmy naszą bieżącą implementację metod akcji Index() i Details() dinnersController za pomocą implementacji, które używają naszego modelu.

Użyjemy utworzonej wcześniej klasy DinnerRepository, aby zaimplementować zachowanie. Zaczniemy od dodania instrukcji "using", która odwołuje się do przestrzeni nazw "NerdDinner.Models", a następnie zadeklarujemy wystąpienie naszego repozytorium DinnerRepository jako pole w klasie DinnerController.

W dalszej części tego rozdziału przedstawimy koncepcję "Wstrzykiwanie zależności" i pokażemy innym sposobem, aby kontrolery mogli uzyskać odwołanie do repozytorium DinnerRepository, które umożliwia lepsze testowanie jednostkowe — ale na razie utworzymy tylko wystąpienie naszego repozytorium kolacji w tekście, jak pokazano poniżej.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NerdDinner.Models;

namespace NerdDinner.Controllers {

    public class DinnersController : Controller {

        DinnerRepository dinnerRepository = new DinnerRepository();

        //
        // GET: /Dinners/

        public void Index() {
            var dinners = dinnerRepository.FindUpcomingDinners().ToList();
        }

        //
        // GET: /Dinners/Details/2

        public void Details(int id) {
            Dinner dinner = dinnerRepository.GetDinner(id);
        }
    }
}

Teraz możemy wygenerować odpowiedź HTML z powrotem przy użyciu pobranych obiektów modelu danych.

Używanie widoków z naszym kontrolerem

Chociaż istnieje możliwość napisania kodu w naszych metodach akcji w celu zebrania kodu HTML, a następnie użycia metody pomocnika Response.Write() do wysłania go z powrotem do klienta, takie podejście staje się dość nieporęcznie szybkie. Znacznie lepszym rozwiązaniem jest wykonanie tylko aplikacji i logiki danych wewnątrz metod akcji DinnersController, a następnie przekazanie danych potrzebnych do renderowania odpowiedzi HTML na oddzielny szablon "view", który jest odpowiedzialny za wyprowadzenie reprezentacji HTML. Jak zobaczymy za chwilę, szablon "view" jest plikiem tekstowym, który zazwyczaj zawiera kombinację znaczników HTML i osadzonego kodu renderowania.

Oddzielenie logiki kontrolera od renderowania widoku przynosi kilka dużych korzyści. W szczególności pomaga wymusić wyraźne "rozdzielenie problemów" między kodem aplikacji i kodem formatowania/renderowania interfejsu użytkownika. Ułatwia to testowanie logiki aplikacji jednostkowej w izolacji od logiki renderowania interfejsu użytkownika. Ułatwia to późniejsze modyfikowanie szablonów renderowania interfejsu użytkownika bez konieczności wprowadzania zmian w kodzie aplikacji. Dzięki niej deweloperzy i projektanci mogą łatwiej współpracować nad projektami.

Możemy zaktualizować klasę DinnersController, aby wskazać, że chcemy użyć szablonu widoku do wysłania odpowiedzi interfejsu użytkownika HTML, zmieniając podpisy metody naszych dwóch metod akcji z zwracania typu "void", aby zamiast tego miał zwracany typ "ActionResult". Następnie możemy wywołać metodę pomocnika View() w klasie bazowej Kontroler, aby zwrócić obiekt "ViewResult", jak pokazano poniżej:

public class DinnersController : Controller {

    DinnerRepository dinnerRepository = new DinnerRepository();

    //
    // GET: /Dinners/

    public ActionResult Index() {

        var dinners = dinnerRepository.FindUpcomingDinners().ToList();

        return View("Index", dinners);
    }

    //
    // GET: /Dinners/Details/2

    public ActionResult Details(int id) {

        Dinner dinner = dinnerRepository.GetDinner(id);

        if (dinner == null)
            return View("NotFound");
        else
            return View("Details", dinner);
    }
}

Podpis metody pomocnika View(), która jest używana powyżej, wygląda następująco:

Zrzut ekranu przedstawiający metodę pomocnika Widok z tekstem Wyświetl widok wyników (nazwa widoku ciągu, model obiektu).

Pierwszy parametr metody pomocnika View() to nazwa pliku szablonu widoku, którego chcemy użyć do renderowania odpowiedzi HTML. Drugi parametr jest obiektem modelu zawierającym dane wymagane przez szablon widoku w celu renderowania odpowiedzi HTML.

W naszej metodzie akcji Index() wywołujemy metodę pomocnika View() i wskazujemy, że chcemy renderować listę HTML kolacji przy użyciu szablonu widoku "Indeks". Przekazujemy szablon widoku sekwencję obiektów Dinner w celu wygenerowania listy z:

//
    // GET: /Dinners/

    public ActionResult Index() {
    
        var dinners = dinnerRepository.FindUpcomingDinners().ToList();
        
        return View("Index", dinners);
    }

W naszej metodzie akcji Details() próbujemy pobrać obiekt Dinner przy użyciu identyfikatora podanego w adresie URL. Jeśli zostanie znaleziona prawidłowa kolacja, wywołamy metodę pomocnika View(), wskazując, że chcemy użyć szablonu widoku "Szczegóły" do renderowania pobranego obiektu Obiad. Jeśli zażądano nieprawidłowej kolacji, zostanie wyświetlony pomocny komunikat o błędzie wskazujący, że kolacja nie istnieje przy użyciu szablonu widoku "NotFound" (i przeciążonej wersji metody pomocnika View(), która po prostu przyjmuje nazwę szablonu):

//
    // GET: /Dinners/Details/2

    public ActionResult Details(int id) {

        Dinner dinner = dinnerRepository.FindDinner(id);

        if (dinner == null)
            return View("NotFound");
        else
            return View("Details", dinner);
    }

Teraz zaimplementujmy szablony widoków "NotFound", "Details" i "Index".

Implementowanie szablonu widoku "NotFound"

Zaczniemy od zaimplementowania szablonu widoku "NotFound" — który wyświetla przyjazny komunikat o błędzie wskazujący, że nie można odnaleźć żądanej kolacji.

Utworzymy nowy szablon widoku, ustawiając kursor tekstu w metodzie akcji kontrolera, a następnie kliknij prawym przyciskiem myszy i wybierz polecenie menu "Dodaj widok" (możemy również wykonać to polecenie, wpisując Ctrl-M, Ctrl-V):

Zrzut ekranu przedstawiający projekt z wyróżnionym prawym przyciskiem myszy elementem menu Dodaj widok w kolorze niebieskim i okręgowym na czerwono.

Spowoduje to wyświetlenie okna dialogowego "Dodaj widok", jak pokazano poniżej. Domyślnie okno dialogowe wstępnie wypełni nazwę widoku do utworzenia w celu dopasowania nazwy metody akcji kursor był w momencie uruchomienia okna dialogowego (w tym przypadku "Szczegóły"). Ponieważ chcemy najpierw zaimplementować szablon "NotFound", zastąpimy tę nazwę widoku i ustawimy ją na wartość "NotFound":

Zrzut ekranu przedstawiający okno Dodawanie widoku z polem Nazwa widoku ustawioną na Nie znaleziono, pole wyboru Wybierz stronę wzorcową oraz pole identyfikatora miejsca zawartości ustawione na zawartość główną.

Po kliknięciu przycisku "Dodaj" program Visual Studio utworzy nowy szablon widoku "NotFound.aspx" dla nas w katalogu "\Views\Dinners" (który również zostanie utworzony, jeśli katalog jeszcze nie istnieje):

Zrzut ekranu przedstawiający hierarchię folderów okna Eksplorator rozwiązań z wyróżnionym plikiem s p x na niebiesko.

Zostanie również otwarty nasz nowy szablon widoku "NotFound.aspx" w edytorze kodu:

Zrzut ekranu przedstawiający okno edytora kodu z otwartą kropką s p x w edytorze kodu.

Szablony wyświetlania domyślnie mają dwa "regiony zawartości", w których można dodawać zawartość i kod. Pierwszy umożliwia dostosowanie "tytułu" strony HTML wysłanej z powrotem. Drugi umożliwia dostosowanie "głównej zawartości" strony HTML wysłanej z powrotem.

Aby zaimplementować szablon widoku "NotFound", dodamy podstawową zawartość:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Dinner Not Found
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Dinner Not Found</h2>

    <p>Sorry - but the dinner you requested doesn't exist or was deleted.</p>

</asp:Content>

Następnie możemy wypróbować go w przeglądarce. W tym celu zażądajmy adresu URL "/Dinners/Details/9999". Spowoduje to odwołanie do kolacji, która nie istnieje obecnie w bazie danych, i spowoduje, że nasza metoda akcji DinnersController.Details() renderuje nasz szablon widoku "NotFound":

Zrzut ekranu przedstawiający okno My MVC Application (Moja aplikacja MVC) z napisem / Dinners / Details / 9999 U R L w polu adresu w kolorze czerwonym.

Jedną z rzeczy, które zauważysz na powyższym zrzucie ekranu, jest to, że nasz podstawowy szablon widoku odziedziczył kilka kodu HTML otaczającego główną zawartość na ekranie. Dzieje się tak, ponieważ nasz szablon widoku używa szablonu "strona wzorcowa", który umożliwia stosowanie spójnego układu we wszystkich widokach w witrynie. Omówimy sposób działania stron wzorcowych w dalszej części tego samouczka.

Implementowanie szablonu widoku "Szczegóły"

Teraz zaimplementujmy szablon widoku "Szczegóły", który wygeneruje kod HTML dla pojedynczego modelu kolacji.

W tym celu umieścimy kursor tekstu w metodzie akcji Szczegóły, a następnie kliknij prawym przyciskiem myszy i wybierz polecenie menu "Dodaj widok" (lub naciśnij klawisze Ctrl-M, Ctrl-V):

Zrzut ekranu przedstawiający okno edytora kodu z wyróżnionym na czerwono elementem menu Dodaj kropkę kropki w prawym przyciskiem myszy.

Spowoduje to wyświetlenie okna dialogowego "Dodawanie widoku". Zachowamy domyślną nazwę widoku ("Szczegóły"). W oknie dialogowym wybierzemy również pole wyboru "Utwórz silnie typizowany widok" i wybierzemy (przy użyciu listy rozwijanej pola kombi) nazwę typu modelu, który przekazujemy z kontrolera do widoku. W tym widoku przekazujemy obiekt Dinner (w pełni kwalifikowana nazwa tego typu to: "NerdDinner.Models.Dinner"):

Zrzut ekranu przedstawiający okno Dodawanie widoku z listą rozwijaną Wyświetl zawartość ustawioną na Szczegóły i Klasą danych Widok ustawioną na Nerd Dinner dot Models dot Dinner (Modele) kropką Dinner (Kolacja).

W przeciwieństwie do poprzedniego szablonu, w którym postanowiliśmy utworzyć widok "Pusty widok", tym razem wybierzemy automatyczne "szkielet" widoku przy użyciu szablonu "Szczegóły". Możemy to wskazać, zmieniając listę rozwijaną "Wyświetl zawartość" w oknie dialogowym powyżej.

"Tworzenie szkieletu" spowoduje wygenerowanie początkowej implementacji szablonu widoku szczegółów na podstawie obiektu Dinner, który przekazujemy do niego. Dzięki temu możemy szybko rozpocząć pracę nad implementacją szablonu widoku.

Po kliknięciu przycisku "Dodaj" program Visual Studio utworzy nowy plik szablonu "Details.aspx" dla nas w katalogu "\Views\Dinners":

Zrzut ekranu przedstawiający okno Eksplorator rozwiązań z hierarchią folderów z folderem Dinners wyróżnionym kolorem niebieskim.

Spowoduje to również otwarcie nowego szablonu widoku "Details.aspx" w edytorze kodu. Będzie zawierać początkową implementację szkieletu widoku szczegółów na podstawie modelu kolacji. Aparat tworzenia szkieletów używa odbicia platformy .NET, aby przyjrzeć się właściwościom publicznym uwidocznionym w przekazanej klasie i doda odpowiednią zawartość na podstawie każdego znalezionego typu:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Details
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Details</h2>

    <fieldset>
        <legend>Fields</legend>
        <p>
            DinnerID:
            <%=Html.Encode(Model.DinnerID) %>
        </p>
        <p>
            Title:
            <%=Html.Encode(Model.Title) %>
        </p>
        <p>
            EventDate:
            <%= Html.Encode(String.Format("{0:g}", Model.EventDate)) %>
        </p>
        <p>
            Description:
            <%=Html.Encode(Model.Description) %>
        </p>
        <p>
            HostedBy:
            <%=Html.Encode(Model.HostedBy) %>
        </p>
        <p>
            ContactPhone:
            <%=Html.Encode(Model.ContactPhone) %>
        </p>
        <p>
            Address:
            <%=Html.Encode(Model.Address) %>
        </p>
        <p>
            Country:
            <%=Html.Encode(Model.Country) %>
        </p>
        <p>
            Latitude:
            <%= Html.Encode(String.Format("{0:F}",Model.Latitude)) %>
        </p>
        <p>
            Longitude:
            <%= Html.Encode(String.Format("{0:F}",Model.Longitude)) %>
        </p>
    </fieldset>
    
    <p>
        <%=Html.ActionLink("Edit","Edit", new { id=Model.DinnerID }) %>|
        <%=Html.ActionLink("Back to List", "Index") %>
    </p>
    
</asp:Content>

Możemy zażądać adresu URL "/Dinners/Details/1" , aby zobaczyć, jak wygląda implementacja szkieletu "details" w przeglądarce. Użycie tego adresu URL spowoduje wyświetlenie jednej z kolacji, które ręcznie dodaliśmy do bazy danych podczas tworzenia:

Zrzut ekranu przedstawiający okno odpowiedzi aplikacji z informacją / Kolacje / Szczegóły / 1 U R L w kolorze czerwonym w polu adresu.

Spowoduje to szybkie uruchomienie i zapewni nam wstępną implementację widoku Details.aspx. Następnie możemy przejść i dostosować go, aby dostosować interfejs użytkownika do naszej satysfakcji.

Gdy przyjrzymy się bliżej szablonowi Details.aspx, przekonamy się, że zawiera on statyczny kod HTML, a także osadzony kod renderowania. <% %> nuggets kodu wykonuje kod podczas renderowania szablonu widoku, a <%= %> nuggets kodu wykonują zawarty w nich kod, a następnie renderuj wynik do strumienia wyjściowego szablonu.

Możemy napisać kod w naszym widoku, który uzyskuje dostęp do obiektu modelu "Kolacja", który został przekazany z kontrolera przy użyciu silnie typizowanej właściwości "Model". Program Visual Studio zapewnia pełną funkcję IntelliSense kodu podczas uzyskiwania dostępu do tej właściwości "Model" w edytorze:

Zrzut ekranu przedstawiający okno edytora kodu z listą rozwijaną z wyróżnionym elementem Opis na niebiesko.

Wprowadźmy pewne poprawki, aby źródło naszego końcowego szablonu widoku Szczegóły wyglądało następująco:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Dinner: <%=Html.Encode(Model.Title) %>
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2><%=Html.Encode(Model.Title) %></h2>
    <p>
        <strong>When:</strong> 
        <%=Model.EventDate.ToShortDateString() %> 

        <strong>@</strong>
        <%=Model.EventDate.ToShortTimeString() %>
    </p>
    <p>
        <strong>Where:</strong> 
        <%=Html.Encode(Model.Address) %>,
        <%=Html.Encode(Model.Country) %>
    </p>
     <p>
        <strong>Description:</strong> 
        <%=Html.Encode(Model.Description) %>
    </p>       
    <p>
        <strong>Organizer:</strong> 
        <%=Html.Encode(Model.HostedBy) %>
        (<%=Html.Encode(Model.ContactPhone) %>)
    </p>
    
    <%= Html.ActionLink("Edit Dinner", "Edit", new { id=Model.DinnerID })%> |
    <%= Html.ActionLink("Delete Dinner","Delete", new { id=Model.DinnerID})%>   
     
</asp:Content>

Gdy ponownie uzyskamy dostęp do adresu URL "/Dinners/Details/1" , będzie on teraz renderowany w następujący sposób:

Zrzut ekranu przedstawiający okno odpowiedzi aplikacji przedstawiające nową stylizację widoku dot NET Futures.

Implementowanie szablonu widoku "Indeks"

Teraz zaimplementujmy szablon widoku "Indeks", który wygeneruje listę nadchodzących kolacji. Aby to zrobić, umieść kursor tekstu w metodzie akcji Indeks, a następnie kliknij prawym przyciskiem myszy i wybierz polecenie menu "Dodaj widok" (lub naciśnij klawisze Ctrl-M, Ctrl-V).

W oknie dialogowym "Dodaj widok" zachowamy szablon widoku o nazwie "Indeks" i zaznaczymy pole wyboru "Utwórz widok silnie typizowane". Tym razem wybierzemy opcję automatycznego wygenerowania szablonu widoku "Lista", a następnie wybierzemy pozycję "NerdDinner.Models.Dinner" jako typ modelu przekazany do widoku (co oznacza, że wskazujemy, że tworzymy szkielet "Lista" spowoduje przekazanie sekwencji obiektów kolacji z kontrolera do widoku):

Zrzut ekranu przedstawiający okno Dodawanie widoku z wartością Nazwa widoku ustawioną na Indeks, pole Utwórz widok silnie typizowane i zaznaczono pole Wybierz stronę wzorcową.

Po kliknięciu przycisku "Dodaj" program Visual Studio utworzy nowy plik szablonu "Index.aspx" dla nas w katalogu "\Views\Dinners". Będzie ona "szkieletem" początkowej implementacji, która udostępnia listę tabel HTML przekazanych do widoku.

Gdy uruchomimy aplikację i uzyskamy dostęp do adresu URL "/Dinners/" , spowoduje to renderowanie listy kolacji w następujący sposób:

Zrzut ekranu przedstawiający okno odpowiedzi aplikacji z listą kolacji w układzie siatki po aktualizacji Dodaj widok.

Powyższe rozwiązanie tabeli daje nam układ danych kolacji przypominający siatkę — co nie jest do końca potrzebne dla naszej oferty obiadowej dla konsumentów. Możemy zaktualizować szablon widoku Index.aspx i zmodyfikować go w celu wyświetlenia mniejszej liczby kolumn danych, a następnie użyć <elementu ul> do renderowania ich zamiast tabeli przy użyciu poniższego kodu:

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Upcoming Dinners</h2>

    <ul>
        <% foreach (var dinner in Model) { %>
        
            <li>                 
                <%=Html.Encode(dinner.Title) %>            
                on 
                <%=Html.Encode(dinner.EventDate.ToShortDateString())%>
                @
                <%=Html.Encode(dinner.EventDate.ToShortTimeString())%>
            </li>
            
        <% } %>
    </ul>
    
</asp:Content>

Używamy słowa kluczowego "var" w powyższej instrukcji foreach, ponieważ przeprowadzamy pętlę na każdej kolacji w modelu. Osoby nieznane w języku C# 3.0 mogą myśleć, że użycie "var" oznacza, że obiekt kolacji jest opóźniony. Oznacza to, że kompilator używa wnioskowania typu względem silnie typizowanej właściwości "Model" (czyli typu "IEnumerable<Dinner>") i kompilowania lokalnej zmiennej "dinner" jako typu kolacji — co oznacza, że uzyskujemy pełną funkcję IntelliSense i sprawdzanie czasu kompilacji dla niej w blokach kodu:

Zrzut ekranu przedstawiający okno edytora kodu z menu rozwijanego z wyróżnionym elementem listy Adres w szarym polu kropkowym.

Po naciśnięciu przycisku odświeżenia w adresie URL /Dinners w przeglądarce nasz zaktualizowany widok wygląda teraz następująco:

Zrzut ekranu przedstawiający okno odpowiedzi aplikacji z listą nadchodzących kolacji po odświeżeniu polecenia.

Wygląda to lepiej - ale jeszcze nie jest. Ostatnim krokiem jest umożliwienie użytkownikom końcowym klikania poszczególnych kolacji na liście i wyświetlania szczegółowych informacji o nich. Zaimplementujemy to przez renderowanie elementów hiperlinku HTML, które łączą się z metodą akcji Szczegóły w elemecie DinnersController.

Te hiperlinki możemy wygenerować w naszym widoku Indeks na jeden z dwóch sposobów. Pierwszym z nich jest ręczne utworzenie kodu HTML <elementów>, takich jak poniżej, gdzie osadzamy <bloki %> w elemecie <> HTML:

Zrzut ekranu przedstawiający okno edytora kodu z wyróżnionym tekstem bloku klasy i procentu oraz zakreślonego na czerwono.

Alternatywną metodą, którą możemy użyć, jest wykorzystanie wbudowanej metody pomocnika "Html.ActionLink()" w ASP.NET MVC, która obsługuje programowe tworzenie kodu HTML <> elementu, który łączy się z inną metodą akcji na kontrolerze:

<%= Html.ActionLink(dinner.Title, "Details", new { id=dinner.DinnerID }) %>

Pierwszy parametr metody pomocnika Html.ActionLink() jest tekstem linku do wyświetlenia (w tym przypadku tytułem kolacji), drugim parametrem jest nazwa akcji Kontroler, do którego chcemy wygenerować łącze (w tym przypadku metoda Details), a trzeci parametr jest zestawem parametrów do wysłania do akcji (zaimplementowany jako anonimowy typ z nazwą/wartościami właściwości). W tym przypadku określamy parametr "id" kolacji, z którym chcemy się połączyć, a ponieważ domyślna reguła routingu adresów URL w ASP.NET MVC to "{Controller}/{Action}/{id}" metoda pomocnika Html.ActionLink() wygeneruje następujące dane wyjściowe:

<a href="/Dinners/Details/1">.NET Futures</a>

W naszym widoku Index.aspx użyjemy metody pomocnika Html.ActionLink() i każda kolacja na liście zawiera link do odpowiedniego adresu URL szczegółów:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Upcoming Dinners
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Upcoming Dinners</h2>

    <ul>
        <% foreach (var dinner in Model) { %>
        
            <li>     
                <%=Html.ActionLink(dinner.Title, "Details", new { id=dinner.DinnerID }) %>
                on 
                <%=Html.Encode(dinner.EventDate.ToShortDateString())%>
                @
                <%=Html.Encode(dinner.EventDate.ToShortTimeString())%>
            </li>
            
        <% } %>
    </ul>
</asp:Content>

A teraz, gdy trafimy na adres URL /Dinners , nasza lista kolacji wygląda następująco:

Zrzut ekranu przedstawiający okno odpowiedzi aplikacji z listą nadchodzących kolacji z nowymi linkami odpowiadającymi elementom listy.

Po kliknięciu dowolnej z kolacji na liście przejdziemy w celu wyświetlenia szczegółowych informacji o nim:

Zrzut ekranu przedstawiający okno odpowiedzi aplikacji z wybranym elementem listy i szczegółami odpowiadającymi mu wprowadzoną do bazy danych.

Nazewnictwo oparte na konwencji i struktura katalogu \Views

ASP.NET aplikacje MVC domyślnie używają struktury nazewnictwa katalogów opartych na konwencji podczas rozpoznawania szablonów widoku. Dzięki temu deweloperzy nie muszą w pełni kwalifikować ścieżki lokalizacji podczas odwoływania się do widoków z klasy Kontroler. Domyślnie ASP.NET MVC będzie szukać pliku szablonu widoku w katalogu *\Views[ControllerName]* pod aplikacją.

Na przykład pracujemy nad klasą DinnersController, która jawnie odwołuje się do trzech szablonów widoków: "Index", "Details" i "NotFound". ASP.NET MVC domyślnie wyszukuje te widoki w katalogu \Views\Dinners poniżej katalogu głównego aplikacji:

Zrzut ekranu przedstawiający okno Eksplorator rozwiązań z hierarchią folderów z folderem Kolacja wyróżnionym w niebieskim prostokątze.

Zwróć uwagę na to, jak obecnie istnieją trzy klasy kontrolerów w projekcie (DinnersController, HomeController i AccountController — ostatnie dwa zostały dodane domyślnie podczas tworzenia projektu) i istnieją trzy podkatalogy (jeden dla każdego kontrolera) w katalogu \Views.

Widoki, do których odwołuje się kontrolery Home and Accounts, automatycznie rozpoznają szablony widoków z odpowiednich katalogów \Views\Home i \Views\Account . Katalog \Views\Shared umożliwia przechowywanie szablonów widoków, które są ponownie używane na wielu kontrolerach w aplikacji. Gdy ASP.NET MVC podejmie próbę rozwiązania szablonu widoku, najpierw sprawdzi się w katalogu specyficznym dla \Views[Controller] i jeśli nie będzie można znaleźć szablonu widoku, będzie on wyglądał w katalogu \Views\Shared .

Jeśli chodzi o nazewnictwo poszczególnych szablonów widoków, zalecane wskazówki to, aby szablon widoku miał taką samą nazwę jak metoda akcji, która spowodowała renderowanie. Na przykład powyżej metody akcji "Indeks" jest używany widok "Index" w celu renderowania wyniku widoku, a metoda akcji "Details" używa widoku "Details" w celu renderowania wyników. Dzięki temu można szybko sprawdzić, który szablon jest skojarzony z każdą akcją.

Deweloperzy nie muszą jawnie określać nazwy szablonu widoku, gdy szablon widoku ma taką samą nazwę jak metoda akcji wywoływana na kontrolerze. Zamiast tego możemy przekazać obiekt modelu do metody pomocnika "View()" (bez określania nazwy widoku), a ASP.NET MVC automatycznie wywnioskuje, że chcemy użyć szablonu widoku \Views[ControllerName][ActionName] na dysku, aby go renderować.

Dzięki temu możemy trochę wyczyścić kod kontrolera i uniknąć dwukrotnego duplikowania nazwy w naszym kodzie:

public class DinnersController : Controller {

    DinnerRepository dinnerRepository = new DinnerRepository();

    //
    // GET: /Dinners/

    public ActionResult Index() {

        var dinners = dinnerRepository.FindUpcomingDinners().ToList();

        return View(dinners);
    }

    //
    // GET: /Dinners/Details/2

    public ActionResult Details(int id) {

        Dinner dinner = dinnerRepository.GetDinner(id);

        if (dinner == null)
            return View("NotFound");
        else
            return View(dinner);
    }
}

Powyższy kod jest potrzebny do zaimplementowania ładnego środowiska listy/szczegółów kolacji dla witryny.

Następny krok

Mamy teraz ładne środowisko przeglądania kolacji zbudowane.

Włączmy teraz obsługę edycji formularzy danych CRUD (Create, Read, Update, Delete).