Tag Archives: WebForms

WebForms: Když nefunguje asynchronní AutoPostBack na RadioButtonech

Pokud bojujete s nefunkčním AutoPostBack na asp:RadioButton v ASP.NET WebForms při asynchronním postbacku, pak je to způsobeno tím, že ASP.NET nerenderuje k RadioButtonu označenému jako Checked klientskou obsluhu události onclick. Zřejmě je to by-design ochrana proti vícenásobné obsluze změn, tedy aby se událost vykonala pouze na jednom z radiobuttonů ve skupině, ne na tom označovaném i odznačovaném.

Možnosti řešení jsou tedy dvě:

  1. Zahrnout i RadioButtons do UpdatePanelu, aby se přerenderovaly a obsluhy událostí správně přenastavily.
  2. Obskurní work-around, který vyrenderuje obsluhu na všech RB taky může být
CisloSmlouvyNoveRB.InputAttributes["checked"] = "true";

CS1647: An expression is too long or complex to compile (workaround)

Na jednom z projektů jsme po úpravě ASPX stránky s enormním objemem html kódu narazili na zajímavou chybu zobrazenou v kompilátorem: CS1647: An expression is too long or complex to compile. Velmi pozoruhodný je též popis chyby na MSDN a doporučené řešení.

Jako funkční workaround zafungovalo doporučení z blogu henrywrites a sice vložit do ASPX stránky serverový komentář, například

<%
// workaround pro CS1647: An expression is too long or complex to compile
%>

ASP.NET WebForms Scaffolding – Slides a dema [TechEd Praha 2013]

Slides a dema z mé přednášky na TechEd DevCon Praha 2013:

Videozáznam z této přednášky nebyl pořizován.

Juice UI – ASP.NET WebForms Controls pro jQuery UI

Na první dnešní přednášce ASP.NET 4.5 na Microsoft MVP Summitu byla prezentována zajímavá nová knihovna, která přináší jQuery UI Widgets do prostředí ASP.NET WebForms – Juice UI. Protože je to jedna z mála věcí, které nejsou pod NDA, mohu se s Vámi o tento poznatek podělit.

Juice UI aspiruje na náhradu AJAX Control Toolkitu a zahrnuje například tyto controly:

  • Accordition
  • Autocomplete
  • Button
  • DatePicker
  • Dialog
  • Draggable
  • Position
  • ProgressBar
  • Resizable
  • Slider
  • Sortable
  • Tabs

Použití je přímočaré a nijak nekomplikované, např.:

<Juice:Tabs ID="_Default" runat="server">
  <Juice:TabPage Title="Tab 1" ID="_Tab1">
    <TabContent>
      ...
    </TabContent>
  </Juice:TabPage>
  <Juice:TabPage Title="Tab 2" ID="_Tab2">
    <TabContent>
      ...
    </TabContent>
  </Juice:TabPage>
</Juice:Tabs>

…uvidíme, jak se vypořádají s porodními bolestmi, které AJAX ControlToolkit měl. Jedná se o open-source knihovnu použitelnou pod licencemi MIT nebo GPLv2.

Detailní ASP.NET Request + WebForm Page LifeCycle diagram

Pokud se zabýváte technologií ASP.NET do hloubky, může se Vám hodit můj „ASP.NET 2.0/3.5 Request + Page LifeCycle Diagram“:

ASP.NET LifeCycle 2

Jedná se o první verzi mého diagramu, který hodlám dále graficky vylepšovat a zpřehledňovat. Až mi zbyde chvilka, dám sem i PDF verzi k tisku. Stejnětak je možné, že jsem v něm někde udělal chybu. Pokud tedy nějakou nesrovnalost objevíte, dejte mi prosím vědět.

Červeně jsou označena místa, kde se lze zapojit s vlastní invencí, jde buď o události, virtuální metody, nebo adapter. Modře jsou vyznačeny interface pro implementaci dané funkčnosti a šedě jsou interní implementace ASP.NET.

Viz též

Detekce ztráty ViewState při použití SessionPageStatePersisteru

Pokud směřujete ViewState do Session pomocí SessionPageStatePersisteru, pak se dříve nebo později setkáte s problémem ztrát ViewState a s potřebou detekovat a řešit tuto situaci.

Ke ztrátě ViewState může dojít v následujících případech:

  • v případě zániku/vyčištění Session, zejména při jejím timeoutování při nečinosti uživatele – uživatel si například nechá dlouho otevřenou stránku, vyprší mu mezi tím session, následně provede postback a problém je na světě, došlý postback nemá na serveru už ViewState,
  • v případě ztráty InProc Session díky restartování aplikace (deployment nové verze, recyklace poolu, atp.)
  • v případě vypadnutí stránky z fronty SessionPageStatePersisteru – persister omezuje totiž počet uložených ViewState a při uložení dalšího nejstarší vypadávají. Výchozí délka fronty je 9, je možné ji přenastavit na větší ve web.configu elementem sessionPageState, atributem historySize
    • pokud si například uživatel otevře současně 10 oken aplikace, pak pokud se vrátí k prvnímu oknu a provede postback, opět chybí ViewState,
    • pokud uživatel používá tlačítko Zpět browseru, např. pokud máme na stránce grid a každý řádek má na sobě tlačítko Detail, jehož obsluha události dělá redirect na stránku s detaily. Takový uživatel pokud klikne na detail prvního řádku, pak Zpět, druhého řádku, Zpět, … kliknutím na detail desátého řádku už způsobený postback na serveru nemá svůj ViewState (devět ViewState detailů vytlačilo z fronty ViewState stránky s gridem) a problém je zase na světě.

Jak takovou situaci detekovat? …overridováním metody LoadPageStateFromPersistenceMedium() v našich stránkách (obvykle pomocí společného předka všech stránek, nějakého PageBase):

protected override object LoadPageStateFromPersistenceMedium()
{
    // base implementace vrací new Pair(pageStatePersister.ControlState, pageStatePersister.ViewState);
    object pageState = base.LoadPageStateFromPersistenceMedium();

    // ztrátu ViewState můžeme tedy detekovat jako (pair.Second == null)
    // metoda LoadPageStateFromPersistenceMedium se volá jen pro IsPostBack
    Pair pair = pageState as Pair;
    if ((pair != null) && (pair.Second == null))
    {
        Trace.Warn("ViewStateLost !!!");

        FormsAuthentication.RedirectToLoginPage();
    }

    return pageState;
}

Základní (base) implementace metody LoadPageStateFromPersistenceMedium() totiž nedělá celkem nic jiného, než že z použitého persisteru získá ControlState, ViewState a udělá z nich Pair.

Situaci můžeme řešit například tím, že uživatele požádáme o nové přihlášení, či provedeme redirect kamsi do rootu nebo sami na sebe. ViewState už nikde nevydolujeme, musíme z toho nějak vybruslit, aniž bychom uživatele obšťastnili výjimkou NullReferenceException, InvalidFormatException a jinými podobnými, které v takových situacích nastávají.

Pozor na Response.Redirect(), Response.End() a obsluhu výjimek

Jak myslíte, že dopadne následující příklad po kliknutí na tlačítko? (Stránka obsahuje jen label MyLabel a button MyButton)

public partial class _Default : System.Web.UI.Page 
{
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        MyLabel.Text = (string)Session["OK"];
    }

    void MyButton_Click(object sender, EventArgs e)
    {
        try
        {
            Session["OK"] = "ok";
            Response.Redirect("~/");
        }
        catch
        {
            Session["OK"] = "exception";
        }
    }
}

Mnohé z Vás asi překvapím, když řeknu, že do Session[„OK“] se uloží „exception“ a ten se v dalším requestu i zobrazí.

Response.Redirect(), resp. metoda Response.End(), kterou Redirect sám volá, totiž funguje tak, že vyvolá ve webové aplikaci interní výjimku (Thread.CurrentThread.Abort()), která je samotnou webovou aplikací zpracovávána tak, aby bylo dosaženo kýženého efektu, tj. aby se vykonávání kódu zastavilo v daném místě a další kód se nevykonal (resp. tato ThreadAbortException se na konci catch-bloků vyvolává znovu a jsou vykonány všechny příslušné catch/finally bloky a tedy např. i Page.OnUnload()).

Potíž však nastane v okamžiku, kdy sami obalíme volání Response.Redirect() či Response.End() zachytáváním výjimek a nespecifikujeme dostatečně typ výjimek, které chceme zachytávat. Pokud necháme chytat výjimky všechny, uvedeme jako typ Exception, pak se dočkáme nežádoucího efektu, kdy nám volání Response.Redirect()/End() způsobí vykonání obsluhy výjimky, blok catch.

Východiskem je tedy obsluhovat pouze specifické typy výjimek, tak, jak to ostatně obecné guidelines doporučují pro všechny situace.