DCSIMG
May 2007 - Posts - Justin myJustin = new Justin( Expriences.Current );

May 2007 - Posts

Question from Tapuz .Net forum: How does UpdatePanel work with ViewState?

שאלה:

 משהו לא ברור לי איך UpdatePanel עובד עם ViewState.

אם ב-ViewState נשמר המצב של פקדי ה-Web שלנו, ואנחנו משנים את המצב הזה במהלך ריצת הדף, איך זה שה-ViewState חוזר לשרת אין חזרה למצב המקורי?

 

תשובה:

 בואו נבין את השאלה.

ניצור דף ASP.Net רגיל לכל דבר עם Label בעל הטקסט Hello World.

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!this.IsPostBack)

            Label1.Text = "hello world2";

    }

נביט על ה-Source של הדף.

 

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

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
   Untitled Page
</title></head>
<body>
    <form name="form1" method="post" action="Default11.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJODExMDE5NzY5D2QWAgIDD2QWAgIBDw8WA
h4EVGV4dAUMaGVsbG8gd29ybGQyZGRkgQasNLsHDgqB8VeiQz1R8R14xHE="
/>
</div> <div> <span id="Label1">hello world2</span> </div> </form> </body> </html>

 

 נשים לב שקיבלנו ערך נסתר בשם VIEWSTATE__ שמייצג את ה-ViewState של הדף.
ה-ViewState זאת אותה חיה מסתורית שחיה במעמקי ASP.Net ודואגת לטעון ערכים מחדש אחרי PostBack.

לאלה מאתנו שאינם קוראים קידוד 64 ביט לא מוצפן שוטף, ניקח את ה-ViewState הזה ונפרש אותו באמצעות תוכנה.
נשתמש ב-ViewState Decoder גירסה 2.1. נעתיק לתוכו את ה-ViewState שקיבלנו וזה מה שנראה:

 אפשר לראות ש-ViewState מורכב מ-Pair ו-ArrayList שמייצגים כנראה איזה היררכית פקדים. (בנוסף גם קיים Triplet שלא ראינו כאן).

נוסיף לדף שלנו כפתור שרק גורם ל-PostBack בלי שום דבר מיוחד. ונלחץ עליו.

הפלא ופלא, אחרי לחיצה על הכפתור, ריצה לשרת (שבא נזכר בזה שהשרת לא מחזיר את הערך לתוך Label באמצעות ה-Page_load) וחזרה ללקוח - יש עדיין את הטקסט hello world2.

איפה הערך הזה נשמר? בתוך ה-ViewState שראינו, והשרת בריצה בחזרה אליו יודע להחזיר את הערך לתוך Label1.Text.

עכשיו בואו נוסיף UpdatePanel לדף, נגרום לכפתור שלנו ליצור PostBack שקט שיעדכן את ערך ה-Label.

 

// in myPage.aspx.cs

public partial class Default11 : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!this.IsPostBack)

            lblText.Text = "hello world2";

    }

    protected void btnSilentPostBack_Click(object sender, EventArgs e)

    {

        lblText.Text = "I was changed by UpdatePanel";

    }

}

 

// in myPage.aspx

 

    <form id="form1" runat="server">

        <div>

            <asp:ScriptManager ID="ScriptManager1" runat="server" />

 

            <asp:UpdatePanel ID="UpdatePanel1" runat="server">

 

                <ContentTemplate>

                    <asp:Label ID="lblText" runat="server" />

                </ContentTemplate>

 

                <Triggers>

                    <asp:AsyncPostBackTrigger ControlID="btnSilentPostBack" EventName="Click" />

                </Triggers>

 

            </asp:UpdatePanel>

 

            <asp:Button ID="btnSilentPostBack" runat="server" Text="Cause Silent PostBack" OnClick="btnSilentPostBack_Click" /><br/>

            <asp:Button ID="btnPostBack" runat="server" Text="Cause PostBack" OnClick="btnSilentPostBack_Click" />

        </div>

    </form>

אין פה שום דבר מיוחד מדי, יש לנו Label, שבלחיצה על הכפתור מתבצע PostBack שקט והטקסט של ה-Label בתוך ה-UpdatePanel מתעדכן.

בטעינת הדף נראה: (שלב א')

לאחר לחיצה על Silent PostBack נקבל (בלי רפרוש מלא של הדף) (שלב ב')

ועכשיו נלחץ על Cause PostBack שיגרום ריצה מלאה לשרת וחזרה ללקוח (שלב ג')

OK, ההתנהגות הגיונית. זה הרי מה שרצינו שיקרה.

אבל, בואו נביט על ה-ViewState לאחר שלב ב' (אחרי הלחיצה על ה-Silent PostBack)

 

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJODI0NTE
2NjUzD2QWAgIDD2QWAgIDD2QWAmYPZBYCAgEPDxYCHgRUZXh0BQxoZWxsbyB3b3JsZDJkZGTeg0TK+W
J21pvQbStDq55G3LB7CQ==/>

נפרש את זה:

בואו נבין מה אנחנו רואים כאן עכשיו - אחרי ה-Slient PostBack ה-ViewState הכתוב ב-Source של הדף הוא עדיין אותו ViewState בטעינת הדף. (הרי הדף לא מתפרשרש)

אז איך השרת יודע אחרי ה-PostBack האמיתי והמלא שהערך של ה-Text של ה-Label הוא "I was changed by postback"?

נתקין תוכנה קטנה בשם Fiddler מבית מיקרוסופט שמטרתה היא לעקוב אחרי ה-Request וה-Response מהדפדפן.

נבצע את שלב א' עוד פעם (נטען את הדף מחדש).

 

אנחנו רואים בפינה הימנית העליונה את הבקשה לשרת לדף נקי וחדש.
בפינה הימנית התחתונה נראה את התשובה של השרת.
בצד שמאל נראה אינדקס של כל הבקשות שבוצעו לשרת (אחת לדף ואחד לקובץ סקריפט חיצוני).

כעת, נלחץ על כפתור ה-Cause Silent PostBack שלנו ונראה את הבקשה והתשובה.

 

נקרא מה כתוב בפינה הימנית העליונה - לשרת נשלחת בקשה בצירוף ה-ViewState הנוכחי של הדף.
ה-ViewState שנשלח לשרת עבר UrlEncode (בדומה ל-QueryString) למעוניינים אפשר להעתיק את הערך לתוך המתודה Server.UrlDecode ולקבל את ה-ViewState. אם נביט על הערך של ה-ViewState שנשלח לשרת נראה:

נביט על הפינה הימנית התחתונה של Fiddler ונראה שקיבלנו בחזרה HTML שיוצג בתוך ה-UpdatePanel וגם ViewState כלשהו.
נפרש את ה-ViewState שהשרת מחזיר ונראה:

אז כנראה שהשרת מקבל את ה-ViewState המקורי ומחזיר את ה-ViewState של הדף אחרי השינוי שבוצע בשרת!

בואו גם נביט על ה-ViewState שבסופו של דבר נשלח לשרת אחרי PostBack מלא.

אם נפרש את ה-ViewState שנשלח לשרת ב-PostBack המלא נראה.

אז מה מסתבר? לשרת גם נשלח ה-ViewState המעודכן!

 

בואו נחזור על השלבים כפי שראינו אותם

  1. טעינת הדף - כל ה-ViewState כתוב כפי שהוא נשלח מהשרת.
  2. PostBack שקט - לשרת נשלח ה-ViewState המקורי והשרת מחזיר לנו את ערך ה-ViewState המעודכן.
  3. PostBack מלא - לשרת נשלח ה-ViewState המעודכן.

 

מה מסתבר? ש-Microsoft AJAX מקבל בחזרה תשובה מ-UpdatePanel הוא מעדכן את ה-ViewState של הדף.

כל ה-ViewState נשלח לשרת, וכל ה-ViewState נשלח בחזרה ללקוח, עם כל רפרוש של ה-UpdatePanel.

 

(חשוב להדגיש שהדוגמה כאן היא להמחשה בלבד על איך UpdatePanel מטפל ב-ViewState.
במצב הזה של עדכון Label יש פתרונות עדיפים מאשר לערב UpdatePanel שהוא פתרון מהיר אך בעל עלויות גבוהות כפי שראינו).

Question from Tapuz .Net forum: How to align RTL (Right-to-left) ASP.Net 2.0 Menu with CSS Control Adapters

שאלה:

 אני רוצה תפריט מגניב ב-ASP.Net 2.0 שיהיה מותאם לתצוגה בעברית.

התפריט צריך להיות דינמי (להציג את החלקים הרלוונטיים בתפריט רק לפי מיקום העכבר על ההורה) וצריך להיות כמה שיותר סטדנרטי.

למישהו יש רעיונות?

 

תשובה:

נתחיל בזה שנשתמש בפקד ה-Menu החדש ב-ASP.Net 2.0.

נגרור פקד Menu לדף שלנו.

עכשיו נרצה לקבוע לו מקור מידע (איזה לינקים להציג בתפריט). נפתח את ה-Quick Tasks ונבקש להגדיר מקור מידע חדש.

נבחר שאנחנו רוצים SiteMap חדש (שהוא חלק מה-Provider Model החדש בדוט נט 2.0).

בחירה זו תיצור לנו קובץ web.sitemap חדש בתיקיית האתר.

נכניס לתוך הקובץ web.sitemap שלנו את התוכן הבא שיכיל פרקים, עמודים ופסקאות בספר. 

<?xml version="1.0" encoding="utf-8" ?>

<siteMap>

    <siteMapNode title="Home" url="~/home.aspx" description="Go To Home">

        <siteMapNode title="Chapter 1" url="~/book1/chapter1/chapter1.aspx" description="Go To Chapter 1">

            <siteMapNode title="Page 1" url="~/book1/chapter1/page1/page1.aspx" description="Go To Page 1">

                <siteMapNode title="Paragraph 1" url="~/book1/chapter1/page1/paragraph1.aspx" description="Go To Paragraph 1"/>

                <siteMapNode title="Paragraph 2" url="~/book1/chapter1/page1/paragraph2.aspx" description="Go Paragraph 2"/>

                <siteMapNode title="Paragraph 3" url="~/book1/chapter1/page1/paragraph3.aspx" description="Go Paragraph 3"/>

            </siteMapNode>

            <siteMapNode title="Page 2" url="~/book1/chapter1/page2/page2.aspx" description="Go To Page 2">

                <siteMapNode title="Paragraph 1" url="~/book1/chapter1/page2/paragraph1.aspx" description="Go To Paragraph 1"/>

                <siteMapNode title="Paragraph 2" url="~/book1/chapter1/page2/paragraph2.aspx" description="Go Paragraph 2"/>

                <siteMapNode title="Paragraph 3" url="~/book1/chapter1/page2/paragraph3.aspx" description="Go Paragraph 3"/>

            </siteMapNode>

        </siteMapNode>

        <siteMapNode title="Chapter 2" url="~/book1/chapter2/chapter2.aspx" description="Go To Chapter 2">

            <siteMapNode title="Page 1" url="~/book1/chapter2/page1/page1.aspx" description="Go To Page 1">

                <siteMapNode title="Paragraph 1" url="~/book1/chapter2/page1/paragraph1.aspx" description="Go To Paragraph 1"/>

                <siteMapNode title="Paragraph 2" url="~/book1/chapter2/page1/paragraph2.aspx" description="Go Paragraph 2"/>

                <siteMapNode title="Paragraph 3" url="~/book1/chapter2/page1/paragraph3.aspx" description="Go Paragraph 3"/>

            </siteMapNode>

            <siteMapNode title="Page 2" url="~/book1/chapter2/page2/page2.aspx" description="Go To Page 2">

                <siteMapNode title="Paragraph 1" url="~/book1/chapter2/page2/paragraph1.aspx" description="Go To Paragraph 1"/>

                <siteMapNode title="Paragraph 2" url="~/book1/chapter2/page2/paragraph2.aspx" description="Go Paragraph 2"/>

                <siteMapNode title="Paragraph 3" url="~/book1/chapter2/page2/paragraph3.aspx" description="Go Paragraph 3"/>

            </siteMapNode>

        </siteMapNode>

        <siteMapNode title="Chapter 3" url="~/book1/chapter3/chapter3.aspx" description="Go To Chapter 3">

            <siteMapNode title="Page 1" url="~/book1/chapter3/page1/page1.aspx" description="Go To Page 1">

                <siteMapNode title="Paragraph 1" url="~/book1/chapter3/page1/paragraph1.aspx" description="Go To Paragraph 1"/>

                <siteMapNode title="Paragraph 2" url="~/book1/chapter3/page1/paragraph2.aspx" description="Go Paragraph 2"/>

                <siteMapNode title="Paragraph 3" url="~/book1/chapter3/page1/paragraph3.aspx" description="Go Paragraph 3"/>

            </siteMapNode>

            <siteMapNode title="Page 2" url="~/book1/chapter3/page2/page2.aspx" description="Go To Page 2">

                <siteMapNode title="Paragraph 1" url="~/book1/chapter3/page2/paragraph1.aspx" description="Go To Paragraph 1"/>

                <siteMapNode title="Paragraph 2" url="~/book1/chapter3/page2/paragraph2.aspx" description="Go Paragraph 2"/>

                <siteMapNode title="Paragraph 3" url="~/book1/chapter3/page2/paragraph3.aspx" description="Go Paragraph 3"/>

            </siteMapNode>

        </siteMapNode>

    </siteMapNode>

</siteMap>

נבצע מספר שינויים בדף:

1. נקבע את ה-Orientation של ה-Menu ל-Horizontal (כלומר שאנחנו רוצים שכל התפריט יהיה אופקי ולא אנכי).

2. נקבע שהתפריט לא יציג את ה-Node הראשון (כי נרצה שהתפריט שלנו יראה רשימת פרקים בלי דף הבית) בכך שנשנה את ה-ShowStartingNode של ה-SiteMapDataSource ל-false.

ככה נראה הקוד של הדף שלנו עכשיו:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default2.aspx.vb" Inherits="Default2" %>

 

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

 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Untitled Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Menu ID="Menu1" runat="server"

        DataSourceID="SiteMapDataSource1"

        Orientation="Horizontal">

        </asp:Menu>

 

        <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server"  ShowStartingNode="false"/>

 

    </div>

    </form>

</body>

</html>

 

ואם נבחר לראות את התצוגה של הקוד הזה בדף, נקבל:

זה בערך גבול הקוסטימזציה שאפשר לעשות ל-Menu מבחינת עיצוב.

נכניס לתערובת עכשיו שחקן חדש - ASP.Net CSS Control Adapters.

מדובר על מוצר מדף נפרד שמתלבש על VIsual Studio 2005 ונותן לנו יכולות נוספות לעיצוב Server Controls.

אפשר להוריד את הקובץ התקנה מכאן - http://msdn.microsoft.com/vstudio/eula.aspx?id=%20096D9643-2597-4a3b-82E3-8863145E4A3D.

נתקין ונפתח Web Site חדש, מסוג ASP.Net CSS Friendly Web Site.

 נעתיק לתוך הפרוייקט את הדף הקיים שלנו.

עכשיו נרצה לעשות מספר שינויים בדף:

1. נרצה שהוא יתמוך בעברית. ולכן נשנה את תגית ה-<body> לכך שתתמוך כברירת מחדל בסידור עברי.

<body style="direction:rtl">

2. נרצה שהדף שלנו יתמוך בכל התוספות המעניינות של CSS שנקבל מהסוג פרוייקט הייחודי הזה. נוסיף הפנייה לקובץ CSS ראשי של ה-CSS Control Adapters שגורם להכלה של כל קבצי ה-CSS הרלוונטיים.

<head runat="server">

    <title>Untitled Page</title>

 

    <link runat="server" rel="stylesheet" href="~/CSS/Import.css" type="text/css" id="AdaptersInvariantImportCSS" />

</head>

3. בנוסף, נרצה תמיכה בעיצוב לדוגמה שמגיע עם הסוג הפרוייקט אז נוסיף הפנייה לקובץ CSS נוסף שמכיל מאפיינים סצפיפיים לעיצוב ספציפי של התפריט.

<head runat="server">

    <title>Untitled Page</title>

 

    <link rel="stylesheet" href="~/WalkThru/SimpleMenu.css" type="text/css" />

    <link runat="server" rel="stylesheet" href="~/CSS/Import.css" type="text/css" id="AdaptersInvariantImportCSS" />

</head>

4. עשינו את כל זה הרי כדי להכיל איזהשהו הגדרת CSS לא ברורה על התפריט שלנו. אז נשנה את ה-CssSelectorClass של התפריט ל-SimpleEntertainmentMenu. עוד שנייה נסביר מה זה אומר.

        <asp:Menu ID="Menu1" runat="server"

        DataSourceID="SiteMapDataSource1"

        Orientation="Horizontal"

        CssSelectorClass="SimpleEntertainmentMenu">

        </asp:Menu>

סה"כ הדף שלנו נראה עכשיו ככה:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default2.aspx.vb" Inherits="Default2" %>

 

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

 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Untitled Page</title>

 

    <link rel="stylesheet" href="~/WalkThru/SimpleMenu.css" type="text/css" />

    <link runat="server" rel="stylesheet" href="~/CSS/Import.css" type="text/css" id="AdaptersInvariantImportCSS" />

</head>

<body style="direction:rtl">

    <form id="form1" runat="server">

    <div>

        <asp:Menu ID="Menu1" runat="server"

        DataSourceID="SiteMapDataSource1"

        Orientation="Horizontal"

        CssSelectorClass="SimpleEntertainmentMenu">

        </asp:Menu>

 

        <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server"  ShowStartingNode="false"/>

 

    </div>

    </form>

</body>

</html>

עכשיו נדבר לרגע על ה-CssSelectorClass הזה, ואולי באמת הגיע הזמן שנסביר מה נותן לנו ה-Css Control Adapters המוזרים האלו.

 

יש בעיה עם Server Controls מורכבים (כגון, Menu, Treeview, GridView, DataList, Login, PasswordRecovery ועוד כמה).
הבעיה היא בעיית CSS - אין לנו היום נגישות לשנות את ה-CSS של כל האלמנטים הקטנים יותר שמרכיבים אותם פקדים מורכבים.
התפריט שלנו למשל מכיל כל מיני אלמנטים גרפיים שונים: חלק גרפי שמייצג את הרמה הראשונה, חלק גרפי שמייצג את הראשונה השנייה בתפריט, הרמה השלישית, כך הלאה וגם את העלים שאין להם בנים.

לכל האלמנטים האלו - אין לנו דרך ליצור היום עיצוב יחודי מלבד המעט שנחשף דרך ה-Server Control עצמו.

ה-CSS Control Adapters חושפים לנו סט נוסף של מאפייני (גם באנגלית: Properties) של CSS שנוכל לשנות. 

איך הוא עושה את ההוספה הזאת, מה המנגנון שעומד מאחוריו (Control Adapters), וכך הלאה למרות שזה מאוד מעניין לא נדבר במאמר הזה.

 

נחזור ל-CssSelectorClass, בפועל זה שם מחלקת ה-CSS היחודיית של שנרצה לתת לפקד שלנו.

עם האתר שיצרנו נוצרו כמה קבצי CSS לדוגמה ואחד מהם מכיל מחלקה בשם SimpleEntertainmentMenu. (ואל הקובץ CSS הזה גם הוספנו הפנייה).

 

הגיע הזמן לשנות את המידע של התפריט ככה שהוא יהיה בעברית:

<?xml version="1.0" encoding="utf-8" ?>

<siteMap>

    <siteMapNode title="Home" url="~/home.aspx" description="Go To Home">

        <siteMapNode title="פרק 1" url="~/book1/chapter1/chapter1.aspx" description="Go To פרק 1">

            <siteMapNode title="עמוד 1" url="~/book1/chapter1/page1/page1.aspx" description="Go To עמוד 1">

                <siteMapNode title="פסקה 1" url="~/book1/chapter1/page1/paragraph1.aspx" description="Go To פסקה 1"/>

                <siteMapNode title="פסקה 2" url="~/book1/chapter1/page1/paragraph2.aspx" description="Go פסקה 2"/>

                <siteMapNode title="פסקה 3" url="~/book1/chapter1/page1/paragraph3.aspx" description="Go פסקה 3"/>

            </siteMapNode>

            <siteMapNode title="עמוד 2" url="~/book1/chapter1/page2/page2.aspx" description="Go To עמוד 2">

                <siteMapNode title="פסקה 1" url="~/book1/chapter1/page2/paragraph1.aspx" description="Go To פסקה 1"/>

                <siteMapNode title="פסקה 2" url="~/book1/chapter1/page2/paragraph2.aspx" description="Go פסקה 2"/>

                <siteMapNode title="פסקה 3" url="~/book1/chapter1/page2/paragraph3.aspx" description="Go פסקה 3"/>

            </siteMapNode>

        </siteMapNode>

        <siteMapNode title="פרק 2" url="~/book1/chapter2/chapter2.aspx" description="Go To פרק 2">

            <siteMapNode title="עמוד 1" url="~/book1/chapter2/page1/page1.aspx" description="Go To עמוד 1">

                <siteMapNode title="פסקה 1" url="~/book1/chapter2/page1/paragraph1.aspx" description="Go To פסקה 1"/>

                <siteMapNode title="פסקה 2" url="~/book1/chapter2/page1/paragraph2.aspx" description="Go פסקה 2"/>

                <siteMapNode title="פסקה 3" url="~/book1/chapter2/page1/paragraph3.aspx" description="Go פסקה 3"/>

            </siteMapNode>

            <siteMapNode title="עמוד 2" url="~/book1/chapter2/page2/page2.aspx" description="Go To עמוד 2">

                <siteMapNode title="פסקה 1" url="~/book1/chapter2/page2/paragraph1.aspx" description="Go To פסקה 1"/>

                <siteMapNode title="פסקה 2" url="~/book1/chapter2/page2/paragraph2.aspx" description="Go פסקה 2"/>

                <siteMapNode title="פסקה 3" url="~/book1/chapter2/page2/paragraph3.aspx" description="Go פסקה 3"/>

            </siteMapNode>

        </siteMapNode>

        <siteMapNode title="פרק 3" url="~/book1/chapter3/chapter3.aspx" description="Go To פרק 3">

            <siteMapNode title="עמוד 1" url="~/book1/chapter3/page1/page1.aspx" description="Go To עמוד 1">

                <siteMapNode title="פסקה 1" url="~/book1/chapter3/page1/paragraph1.aspx" description="Go To פסקה 1"/>

                <siteMapNode title="פסקה 2" url="~/book1/chapter3/page1/paragraph2.aspx" description="Go פסקה 2"/>

                <siteMapNode title="פסקה 3" url="~/book1/chapter3/page1/paragraph3.aspx" description="Go פסקה 3"/>

            </siteMapNode>

            <siteMapNode title="עמוד 2" url="~/book1/chapter3/page2/page2.aspx" description="Go To עמוד 2">

                <siteMapNode title="פסקה 1" url="~/book1/chapter3/page2/paragraph1.aspx" description="Go To פסקה 1"/>

                <siteMapNode title="פסקה 2" url="~/book1/chapter3/page2/paragraph2.aspx" description="Go פסקה 2"/>

                <siteMapNode title="פסקה 3" url="~/book1/chapter3/page2/paragraph3.aspx" description="Go פסקה 3"/>

            </siteMapNode>

        </siteMapNode>

    </siteMapNode>

</siteMap>

וככה יראה הדף שלנו עכשיו בתצוגה:

אז, יש בעיה. אפשר לראות שהחצים הקטנים נמצאים בצד ימין (על הטקסט בכל הרמות שהן לא עלים), והילדים (הפסקאות) נפתחות לצד ימין ולא לצד שמאל.

נלך לקובץ SimpleMenu.css ונשנה את ההגדרה הבאה:

  /* Tier 3+ */

.SimpleEntertainmentMenu ul.AspNet-Menu ul ul

{

    top: -0.5em;   

    left: 6em;

}

נשנה ל-

  /* Tier 3+ */

.SimpleEntertainmentMenu ul.AspNet-Menu ul ul

{

    top: -0.5em;   

    right: 6em;

}

נסביר מה זה ה-CSS המוזר הזה ומה השינוי הזה שעשינו.

ברור לנו ש-SimpleEntertainmentMenu זה שם המחלקה שלנו.

ul.AspNet-Menu זאת הקידומת לכל שינוי שנרצה ליצור לכל ילד בתוך פקד Menu של ASP.Net.
כל ul שנכתוב לאחר מכן יפרט את הרמה הבאה, כאשר האחרון הוא לכל הרמות שיבואו אחריו.
כך ש-ul.AspNet-Menu הוא לרמה ה-1, ה-ul.AspNet-Menu ul הוא לרמה השנייה וה-ul.AspNet-Menu ul ul הוא לרמה השלישית והיות והוא האחרון הוא לשלישית ואלו שיבואו אחריה.
יש עוד המון מחלקות CSS שנחשפות לנו (התא הנבחר, ילד שנבחר, עלה שנבחר וכך הלאה).

 

סה"כ אמרנו שאנחנו רוצים שהתאים מהרמה השלישית ומעלה יפתחו לצד הנגדי לצד שהם נפתחו עד עכשיו.

נשנה גם את השורת CSS הבאה בכל מקום שנראה אותו:

    background: transparent url(activeArrowRight.gif) right center no-repeat;

ל-

    background: transparent url(activeArrowRight.gif) left center no-repeat;

וכאן ביקשנו מהרקע שיציג את התמונה של החץ בצד שמאל במקום בצד ימין.

ובעקבות שני שינויי ה-CSS הקטנטנים האלו, נקבל תפריט דינמי שתומך בעברית.

 

 

עכשיו נחפור קצת על איזה עוד מחלקות CSS מעניינות נוכל לקנפג בתפריט.

נוכל לראות שיש שתי סוגי מחלקות לפקד ה-Menu: מחלקות לסידור אנכי (עם התחילית ul) ומחלקות לסידור אופקי (בלי תחילית).

את המחלקה ul כבר הכרנו וראינו שהיא מדברת על תא מסויים ברמה כמספר ה-ulים שמופיעים.

נוכל גם לראות את המחלקה AspNet-Menu-Hover שמאפשרת לנו לקבוע נתוני CSS לתא שעליו כרגע עומד העכבר.

גם נראה את המחלקה AspNet-Menu-Left שמאפשרת לנו לקבוע נתוני CSS לכל האלמנטים בתפריט ללא ילדים.

 

עומק הקורה ב-CSS Control Adapters הולך הרבה יותר עמוק:

  • נתמכים מספר פקדים נוספים של ASP.Net 2.0 (בניהם פקדי ה-TreeView, GridView ו-DataList שידועים בצרכי גרפיקה מוזרים ומשונים שלא נענו בדוט נט 2.0)
  • ניתן לשנות מספר רב של מאפיינים בתוך המחלקות השונות, לרבות האלמנטים ה-HTMLים שלהם (ע"י פירוט שם האלמנט לאחר שם המחלקה).
  • אפשר לקבוע קבצי CSS נפרדים לדפדפנים מסוג שונה. 
  • אם נרצה תמיכה של CSS Control Adapters בפקדים נוספים שלא מגיעים כברירת מחדל נוכל לרשת מ-System.Web.UI.WebControls.Adapters.WebControlAdapter וליצור להם CSS Control Adapter משלהם. 

 

פרוייקט לדוגמה זמין להורדה כאן - http://www.JustinAngel.Net/files/CssAdapters.zip.

 

קישור:  http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=99616949

Silverlight - The Financial Impact

I'm quite well versed in the stock market around the world and I'd like to draw everyone's attention to a nice trend I've noticed.

Since Microsoft (nasdaq: MSFT) announced their new plans for Silverlight their stock's value increased 5.5%.
And every time Microsoft has a noticeable increase in stock value - Google's (nasdaq: GOOG) stock loses value.

Silverlight has been called a "flash-killer" and you can noticeably see the impact of Microsoft moving into Adobe turf on Adobe's shares. 

 

Red line - Microsoft's shares increase in 5.41% value.

Orange Line - adobe's shares loses 2.7% value.

Blue line - Google's shares loses 2% value.

Question from Tapuz .Net Forum: What is GDI? GDI+? WPF?

שאלה:

אם אני אתנהג במשך הרבה זמן כאילו אני יודעת מה זה GDI בסוף אני באמת אדע?

 

תשובה:

 

GDI היא טכנולוגיה בת בערך 10 שנים פחות או יותר שיצאה עם חלונות 3.11 שמטרתה היא לדאוג לציור ולרנדור של מסכים ואפקטים חלונאיים. מיותר לציין שכל דבר בן 10+ שנים בטכנולוגיה מיקרוסופטית כבר הוחלף.

+GDI היא טכנולוגיה הדוט נטית והיא מהווה שכבת תקשורת בין אפליקציות דוט נטיות לבין ה-GDI של מערכת ההפעלה. הרעיון מאחורי +GDI הוא "היי, GDI גרוע ואי-אפשר לעשות איתו כלום בלי לדאוג ל-4,000 דברים לא רלוונטים אז בואו נבנה מעליו ממשק שמאפשר באמת לעשות משהו".

העיקר של +GDI הוא אובייקט ה-graphics. אני ממליץ בחום שתפתחי את ה-Intellisense מעל האובייקט הנ"ל ותראי את רשימת המתודות שנמצאת שם. המתודות הנ"ל מייצגות את המשימות ש-+GDI בא לעזור לעשות, וביניהן ציור של צורות רבות (מרובע, משולש, אליפסה, ריבוע, וכך הלאה), מילוי אזור על המסך בצבע מסויים, ציור טקסט (כולל פונט וגודל) על המסך ואפשרות להחזיר את הגודל שלו בפיקסלים לאחר שצוייר וכך הלאה.

בנוסף לזה, יש עוד מספר משימות שמקוטלגות תחת +GDI שהיא בעיקר כל מה שאפשר להגדיר בנפנוף ידיים "כל מה שנראה כמו עבודת נמלים גרפית". למשל לקחת תמונה (בזכרון) ולקבוע לה שפיקסל מסויים הוא בצבע מסויים או להחזיר צבע של פיקסל מסויים בתמונה.

כל רכיב בפריימוורק בסופו של דבר או משתמש ברכיבי GDI בסיסיים של חלונות או מצייר את עצמו. בפועל, יש רכיבים בפריימוורק שאומרים ל-+GDI "או קיי, אתה בא לצייר אותי על המסך למשתמש, סבבה. תצייר פה קו, עוד קו כאן, עוד קו כאן, כאן תצייר מילים ותמלא את האזור של הטקסט בצבע כחול".

הרבה מאוד מהיופי של פקדים חלונאיים קנויים מחברות צד שלישי (syncfusion, infergistics, janus, xceed וכך הלאה) שהן לא משתמשות בציור הברירת מחדל של חלונות ובפועל פשוט מציירים קו-קו ריבוע-ריבוע מהתחלה ויוצרים פקדים מאוד מרשימים גרפית.

הבעיה של +GDI מתגלה בכמה דרכים:

1. זה לצייר קו-קו ריבוע-ריבוע טקסט-טקסט. זאת עבודת נמלים בכל משמעות המילה וכל עבודה מול +GDI מרגישה ככה (מבחינת חוויית תוכניתן) ולכן גוררת אחריה חוויית כתיבה מייסרת וארוכה שגורמת לזמן פיתוח ארוך וחוסר יכולת לדבאג.

2. אין שום התייחסות לאפקטים. ממשקי מחשב (בדומה למציאות) צריכים להיות אינטרקטיבים. תעמדי עם העכבר עכשיו על כפתור חלונאי כלשהו ורוב הסיכויים שהוא יזהר כדי למקד את תשומת הלב שלך שהעמדת את העכבר על כפתור.
+GDI תומך בציור, אבל אין שום התייחסות לאנימציות.

המקרה הפשוט ביותר הוא ציור של מרובע שגדל בגודל שלו בפי שניים תוך חמש שניות (כלומר, מריבוע של 50*50 פיקסל הופך להיות ריבוע של 100*100 פיקסל תוך חמש שניות).
ב-+GDI היינו צריכים לעשות חישובים ולהתחיל להגיד "OK, הוא גודל ב-50 בכל צד במשך 5 שניות, אז ניצור טיימר שכל שנייה מוחק את הריבוע, מעלה איזה פרמטר פנימי שמייצג את הגודל החדש שלו, מצייר אותו מחדש וכל זה בלולאה א-סינכרונית מול התצוגה".

בטכנולוגיה המיקרוסופטית החדשה שמחליפה את Winforms שנקראת WIndows Presentation Framework (או בקצרה, WPF) יש תמיכה הרבה הרבה יותר פשוטה בכל נושא האנימציות והציור (ותמיכה נוספת קלה יותר בנושאים כמו סאונד ווידאו). כמו כן הכלים של WPF הם כלים גרפיים ולא כלים של תוכניתן (הווה אומר, יש איזה כלי שנקרא Expression שהוא בעקרון לוח ציור חכם ולא Visual Studio שהוא כלי כתיבת קוד). ב-WPF היינו מגדירים Animation שרצה במשך חמש שניות ומגדילה את גודל הריבוע שכבר מצוייר.
כלומר, במקום לכתוב קוד מעפן וקשה ב-+GDI שאומר למה התכוונו, אנחנו ב-WPF פשוט נכתוב למה התכוונו.

 

קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=97985471