Menu nawigacyjne w ASP.NET
Wstęp
Każdy z pewnością wie doskonale czym jest i jednocześnie jak ważne może być dla osiągnięcia pełnej funkcjonalności każdej witryny internetowej dobrze zaprojektowane menu nawigacyjne. Nawet najlepiej zaprojektowany serwis od strony merytorycznej bez równie dobrego systemu nawigacyjnego nie usatysfakcjonuje w pełni jego użytkowników. W ekstremalnych sytuacjach źle zaprojektowana nawigacja może nawet zniechęcić do całej witryny bez względu na jej innej zalety. Takie menu de facto jest dzisiaj obowiązującym standardem każdego portalu internetowego wraz z nagłówkiem (zawierającym często logo reklamowe) oraz ze stopką serwisu. W poniższym artykule chciałbym, więc przedstawić sposoby projektowania i wykonania takiego właśnie menu.
Niektórzy z nas pamiętają jeszcze zapewne czasy starego, klasycznego ASP i poczciwego <!--#include... -->. Wykonanie porządnego systemu nawigacyjnego oraz zarządzanie nim stanowiło nieraz prawdziwy koszmar. Na szczęście tamte czasy już dawno minęły i wraz z technologią ASP.NET otrzymaliśmy nowe sposoby podejścia do tego tematu. Główny nacisk w poniższym artykule położę przede wszystkim na przedstawienie najbardziej rozpowszechnionych metod wykonania menu funkcjonujących dzisiaj w świecie internetowym. Nie jest moją intencją całkowite wyczerpanie tematu, ale nakreślenie głównych idei i praktyk, dzięki czemu każdy (mam nadzieję) będzie w stanie samemu wykonać menu nawigacyjne na własne potrzeby. Zacznę od przedstawienia najprostszego menu. Następnie wykonamy nieco bardziej skomplikowane (dwupoziomowe) menu. Później przedstawię dostępne w internecie darmowe rozwiązania i zakończę artykuł tym, co nas czeka w przyszłości, czyli ASP.NET 2.0 i możliwościami MS Visual Studio 2005.
1. Proste menu nawigacyjne
Zacznę od przedstawienia najprostszego rozwiązania, które będzie jednak zawierało te same pomysły, co wykorzystywane również w bardziej zaawansowanych projektach menu. Oto jaki efekt chcielibyśmy osiągnąć:
Rys. 1 Proste menu nawigacyjne
Każdy dział posiada tytuł oraz jest oznaczony ikoną. W środku poszczególnych działów znajdują się linki do zdefiniowanych przez nas pozycji. Dodatkowo zależy nam na jak największej elastyczności i funkcjonalności. Poniżej opisze po kolei elementy, które będą nam do tego potrzebne.
1.1 XML
Podstawową rzeczą jaką musimy uwzględnić jest problem przechowywania pozycji takiego menu. XML zapewnia elastyczność, niezawodność (dane są przechowywane lokalnie w pliku) oraz przede wszystkim oddzielenie warstwy danych od warstwy kodu. Dzięki temu możemy bardzo łatwo w przyszłości zmieniać pozycje menu bez potrzeby wykonania rekompilacji kodu. Wystarczy w miarę potrzeb edycja pliku XML zawierającego odpowiednie dane. Dodatkowo (co jest być może najważniejsze) menu nawigacyjne jest oczywiście rekurencyjne (zawiera podmenu, które z kolei mogą być także menu itd.), a XML pozwala na bardzo łatwe definiowanie tego typu struktur.
Oto plik menu.xml zdefiniowany na potrzeby prostego menu nawigacyjnego:
<?xml version="1.0"?>
<Menu opis="Nawigacja">
<Dzial opis="Sci-Fi" ikona="logo1.gif">
<Pozycja opis="Link1" link="default.aspx"/>
</Dzial>
<Dzial opis="Horror" ikona="logo2.gif">
<Pozycja opis="Link2" link="default.aspx"/>
</Dzial>
</Menu>
Korzeń dokumentu <Menu> zawiera pojedynczy atrybut, który stanowi tytuł menu nawigacji. Poniżej znajduje się lista poszczególnych działów <Dzial>, w których znajdują się pozycje <Pozycja> stanowiące odnośniki do odpowiednich stron. <Dzial> zawiera dwa atrybuty: tytuł oraz ikonę, która pojawi się przy nazwie działu. Więcej informacji na temat standardu XML można znaleźć pod adresem http://www.w3.org/XML .
1.2 CSS (Cascading StyleSheets)
Skoro mamy już zdefiniowany plik XML, w którym przechowujemy dane wyświetlane w menu, a jego edycja jest całkowicie niezależna od warstwy kodu to po połączeniu tego ze stylami CSS otrzymujemy potężne możliwości. Bez przeprowadzania żadnej rekompilacji kodu jesteśmy w stanie zmieniać wewnętrzne pozycje menu oraz dodatkowo, dzięki stylom możemy w szybki i prosty sposób zmieniać wygląd naszego menu. Dokładne informacje na temat CSS można znaleźć tutaj http://www.w3.org/Style/CSS/ .
Oto plik menu.css zdefiniowany na potrzeby naszego menu nawigacyjnego:
.Menu_Table
{
background-color: #eff7de;
font-size: 8pt;
font-family: Verdana;
font-style: normal;
border: 1px;
border-color:#000000;
border-style:solid;
line-height: 10pt;
}
.Menu_Tytul
{
font-weight: bold;
font-size: 8pt;
font-family: Tahoma;
background-color: #eeeeee;
text-align: center;
border: 1px;
border-style: solid;
border-color: #c0c0c0;
border-top: 0px;
border-left: 0px;
border-right: 0px;
padding-top: 2px;
padding-bottom: 2px;
}
.Menu_Dzial
{
background-color:#eff7de;
font-size: 10pt;
font-style: normal;
font-weight: bold;
font-family: Tahoma;
padding-top: 10px;
padding-bottom: 8px;
color: #000088;
}
.Menu_Pozycja
{
background-color:#eff7de;
font-size: 8pt;
font-style: normal;
font-family: Verdana;
}
A.Menu_Pozycja_Link
{
color:#000000;
font-family: Tahoma;
font-size:9pt;
font-style:normal;
text-decoration: none;
}
A.Menu_Pozycja_Link:Hover
{
color:#0000ff;
font-family:Tahoma;
font-size:9pt;
font-style:normal;
text-decoration: none;
}
Zawiera następujące klasy:
.Menu_Table: odpowiada za ramkę całego menu
.Menu_Tytul: odpowiada za nagłówek z tytułem naszego menu
.Menu_Dzial: odpowiada za formatowanie opisu poszczególnych działów
.Menu_Pozycja: odpowiada za poszczególne pozycje w danym dziale
A.Menu_Pozycja_Link i A.Menu_Pozycja_Link:Hover odpowiadają za odpowiednie formatowanie linków
Tak przygotowany plik stylów pozwala na pełną kontrolę nad menu oraz zapewnia odpowiednią elastyczność. Przykładowo, jeżeli chcemy zmienić kolor tła naszego menu, wystarczy dokonać małej zmiany w pliku stylów.
1.3 XSLT (Extensible Stylesheet Language Transformations)
Aby przekształcić przygotowany wcześniej plik XML do HTMLa przedstawiającego naszego menu potrzebny jest jeszcze plik xsl, dzięki któremu dokonamy odpowiedniej zmiany. Dopiero połączenie tych trzech technologii (XML, CSS, XSLT) stworzy podwaliny pod niezwykle elastyczne, dynamiczne i łatwe w zarządzaniu menu.
Oto plik menu.xsl zdefiniowany na potrzeby naszego menu nawigacyjnego:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/Menu">
<table width="150" cellspacing="0" cellpadding="0" class="Menu_Table">
<tr>
<td colspan="2" class="Menu_Tytul">
<xsl:value-of select="@opis"/>
</td>
</tr>
<xsl:for-each select="Dzial">
<tr >
<td width="40" align="center">
<img align="absmiddle">
<xsl:attribute name="src">
<xsl:value-of select="@ikona"/>
</xsl:attribute>
</img>
</td>
<td width="110" class="Menu_Dzial">
<xsl:value-of select="@opis"/>
</td>
</tr>
<xsl:for-each select="Pozycja">
<tr>
<td width="40">
<br/>
</td>
<td width="110" class="Menu_Pozycja">
<a class="Menu_Pozycja_Link">
<xsl:attribute name="href">
<xsl:value-of select="@link"/>
</xsl:attribute>
<xsl:value-of select="@opis"/>
</a>
</td>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Nie jestem w stanie omówić tutaj dokładnie transformacji XSL. Omówię krótko kilka rzeczy. Po resztę informacji odsyłam do http://www.w3.org/TR/xslt .
Najważniejszym elementem jest <xsl:for-each>, co umożliwia iterację po kolekcji działów i pozycji. Natomiast <xsl:attribute> pozwala na przydzielenie atrybutu (link i ikona w tym przypadku), a „@” służy do pobrania wartości samego atrybutu z pliku XML.
1.4 Kod (C#)
Skoro mamy już plik XML, CSS oraz XSL to wystarczy teraz tylko odrobina kodu, aby otrzymać menu.
Musimy przede wszystkim w kodzie strony zadeklarować odpowiednie nazwy przestrzeni pozwalające na obsługę XML oraz XSL:
[Kod C#] |
Dodatkowo potrzebujemy też:
[Kod C#] |
Na stronie tworzymy kontrolkę Label, w której umieścimy nasze menu:
<asp:Label id="MenuLabel" runat="server"> Tutaj znajdzie się menu nawigacyjne. </asp:Label>
Po stronie kodu tworzymy metodę Menu:
[Kod C#] |
Jako parametry przekazujemy jej nazwę pliku XML oraz pliku XSL. Używamy instancji klasy XslTransform, dzięki której przekształcimy plik XML (wykorzystując plik XSL) do postaci HTML. Do ładowania pliku XML wykorzystujemy klasę XpathDocument, a wynik przekształceń zapisujemy w utworzonej wcześniej kontrolce Label. Kod jest naprawdę prosty. Teraz wystarczy tylko wrzucić wywołanie tej metody do Page_Load i koniec - zbudowaliśmy właśnie menu.
[Kod C#] |
2. Złożone menu nawigacyjne
Poprzedni przykład wykorzystywał większość pomysłów, które będą również wykorzystane przy budowaniu trochę bardziej złożonego menu. Połączenie CSS, XML i XSLT stworzyło ogromną elastyczność i łatwość zarządzania. Jednak możemy pójść jeszcze o krok dalej i zbudować kontrolkę użytkownika (ang. User Control) implementujący menu, co jeszcze bardziej zwiększy możliwości. Dodatkowo chcemy, aby to menu było dwupoziomowe, więc wykorzystamy także JavaScript i nieuwzględnione wcześniej możliwości stylów CSS. Samo menu będzie tym razem zorientowane poziomo.
Kontrolka użytkownika pozwala wykorzystywać raz napisaną funkcjonalność (kod + interfejs) na innych stronach bez zmiany kodu, a nieraz nawet bez potrzeby wiedzy jak ten kod jest napisany. Na dodatek stworzenie kontrolki jest niezwykle proste. Z poziomu Visual Studio .NET wystarczy wybrać `Project-->Add Web User Control'. Nazwijmy naszą nową kontrolkę „menu.ascx”. Sama kontrolka użytkownika budową przypomina zwykły Web Form, ale nie będę tutaj dokładnie opisywał tworzenia tych kontrolek. Po więcej informacji odsyłam pod adres :
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconwebformsusercontrols.asp
Końcowy efekt będzie wyglądał tak:
Rys. 2 Złożone menu
Tradycyjnie już zacznę od przedstawienia pliku XML, następnie CSS, XSLT i pliku z kodem JavaScript obsługującym otwieranie i zamykanie menu.
2.1 XML
<?xml version="1.0"?>
<Menu>
<Gatunek opis="Sci-Fi">
<Autor>
<Nazwisko>Clarck</Nazwisko>
<Url>default.aspx</Url>
</Autor>
<Autor>
<Nazwisko>Dick</Nazwisko>
<Url>default.aspx</Url>
</Autor>
</Gatunek>
<Gatunek opis="Horror">
<Autor>
<Nazwisko>King</Nazwisko>
<Url>default.aspx</Url>
</Autor>
</Gatunek>
<Gatunek opis="Political Fiction">
<Autor>
<Nazwisko>Grisham</Nazwisko>
<Url>default.aspx</Url>
</Autor>
</Gatunek>
<Gatunek opis="Kryminal">
<Autor>
<Nazwisko>Chandler</Nazwisko>
<Url>default.aspx</Url>
</Autor>
</Gatunek>
<Gatunek opis="Fantasy">
<Autor>
<Nazwisko>Sapkowski</Nazwisko>
<Url>default.aspx</Url>
</Autor>
<Autor>
<Nazwisko>Gibson</Nazwisko>
<Url>default.aspx</Url>
</Autor>
<Autor>
<Nazwisko>Tolkien</Nazwisko>
<Url>default.aspx</Url>
</Autor>
<Autor>
<Nazwisko>Zelazny</Nazwisko>
<Url>default.aspx</Url>
</Autor>
</Gatunek>
</Menu>
Widzimy tutaj listę gatunków książek i przyporządkowanych im autorów. Każdy <Gatunek> posiada atrybut „opis” mówiący o typie gatunku. Natomiast <Autor> składa się z nazwiska i adresu url do odpowiedniej strony. Jak zwykle po więcej informacji na temat XML odsyłam pod adres http://www.w3.org/XML .
2.2 CSS
DIV#divMenu { background-color:#6699CC; }
TABLE#tMenu TD { color:white; padding:0px 5px 0px 5px; cursor:default; }
TABLE#tMenu TD.sMenuItem { font-weight:bold; cursor:hand; }
DIV.sMenu
{
font-size:120%; background-color:#6699CC;
position:absolute; visibility:hidden; width:130px;
padding:5px 5px 5px 8px; border-top:1 white solid;
}
DIV.sMenu A { text-decoration:none; color:white; font-weight:bold; }
DIV.sMenu A:hover { color:moccasin; }
W zasadzie najważniejszym nowym elementem jest tutaj visibility:hidden, dzięki czemu otwierane menu będzie domyślnie ukryte i za pomocą javscriptu będziemy nim sterowali. Widać, że największym problemem będzie właśnie otwieranie, zamykanie menu oraz sterowanie jego położeniem. Do sterowania położeniem wykorzystamy takie właściwości CSS jak:
• Top:
- położenie od góry
• Left:
- położenie od lewej strony
• OffsetHeight
- całkowita wysokość obiektu
• Offsetleft:
- względna pozycja od lewej
• Offsettop
- względna pozycja od góry
• Position
- pozycja
Po dodatkowe informacje na temat CSS odsyłam pod adres http://www.w3.org/Style/CSS/ .
2.3 XSLT
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<DIV ID="divMenu">
<TABLE ID="tMenu" BORDER="0">
<TR>
<xsl:for-each select="//Gatunek[Autor]">
<TD CLASS="sMenuItem"><xsl:attribute name="ID">tdMenu<xsl:value-of select="@opis" /> </xsl:attribute>
<xsl:value-of select="@opis" /></TD>
<xsl:if test="position()!=last()">
<TD>|</TD>
</xsl:if>
</xsl:for-each>
</TR>
</TABLE>
</DIV>
<xsl:for-each select="//Gatunek[Autor]">
<DIV CLASS="sMenu">
<xsl:attribute name="ID">divMenu<xsl:value-of select="@opis"/></xsl:attribute>
<DIV CLASS="sMenuSpac"></DIV>
<xsl:for-each select="Autor">
<DIV>
<A>
<xsl:attribute name="HREF"><xsl:value-of select="Url"/>
</xsl:attribute>
<xsl:value-of select="Nazwisko"/>
</A>
</DIV>
</xsl:for-each>
</DIV>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Informacje na temat XSLT można znaleźć pod tym adresem http://www.w3.org/TR/xslt .
2.4 Kod JavaScript
Do prawidłowej obsługi menu niezbędny jest JavaScript, gdyż otwieranie i zamykanie menu musi odbywać się oczywiście po stronie klienta bez wysyłania (postback) strony na serwer.
[Kod JavaScript] |
Kod w zasadzie jest stosunkowo prosty, dlatego omówię krótko tylko główną funkcję: function document.onmouseover(). Zostaje ona wywołana jako reakcja na zdarzenie najechania kursorem myszki na naszą kontrolkę.
Następnie znajdujemy element, który wybrano:
[Kod JavaScript] |
Zmieniamy jego kolor i znajdujemy jego podmenu:
[Kod JavaScript] |
Jeżeli jest otwarte inne podmenu to je zamykamy:
[Kod JavaScript] |
Jeżeli jest podmenu to je otwieramy:
[Kod JavaScript] |
Natomiast jeżeli kursor nie znajduje się na naszym menu pierwszego poziomu i jest otwarte podmenu, ale to podmenu nie zawiera elementu, który wybrano i nie jest też wybrany żaden element menu to zamykamy menu.
[Kod JavaScript] |
2.5 Kontrolka użytkownika
Posiadamy w tej chwili już wszystkie niezbędne elementy do zbudowania menu. Musimy je teraz połączyć w całość w postaci naszej kontrolki „menu.ascx”.
Po pierwsze po stronie kodu HTML kontrolki menu.ascx musimy dodać następujące rzeczy:
<link rel="stylesheet" type="text/css" href="menu.css">
<script language="javascript" src="menu.js"></script>
Oraz:
<asp:Label id="MenuLabel" runat="server"> Tutaj znajdzie się nasze menu. </asp:Label>
Dwa pierwsze odpowiadają za dodanie stylów i kodu JavaScript. Natomiast w kontrolce Label, już tradycyjnie, umieścimy całe menu.
Natomiast po stronie kodu musimy dodać, tak jak w poprzednim przykładzie, wywołanie metody Menu(), która przekonwertuje nasz plik XML na HTML. Oto nowa metoda Menu():
[Kod C#] |
Nazwy plików XML i XSL będziemy przechowywali w osobnych zmiennych:
[Kod C#] |
Dlatego dodamy jeszcze metody pozwalające na dostęp do tych zmiennych z zewnątrz:
[Kod C#] |
Wystarczy jeszcze dodać wywołanie metody Menu() do Page_Load kontrolki:
[Kod C#] |
W tej chwili mamy już działającą kontrolkę implementującą menu.
Aby dodać ją do dowolnej innej strony .aspx należy ją zarejestrować:
<%@ Register TagPrefix="Menu" TagName="NaszeMenu" Src="menu.ascx" %>
i użyć (należy pamiętać o podaniu nazw plików XML i XSL podczas jej wywołania):
<Menu:NaszeMenu id=menu runat="server" XmlFileName="menu.xml" XslFileName="menu.xslt">
3. Dostępne rozwiązania
Bardzo wiele komercyjnych rozwiązań można znaleźć na stronie „ASP.NET Control Gallery” (http://www.asp.net/ControlGallery/default.aspx?Category=32&tabindex=2) .
Spośród darmowych rozwiązań wyróżnia się „skmMenu”, który jest dostępny na zasadach open-source. Jego budowa jest podobna do rozwiązań przedstawionych w tym artykule. Menu używa plików XML, CSS, XSLT oraz JavaScriptu. Można je znaleźć pod adresem www.skmmenu.com . Aby go używać wystarczy dodać referencje do skompilowanego assembly skmMenu.dll. Z Visual Studio .NET można je także dodać do Toolboxa i operować z poziomu Designera w trybie WYSIWYG (ang. What You See Is What You Get). Samo menu łączy się z plikiem XML podobnie jak inne kontrolki dostępne w Visual Studio .NET poprzez DataSource i DataBind(). Więcej informacji na temat budowy i działania „skmMenu” można znaleźć w tym artykule
http://msdn.microsoft.com/asp.net/community/authors/scottmitchell/default.aspx?pull=/library/en-us/dnaspp/html/aspnet-buildmenuservercontrol.asp .
4. ASP.NET 2.0
Cały artykuł byłby zupełnie zbędny, gdyby Microsoft już w wersji 1.0 lub 1.1 .NET dodał kontrolki obsługujące menu nawigacyjne będące na równie wysokim poziomie co na przykład sławny już DataGrid. Jednak nic takiego się nie stało, co więcej zupełnie zabrakło jakichkolwiek kontrolek wspomagających tworzenie takiego menu w ASP.NET. Na szczęście w wersji 2.0 dostępnych jest już wiele gotowych i bardzo dobrych kontrolek, które właściwie we wszystkim wyręczą twórcę takiego menu. Na koniec artykułu chciałbym właśnie poświęcić chwilę na krótkie przedstawienie tego, co nas czeka już w całkiem niedalekiej przyszłości (na bazie Visual Studio .NET 2005 Beta 1).
W Toolboxie pojawiła się nowa kategoria „Navigation” a w niej:
• SiteMapPath control
Binduje się do pliku XML i pozwala na dodanie linków nawigacyjnych w postaci Root Node --> Parent Node --> Current Node, czyli nawigacji z zachowaniem hierarchii w strukturze.
• Menu control
Typowe menu z wieloma możliwościami konfiguracji (położenie poziome, pionowe, różne style jeżeli chodzi o wygląd itd.). Również używa pliku XML.
Dodatkowo w Standard tab w Toolboxie znajduje się TreeView control. Kontrolka ta pozwala teraz na utworzenie podobnej nawigacji do tej znanej z MSDN.
Widać, więc że Microsoft nadrobił zaległości i wraz z ASP.NET 2.0 dostaniemy większość niezbędnych rzeczy potrzebnych do stworzenia bardzo dobrej nawigacji pomiędzy stronami.
Podsumowanie
Mam nadzieję, że w tym krótkim artykule udało mi się przedstawić główne podejścia i idee stosowane przy projektowaniu menu nawigacyjnego. Oto przypomnienie niezbędnych technologii potrzebnych przy projektowaniu elastycznego i uniwersalnego menu:
• XML
• CSS
• XSLT
Oczywiście do osiągnięcia końcowego efektu potrzebna jest jeszcze możliwość połączenia tych trzech rzeczy i w tym przypadku bardzo dobrze sprawdza się środowisko .NET posiadające doskonałą klasę przekształceń XSL (XslTranform). Przyszłość zapowiada się dla ASP.NET jeszcze lepiej pod tym względem ze względu na istotne wzmocnienia w kontrolkach poczynione przez Microsoft w tym kierunku.