Daily Archives: 2007.03.5. 17:56

DefaultButton belülről

Valamikor régen ASP.NET 1.1 alatt nagyon sokat küzdöttünk azzal, hogy kezeljük azt az esetet, amikor a felhasználó valamelyik kontrollunkon állva rácsap az Enter gombra. Szerencsére a 2.0 verziótól kezdve ott van a DefaultButton tulajdonsága a HtmlFormnak és a Panelnek, ami látszólag megoldja ezt a problémát.

Sajnos előfordulhat, hogy az ember ezzel a hibaüzenettel találkozik:

The DefaultButton of ‘form1’ must be the ID of a control of type IButtonControl. smile_sarcastic

Kezdjünk el nyomozni, mi is történik! A nyomozás során természetes Lutz Roeder .NET Reflectorja játssza Watson doktor szerepét.

Panel

A Panel osztály AddAttributesToRender metódusában a következőt találjuk:

    Control button = this.FindControl( this.DefaultButton );
    if( !( button is IButtonControl ) )
    {
        throw new InvalidOperationException( SR.GetString( "HtmlForm_OnlyIButtonControlCanBeDefaultButton", 
new object[] { this.ID } ) ); } this.Page.ClientScript.RegisterDefaultButtonScript( button, writer, true );

A bökkenő a this.FindControlnál van, ez ugyanis általában csak a közvetlen gyermek elemeket fogja végignézni. Ha összetett kontrollunk van, akkor bizony nehezen fogja megtalálni benne a gombot. Nem baj, ássunk tovább:

A RegisterDefaultButtonScript a következő kódot rendeli a hivatkozott kontroll onkeypress eseményéhez:

    string text = "javascript:return WebForm_FireDefaultButton(event, '" + button.ClientID + "')";

HtmlForm

A HtmlForm osztály ugyanezt a metódust hívja meg, csak előtte máshogy készíti elő a terepet:

    Control button = this.FindControl( this.DefaultButton );
    if( ( button == null ) && ( this.Page != null ) )
    {
        char[] anyOf = new char[] { '$', ':' };
        if( this.DefaultButton.IndexOfAny( anyOf ) != -1 )
        {
            button = this.Page.FindControl( this.DefaultButton );
        }
    }
    if( !( button is IButtonControl ) )
    {
        throw new InvalidOperationException( SR.GetString( "HtmlForm_OnlyIButtonControlCanBeDefaultButton", 
new object[] { this.ID } ) ); } page.ClientScript.RegisterDefaultButtonScript( button, writer, false );

Itt már két FindControl találunk, és a Page szintű csak akkor hívódik meg, ha az első nem talál semmit és a DefaultButton értékeként megadott ID $ vagy : karaktereket tartalmaz. Ha ilyet szeretnénk, akkor kódból kell beállítanunk az értékét és a gomb UniqueID tulajdonságát kell használnunk. Az MSDN ezt kifejezetten ajánlja, ha master page-content page környezetben vagyunk.

Ez így bármennyire is logikusnak hangzik, a fenti megoldást nem sikerült működésre bírnom egy olyan Login kontroll esetén, amelynek renderelését CSS control adapter határozta meg. Ennek pedig az az oka, hogy a FindControl a vezérlő hierarchiát nézi végig, nem pedig a keletkezett kódot, a control adapterek pedig nem küzdenek a vezérlő fa felépítésével, hanem csak generálják a HTML-t.

Mi lett a megoldás? Az én esetemben megoldást (HACK!-et) jelentett, hogy a WebForm_FireDefaultButton javascript függvényt közvetlenül hozzáadtam egy DIV elemhez. Igen, ez nem szép…

 

Technorati tags: ,

ASP.NET control adapterek

Aki már futott bele abba a problémába, hogy az ASP.NET miért ilyen vagy olyan HTML kódot generál és utánanézett, hogy mindezt miért és hogyan teszi, akkor biztosan találkozott már az ASP.NET control adapterek fogalmával.

A lényeg, hogy bár az ASP.NET vezérlőelemek tudják, hogyan kell magukból HTML-t generálni, ez a folyamat egy saját control adapter készítésével felüldefiniálható. Első lépésként készítsünk egy saját osztályt, ami a System.Web.UI.WebControls.Adapters.WebControlAdapter ősosztályból származik, majd írjuk felül a RenderBeginTag, RenderChildren és RenderEndTag metódusokat. Ezeket a rendszer ebben a sorrendben hívja meg nyító- és záró tag, valamint a tartalom generálásakor. Ha figyelembe szeretnénk venni, hogy éppen milyen böngészőben jelenik meg a kódunk, használjuk bátran a Browser tulajdonságot, ha pedig azt a kontrollt szeretnénk elérni, amelynek a kódját gyártjuk éppen, akkor a Control tulajdonság lesz a barátunk. Miután megvagyunk az osztállyal, hozzunk létre egy .browser kiterjesztésű fájlt az App_Browser mappában. Ebben az XML-ben azt kell megadnunk, hogy milyen böngésző esetén melyik kontroll típust melyik kontroll adapter alakítja HTML-lé.

Aki többre kíváncsi, olvassa el az Adaptive Control Behavior fejezetet az MSDN-en, tanulmányozza a browser fájl sémáját vagy nézze meg Fritz Onion példáját az MSDN Magazine-ban.

Persze mindez nem lenne elég izgalmas, hiszen ezzel még csak ott vagyunk, ahol a part szakad, írhatjuk magunk a HTML-t. Sőt, generálhatjuk kódból, ami még rosszabb! A jó hír, hogy a munka nagyját elvégezték már helyettünk és CSS Friendly Control Adapters 1.0 néven le is tölthető az asp.net honlapról. Aki a képes beszámolót szereti, annak megint csak Scott Guthrie-t tudom ajánlani.

Miután letöltöttük a fájlokat, némi másolgatás és konfigurálás után a következő vezérlőink nem TABLE elemekkel fognak pozícionálni, hanem DIV-eket használnak, ahol kell és jó sok CSS classt használnak, hogy szinte mindent beállíthassunk kívülről: Menu, TreeView, DetailsView, FormView, GridView, DataList, Login, ChangePassword, PasswordRecovery, CreateUserWizard és LoginStatus.

Sőt, aki akarja, CSSesítheti például a SharePointot is, ehhez Patrick Tisseghem és Tam Tam Frank írtak útmutatót.

A módszer tehát jó és örülhetünk, hogy van implementáció is. Azonban vigyázat, az implementáció nem tökéletes! Egy darabig nagyon hittem ebben a csomagban, de mikor megláttam, hogy a forráskódban nem egy helyen bele van drótozva, hogy mi legyen egy IMG-hez tartozó ALT attribútum, vagy egy A taghez tartozó TITLE attribútum értéke, kicsit elment a kedvem és a bizalmam. Tudom, hogy nem minden vezérlőnek van meg minden tulajdonsága, de akkor is elég ciki, hogy az ember megcsinálja a Login kontrollt szépen lokalizálva, a felhasználó pedig a regisztrációs link fölé viszi az egeret, ahol tooltipben megjelenik, hogy Create user. Szerencsére a Membership vezérlőket lehet template-esíteni és ott olyan layoutot csinálunk, amilyet csak szeretnénk, de ugyanez a szabadságunk nincs meg a Menunél. Ott – még – nem láttam hasonló csúnyaságot.

Szóval örüljünk ennek a lehetőségnek, de ne bízzunk benne vakon!

 

Technorati tags: ,