MasterPage - začlenění stránky do stránky jiné

3. díl - MasterPage - začlenění stránky do stránky jiné

Tomáš Herceg       28. 8. 2007       C#, VB.NET, ASP.NET WebForms       11914 zobrazení

V tomto díle si ukážeme, jak v prostředí ASP.NET včlenit jednu stránku do druhé, a to pomocí MasterPages. Ukážeme si jednoduchý příklad použití a také se dozvíte něco o odkazech a o tom, jak používat komponentu HyperLink.

V minulém díle jsme si popsali úplné základy tvorby stránky v ASP.NET 2.0. Víme již, že můžeme využívat serverové komponenty, které mají svoje události, na něž můžeme reagovat, máme i základní povědomí o tom, jak to funguje. Můžeme se tedy pustit dále, dnes si ukážeme, jak využít tzv. MasterPage.

Většina webových aplikací má jednotné uživatelské rozhraní - nahoře je logo, na boku menu, dole copyright a napravo třeba novinky a odkazy na nejnovější obsah. A někam doprostřed mezi to všechno dynamicky dosazujeme obsah, který si uživatel vyžádal. Dříve se to dalo řešit pomocí rámců, které okno prohlížeče rozdělily na několik autonomních stránek, jež se mohly ovlivňovat navzájem (kliknutím na odkaz v jednom rámci se otevřela stránka v rámci jiném). Pokud to mělo být bez rámců, dalo se využít SSI (Server Side Includes), anebo jakoukoliv skriptovací technologii na straně serveru (např. PHP).

Často se celý layout rozdělil na tři části - header (hlavička), footer (patička) a dynamická část. A právě do dynamické části se přidaly dvě includes (vsuvky) - jedna úplně nahoru vložila do stránky header a druhá úplně dolů vložila footer. Výsledkem bylo, že adresa URL ukazovala na požadovanou stránku, která si sama před sebe dala hlavičku a za sebe patičku. Není to rozhodně jediné používané řešení a určitě není ideální, ale zvláště mezi začátečníky našlo velkou oblibu.

ASP.NET (nejen) přistupuje k problému jinak - pomocí MasterPages. MasterPage je speciální stránka, která obvykle začíná značkami <!DOCTYPE... a <html> a končí značkou </html>. Najdete v ní celý layout se vším všudy, až na dynamickou část. Ta je nahrazena komponentou ContentPlaceHolder a právě do ní se dynamický obsah dosadí.

MasterPage je ale sama o sobě k ničemu - v prohlížeči na ni neodkazujeme (má příponu .master a ne .aspx). Musíme tedy kromě ní vytvořit normální stránku, do které dáme dynamický obsah (třeba jej vypíšeme z databáze atd.). Celý dynamický obsah musí být uvnitř komponenty Content, které navíc musíme nastavit hodnotu vlastnosti ContentPlaceHolderId na ID ContentPlaceHolderu v MasterPage, do kterého dosazujeme. Navíc v direktivě Page této stránky musíme specifikovat vlastnost MasterPageFile, která ukazuje na příslušný MasterPage. Možná si řeknete, že je toho nastavování nějak moc, ale většinu toho za nás stejně udělá Visual Studio 2005 či Visual Web Developer.

První seznámení s MasterPage

Přidání nové položky

Spusťte si vývojové prostředí a pokud máte projekt z minula, klidně jej otevřete kliknutím na jeho název na úvodní stránce. Můžete ale klidně vytvořit nový, je to úplně jedno.

V projektu byste měli nyní mít soubor Default.aspx. V podokně Solution Explorer na něj klikněte pravým tlačítkem a zvolte možnost Delete, čímž tento soubor smažete. Dále pravým tlačítkem klikněte na první položku (název projektu) a zvolte položku Add New Item.... Zobrazí se dialog pro výběr typu nové položky.

Dialog pro výběr typu nové položky.

Vyberte typ MasterPage, zkontrolujte, že není zaškrtnuto pole Place code in separate file, které by zapříčinilo, že kód bude umístěn do druhého souboru. Vyberte si svůj oblíbený programovací jazyk (pro tentokrát je to ale jedno, kód dnes psát nebudeme), nakonec potvrďte tlačítkem Add a je hotovo.

Přepněte se do režimu Source a prohlédněte si kód, který stránka obsahuje.

<%@ Master Language="VB" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
        </asp:contentplaceholder>
    </div>
    </form>
</body>
</html>

První řádek je direktiva Master, která definuje jazyk stránky (záleží, který jste si v dialogu zvolili). Následuje nám jistě důverně známá deklarace !DOCTYPE, která říká, že dokument má být XHTML 1.0 Transitional. Nic neobvyklého.

První zvláštnost, která by nás mohla zaskočit, je sekce <script runat="server"></script>. To je divné, značka script by měla být přece v sekci head, případně body. Atribut runat by nám ale mohl něco prozradit - asi to bude jen pro server. A je to pravda. V dialogu jsme úmyslně nezaškrtli Place code in separate file, čímž jsme zajistili, že na rozdíl od minula se kód na pozadí (code-behind) bude nyní vkládat do stejného souboru jako deklarativní popis vzhledu stránky (HTML a komponenty). Konkrétně se code-behind vloží právě do této sekce, čímž je tedy záhada zbloudilé sekce script odtajněna a můžeme pokračovat dále.

Následuje totiž standardní struktura HTML stránky - html, head, title atd., všichni to dobře známe. Uvnitř formuláře je komponenta ContentPlaceHolder, o které jsem již psal na začátku článku. Do ní se dosadí obsah vkládané stránky. Abychom nasimulovali reálnou aplikaci, vytvoříme alespoň primitivní layout. Upravte tedy kód stránky MasterPage.master, aby vypadal takto: 

<%@ Master Language="VB" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled page</title>
    <style>
        #header {
            background-color: #c0ffa0;
            width: 100%;
        }
        #header h1 {
            margin: 0.5em;
        }
        #footer {
            font-size: 80%;
            text-align: center;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <div id="header">
        <h1>První web s MasterPage</h1>
    </div>
    <div>
        <asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
        </asp:contentplaceholder>
    </div>
    <p id="footer">&copy; 2007 VbNet.cz</p>
    </form>
</body>
</html>

Layout je to opravdu primitivní (pokud se to dá nazvat vůbec layoutem), omlouvám se také za tu hnusnou zelenou, nejsem designér a nemám estetické cítění. Ještě musím podotknout, že MasterPage může obsahovat více komponent ContentPlaceHolder, takže můžeme mít dynamických částí libovolný počet.

Stránka s obsahem

image

Abychom nemuseli obsahovou stránku vytvářet ručně (musí v ní být komponenta Content a je to trocha nastavování), může to opět vývojové prostředí udělat za nás. V okně Solution Explorer tedy klikněte pravým tlačítkem na naši MasterPage.master a vyberte volbu Add Content Page. Visual Studio vytvoří obsahovou stránku "na míru" pro vybranou MasterPage.

Nově vytvořená stránka nyní nebude obsahovat žádné značky html, head apod., jediné, co v ní bude, je komponenta Content. Její vlastnost ContentPlaceHolderID je nastavena na ID odpovídající komponenty v MasterPage a do ní se také dosadí.

Vnitřně to funguje o něco složitěji, MasterPage je vlastně serverová komponenta a je uchována v kolekci Controls ve stránce s obsahem. To je pro nás ale v tuto chvíli nepodstatné.

Pokud se podíváme na kód obsahové stránky, vypadá zhruba takto:

<%@ Page Language="VB" MasterPageFile="~/MasterPage.master" Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
</asp:Content>

Veškerý obsah, který chceme zobrazit, musíme umístit dovnitř elementu Content. V direktivě Page vidíme dvě podstatné vlastnosti - MasterPageFile (určuje cestu k MasterPage) a Title. Do Title napište název stránky - např. "První ASP.NET stránka". Hodnota této vlastnosti se nastaví do hlavičky stránky.

Možná vás trochu zarazil zápis cesty ve vlastnosti MasterPageFile. Tento zápis se používá v ASP.NET velmi často, znak ~ (tilda) označuje kořenový adresář naší webové aplikace. Hodí se to v případě, pokud potřebujete odkázat na nějaký soubor a nevíte, kde přesně se v rámci aplikace nacházíte (často se to stává, když píšete vlastní komponenty - nikdy předem nevíte, kde leží stránka, která komponentu využívá). Často ani nevíte, jestli aplikace leží přímo v kořenové URL adrese serveru (www.něco.cz), nebo v nějakém virtuálním adresáři (www.něco.cz/aplikace), nemůžete tedy adresy začínat jen lomítkem (třeba /Default.aspx). Proto se používá vlnovka, která se o tyto případy postará. Funguje prakticky u všech serverových komponent, nikoliv pochopitelně u HTML značek odkazů, obrázků atd., které nemají runat="server".

Vraťme se ale k samotné stránce, vepište do ní nějaký odstavec a nějaké HTML, uložte a stiskněte klávesovou zkratku Ctrl+F5. Stránka se objeví v prohlížeči a mezi záhlavím a zápatím v MasterPage se objeví námi vytvořená obsahová stránka. Komponenty a jejich události by fungovaly samozřejmě pořád, nic se prakticky nemění.

Výsledná stránka v prohlížeči

Pár odkazů

Abyste viděli, jak se odkazuje na dynamické stránky, přidejte si druhou Content Page a pojmenujte ji Druha.aspx. Napište do ní opět nějaký text, nastavte jí titulek v direktivě Page a do stránky Default.aspx vložte tento odkaz:

<a href="Druha.aspx">Druhá stránka</a>

Pokud v prohlížeči stránku obnovíte klávesou F5, objeví se v ní přidaný odkaz. Pokud na něj kliknete, ukáže se druhá stránka.

Potíž s adresami by nastala, kdybychom chtěli dát do MasterPage menu, které by odkazovalo na stránky v různých složkách. Zde bychom museli použít komponentu HyperLink, což je vlastně jakési "zaobalení" klasického <a href=.... Umí totiž adresy s vlnovkou.

Pokud třeba budu mít MasterPage, které bude odkazovat na stránky Default.aspx a Admin/Default.aspx, obě včleněné do této MasterPage, musím použít asp:HyperLink. Pokud bych v MasterPage použil obyčejné odkazy na Default.aspx a Admin/Default.aspx, nefungovaly by již ze stránky Admin/Default.aspx, protože první by odkazoval na tu samou adresu a druhý na adresu Admin/Admin/Default.aspx, která ale samozřejmě neexistuje. Pokud použijeme adresy ~/Default.aspx a ~/Admin/Default.aspx, nemusíme se ničeho bát. Normální odkaz je ale nezvládne, musíme tedy použít komponentu. Menu by vypadalo takto:

<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Home</asp:HyperLink>
<asp:HyperLink ID="HyperLink2" runat="server" NavigateUrl="~/Admin/Default.aspx">Administrace</asp:HyperLink>

Vlastnost NavigateUrl obsahuje adresu, která se nejprve převede (vlnovka se nahradí relativní cestou z aktuálního umístění do kořene aplikace), a pak teprve vypíše do stránky jako odkaz.

Zbývá ještě jednou připomenout, že nikdy neodkazujeme na samotný MasterPage, ale vždy na content pages, tedy na stránky s obsahem. A ještě by bylo vhodné připomenout, že ASP.NET podporuje i složitější včleňování - i MasterPage můžete včlenit stejným způsobem do jiného MasterPage.

Podotýkám, že ASP.NET má vestavěné sofistikovanější nástroje a funkce pro tvorbu menu a mapy webu, má i některé předpřipravené komponenty, celý tento model menu je lokalizovatelný a automaticky umí skrývat uživateli ty položky, ke kterým nemá přístup. O tom ale až někdy příště, je to na nás zatím příliš složité.

Pro dnešek je to vše, v příštím díle si předvedeme základy práce s databází. Doufám, že se vám tutoriál líbí, jakékoliv připomínky či náměty pište do diskuse.

 

hodnocení článku

2 bodů / 2 hlasů       Hodnotit mohou jen registrované uživatelé.

 

 

 

Nový příspěvek

 

Diskuse: MasterPage - začlenění stránky do stránky jiné

Mám následující problém - v ContentPage mám button, který spustí určitou funkci. Na konci této funkce potřebuji zavolat funkci, která je zapsána v MasterPage. Jde toto nějak vyřešit? Zatím jsem na nic nepřišel.

Všem díky za odpověď...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

To ve většině případů svědčí o špatném návrhu, nenapadá mě rozumné použití, proč volat se stránky funkci v MasterPage. Zkuste popsat, čeho se přesně snažíte dosáhnout.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Problém je trošku složitější, ale princip se dá ukázat na příkladu:

- na MasterPage je zobrazován nějaký aktuální stav hodnoty (např. součet nákladů)

- na ContentPage je formulář pro zadání položek související s náklady, button zavolá metodu v ContentPage a podle zadaných parametrů vypočítá další náklady, které jsou následně (resp. jejich suma) potřeba aktualizovat na MasterPage (bez znovunačtení celé stránky). Formulářů je více (více ContentPage), každý používá jiný výpočet nákladů, proto se mi jeví lepší použít MasterPage, než ke každému formuláři "kopírovat" funkci pro výpočet sumy nákladů...

Děkuji za ochotu

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Podle mě je na tohle nejlepší vhodně použít objekty - prostě funkce pro sčítání nákladů napsat do nějaké třídy (pokud to jde, využít dědičnost). Jinak je také dobré si uvědomit, že v MasterPage můžete mít víc prvků ContentPlaceHolder, a v každé ContentPage tedy i odpovídající počet Contentů.

Proto tedy doporučuji udělat místo zobrazování výsledků přímo v nějakém Labelu v MasterPage dát tam ContentPlaceHolder a do stránek s formulářem vrazit druhý Content, do kterého dáte Label a jeho hodnotu nastavíte přímo v kódu v ContentPage. Pokud více ContentPages používá nějakou společnou funkci, nedávejte ji do MasterPage, ale do nějaké třídy (třeba jako statickou metodu).

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Jen upresnim, ze k prvkum Master tridy se dostat lze pouzitim objektu Master ve tride ContentPage. Je potreba do ContentPage vlozit nasledujici:

<%@ MasterType virtualpath="~/Index.Master" %>

Tohle nejspis zaruci, ze pak objekt Master bude vas upraveny Master a ne zakladni trida MasterPage. To je trochu slozitejsi takze priklad:

Moje master trida se nazyva Index a je zdededena od MasterPage. V content page mam pristup k objektu Master, ktery je bez vyse uvedene direktivy typu MasterPage (coz je mi k nicemu, kdyz potrebuju pristupovat k moji tride Index) a tato directiva "pretypuje" objekt Master na muj Index.

Pak muzu ve sve ContentPage pristoupim k Masteru takto:

Master.AboutLinkImage.ImageUrl = "priklad.jpg"

AboutLinkImage je normalne property v Master tride.

Swap

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: MasterPage - začlenění stránky do stránky jiné

Dobrý vysvětlení MasterPage. Thx

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: MasterPage - začlenění stránky do stránky jiné

Dost časté je v MasterPage použít TreeView a z nabídky pan volat přes NavigateURL stránky, které se objevují v ContentPlaceHolder. Mám problém s tím, že se mi pokaždé TreeView nastaví do původního tvaru. Bylo by vhodné, kdyby v TreeView zůstala struktura rozbalená nebo sbalená tak, jak byla. Umíte poradit?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

tak jsem trochu zapremýšlel, a vyřešil to takhle ;)

SiteMapNode node = (SiteMapNode)e.Node.DataItem;

if (node.HasChildNodes)

{

e.Item.Selectable = false;

}

stačilo to přetypovat, ehe

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: MasterPage - začlenění stránky do stránky jiné

Dobrý den, mám problém s pozadím a zobrazováním obrázků na content page. Master Page a constent page jsou v kořenovém adresáři,obrázky a pozadí načítají ze složky image která je také v kořenovém adresáři. Zde se zobrazuje vše správně. Vytvořil jsem si ale v kořenovém adresáři další složku která se jmenuje private a kde je uložena jedna content page s názvem private pro kterou chci později nastavit oprávnění.Tato stránka taky využívá master page,rozložení načte dobře ale veškeré obrázky a pozadí načítané ze složky image se nezobrazí.Nevíte kde by mohl být problém?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Problém je naprosto evidentní - pokud je cesta k aplikaci http://localhost/něco/ a tam máte MasterPage, ve kterém odkazujete na obrázek např. images/obr.jpg, pak se cesta dohromady poskládá jako http://localhost/něco/images/obr.jpg. Pokud ale vlezete na adresu http://localhost/něco/podadresář/, pak prohlížeč požaduje obrázek http://localhost/něco/podadresář/images/obr.jpg, který pochopitelně neexistuje.

Řešení je následující: pokud obrázky máte jako <img src="images/obr.jpg">, musíte místo nich dát komponentu Image a vlastnost ImageUrl nastavit na "~/images/obr.jpg". Ta vlnovka je důležitá, aby se nedělo to, co teď. Vlnovka značí kořenový adresář aplikace a ASP.NET se už o to postará.

Pokud máte cesty k obrázkům uvedené někde v CSS, pak je to trošku složitější - musíte vytvořit tzv. skin. O tom, jak se to dělá, se dočtete v připravovaném dalším dílu tohoto seriálu. Nemůžu bohužel říci, kdy tento díl vyjde.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Děkuji, problémy s obrázkama to vyřešilo:) Ale ještě mám problém s pozadím. Na stránce mám několik Layerů do kterých načítám pozadí také ze složky image a ty se mi také nezobrazují.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: MasterPage - začlenění stránky do stránky jiné

Na MasterPage som pridal dva Buttony a nasledujúce kódy:

<script runat="server">  
    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        Me.Button1.Enabled = False
        Me.Button2.Enabled = True
    End Sub

    Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        Me.Button2.Enabled = False
        Me.Button1.Enabled = True
    End Sub
</script>

<div id="header">
        <h1>První web s MasterPage&nbsp;&nbsp;&nbsp;
            <asp:Button ID="Button1" runat="server"  Text="Button1" OnClick="Button1_Click" PostBackUrl="~/Default2.aspx" />
            <asp:Button ID="Button2" runat="server" Text="Button2" OnClick="Button2_Click" PostBackUrl="~/Default.aspx" /></h1>
    </div>

V podstate to funguje až na jeden problém na kždé tlačítko treba klikať dvakrát. Najprv prepne stránku a až potom sa uvedie do stavu Enabled=false. Mohli by ste mi povedať, že prečo?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: MasterPage - začlenění stránky do stránky jiné

Zdravím,

chystáte se také napsat nějaký článek o technologii Ajax?

Děkuji

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Trpělivost, na všechno se dostane, ale zatím nejsme tak daleko. Ajax není věc pro začátečníky. Ale časem se na něj určitě dostaneme, implementace v ASP.NET je velmi snadná.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Správně, žádný spěch, jen prosím hezky pokračuj čtvrtým dílem :-)

Díky!

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Tak to jsem rád. Když jsem se pouštěl do ASP .NET tak jsem si myslel bůhvíjak to nebude složité, ale základy si člověk osvojí hned! Máte vážně SKVĚLÝ seriál!

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Neboj se, ono to teprve složité začne být. Přijdou databáze a pěkně se to zkomplikuje.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Už aby to bylo! :-)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Fajn. Chlape, už máš další díl? My jsme připraveni. Máš?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Sorry chlape, co nejdřív něco dalšího napíšu, teď jsem byl na pár dní pryč.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback