Daily Archives: 2009.01.25. 17:51

Nagy méretű weblapok fotózása – Webpage Capture

Akinek kellett már dokumentálás vagy UI prototípus egyeztetés céljából weblapról képernyőképet készítenie, az tudja, hogy a feladat nem minden esetben egyszerű. Akkor nincs gond, ha az oldal kifér a képernyőre, de mi van akkor, ha függőlegesen csak három képernyőnyi scrollozás után lehet az oldal aljára érni? Ebben az esetben marad Nikhil Web Development Helpere vagy a print screenek egymás alá ragasztgatása. Mivel az előbbi elkezdett nem működni nálam, kénytelen voltam írni egy célalkalmazást.

Mivel évente kb. egy vastag kliens alkalmazást írok, úgy látszik ez lesz az idei termés, íme a Webpage Capture:

Webpage Capture

És íme a kimenet, egy fotó a Weblaborról 976 x 3565 pixel méretben:

WebpageCapture: Weblabor

Bár az alkalmazás egészen pofásra sikerült (a saját gyermek ugye 🙂 ) és használhatóan is működik, azért van egy apró szépséghibája: mivel nem akartam P/Invoke-t használni, ezért inkább egy nem támogatott megoldást választottam a képernyőképek elkészítésére. A WebBrowser kontrollnak van egy DrawToBitmap metódusa, amely bár kiválóan működik, “supports the .NET Framework infrastructure and is not intended to be used directly from your code”.

A másik lényeges pont, hogy a DrawToBitmap azt rajzolja a bitmapbe, amit a WebBrowser kontroll éppen mutat, ezért a rajzolás előtt át kell méretezni a kontrollt akkorára, amekkora a weboldal. A dokumentum mérete pedig a WebBrowser.Document.Body.ScrollRectangle tulajdonságból derül ki.

Lényegében ennyi kódolás kellett a fenti alkalmazás elkészítéséhez, a többi sallang, ami viszont igazán kényelmesen használhatóvá tette, szinte összekattintgatható: a böngészőben korábban használt URL-ek felkínálása és automatikus kiegészítése, az utoljára használt mappa, URL és checkbox állapot felhasználónkénti elmentése, a mentési útvonal tallózása, a beágyazott erőforrásként tárolt ikonokkal ellátott gombok, progress bar és link a status barra, na és persze mindez egy kattintással admin jogok nélkül a webről települve és önmagát frissítve ClickOnce segítségével. Azt kell hogy mondjam, igazán jó dolgok vannak a Windows Formsban, amiket én fontosabbnak ítélek, mint a 3D-ben forgazható grafikonokat 🙂

Aki szeretné kipróbálni az alkalmazást, ide kattintson:

http://www.msdnkk.hu/Storage/balassy/Enclosures/WebpageCapture

Known issue: bizonyos weblapok (pl. index) esetén többször készül el a kimeneti fájl, mert valamilyen okból az oldal letöltését jelző esemény többször sül el. Mindez persze csak akkor idegesítő, ha a View after capture be van kapcsolva. Megoldás: kapcsoljuk ki 🙂

 

Advertisements

Response.Redirect kliens oldalon

Számtalanszor előfordul, hogy egy oldal feldolgozása során a felhasználót át kell irányítanunk egy másik oldalra. Erre vannak jól bevált megoldások, a Response.Redirect, a Server.Transfer vagy a Server.Execute. Mindegyiknek megvan a maga előnye és hátránya, van azonban egy közös nagy hátrányuk, amit hajlamosak vagyunk elfelejteni.

Gyakran kell például Cancel jellegű gombot készítenünk, ami visszairányítja a felhasználót az előző oldalra, ilyenkor általában ezt a megoldást látom:

    <asp:Button ID="btnGo" runat="server" OnClick="btnGo_Click" Text="Vissza a Kezdőlapra" />

    protected void btnGo_Click( object sender, EventArgs e )
    {
        this.Response.Redirect( "Default.aspx" );
    }

Azaz egy szerver oldali gomb, szerver oldali eseménykezelővel. Érdemes megfigyelni, hogy nincs benne semmi olyan, ami szerver oldali feldolgozást igényelne. Az ilyen jellegű átirányításra lehetne egyszerű HTML <a> taget használni, de amikor ezt felvetem, általában azt a választ kapom, hogy a Cancel mellett van egy OK gomb is, ami viszont igenis igényli a szerver oldali logikát és hogy nézne ki, ha az egyik link, a másik pedig gomb lenne.

A Response.Redirectnek azonban van egy közismerten rossz tulajdonsága: kliens oldali átirányítást végez, azaz visszaküld egy HTTP Error 302-t (Object moved), mire a böngésző egy másik HTTP requestben kéri le a hivatkozott oldalt. Felesleges roundtrip, ami bizonyos esetekben elfogadható, a fentiben azonban semmiképpen.

Nosza, készítsünk egy egyszerű gombot, ami kliens oldalon irányít át! Ennyi az egész, csupasz HTML, semmi szerver oldali kód nincs benne:

    <input type="button" value="Vissza" onclick="window.location='Default.aspx';" />        

Ja, hogy ilyen vezérlő nincs a Toolboxon? “És akkor mi van, ember?!”

Csak egy kis JavaScript kell és még barátságosabb lesz a gombunk:

    <input type="button" value="Vissza" onclick="if( confirm('Biztosan?') ) window.location='Default.aspx';" />        

Sőt, ha csak egy Vissza gombra van szükségünk, akkor talán a history-val is próbálkozhatunk, hátha a böngésző kivágja az oldalt a gyorsítótárból és a szerverünk nem is kap kérést:

    <input type="button" value="Vissza" onclick="history.back()" />    

Ez a megoldás nem csak akkor működik, ha Cancel funkcióra van szükségünk, hanem akkor is, ha master-detail jellegű oldalakat készítünk, ahol az egyik listáz minden elemet, a másik oldal pedig egy kiválasztott elem részleteit mutatja. A részletes oldalt gyakran úgy oldjuk meg, hogy a kiválasztott elem azonosítóját query stringben adjuk át, tehát a lista oldalon úgy kell átirányítanunk, hogy az azonosító bekerüljön az URL-be. Ehhez sem kell szerver oldali kód, adakötéssel ez is megoldható:

    <asp:ListView runat="server" DataSourceID="sdsCustomers">
        <LayoutTemplate>
            <asp:PlaceHolder ID="itemPlaceholder" runat="server" />
        </LayoutTemplate>
        <ItemTemplate>
            <%# Eval( "CompanyName" ) %> - <%# Eval( "ContactName" ) %>
            <input type="button" value="Részletek" 
               onclick="<%# Eval( "CustomerID", "window.location='Customer.aspx?Id={0}';" ) %>" /> 
        </ItemTemplate>
        <ItemSeparatorTemplate>
            <br />
        </ItemSeparatorTemplate>
    </asp:ListView>
    
    <asp:SqlDataSource ID="sdsCustomers" runat="server" 
      ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
      SelectCommand="SELECT [CustomerID], [CompanyName], [ContactName] FROM [Customers] ORDER BY [CompanyName]">
    </asp:SqlDataSource>

Itt sem használok szerver oldali kódot, az adatkötés ugyanis runat=”server” nélkül is tud működni. Az aláhúzott kifejezésben az az érdekes, hogy az idézőjelek párosítása szemmel láthatóan rossz, mégis működik 🙂

Adott tehát a lehetőség: lustán és gyorsan olyan kódot írni, amely feleslegesen terheli a szervert és várakoztatja a felhasználót vagy némi kézimunkával mindenkinek a kedvére tenni.

Te melyik utat szoktad választani?

 

A cikkhez tartozó forráskód letölthető innen: ClientRedirectSampleWebSite.zip

Technorati Tags: ,