MasterPage, משתנים, ו - CrossPostBa

MasterPage, משתנים, ו - CrossPostBa

למי שלא שם לשגיאה הנפוצה הזו, לא ניתן לשתף באופן ישיר משתנים בין MasterPage והדפים שמתמשים בו (כלומר לגשת מדף X למשתנה שנמצא ב - MasterPage שלו), יש כל מיני חלטורות כאלו (לדוגמה לשתף עם Session או כל מיני דברים מסובכים), האם מישהו מכיר פתרון קל, נוח ויעיל לנושא
ועוד בעיה היא שלא ניתן להשתמש ב - CrossPostBack בדפים שמשתמשים ב - MasterPage, כלומר אם הדף השולח הוא עם MP, הדף שנשלחים אליו הנתונים לא יזהה את הפקדים ב PreviousPage, האם מישהו מכיר פתרון לבעיה הזו
 
Nil ref error

1. כן, יש דרך כזו. <%@ MasterType VirtualPath="Site.master" %> תבדוק כאן עבור הפרטים: http://dotnetjunkies.com/QuickStartv20/aspnet/doc/masterpages/default.aspx
 

blue25

New member
נראה לי...

כשאתה פונה ל control בדף היעד, תפנה בעזרת fincontrol אבל אל תשכח שיש לשרשר ל ID את שם ה MasterPage controlId_MasterPageId.
 

Justin Angel

New member
קה פרובלמה מואי צ'יקיטה?

בקשר לבעיה השנייה, לא ברור בדיוק מה הבעיה (איזה פקדים לא יזוהו?). קוד מינימלי ורלוונטי כמובן הוא מילת המפתח כאן. בגדול, CrossPagePostBack אמור לדעת לזהות את כל הפקדים על הדף הקודם. הבעיה הראשונה היא דווקא בעיה מאוד מעניינת ומאוד נפוצה - עבודה מול MasterPage מתוך הדף (Page) שלנו. בואו נציג דוגמה מאוד פשוטה. יש לי MasterPage שמציג ככותרת את השם של הדף הנוכחי לפי ה-SiteMapProvider שלי. במימוש הכי בסיסי, יש לי MasterPage עם Label וה-Label.Text שלי מציג את האינפורמציה מקובץ sitemap. נביט על קובץ ה-siteMap:
// Web.sitemap <?xml version="1.0" encoding="utf-8" ?> <siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" > <siteMapNode url="Content.aspx" title="Hello world!" /> </siteMap>​
אני רוצה שבראש כל עמוד באפליקציה שיש לה MasterPage שהוא יציג אוטומטית את ה-Title מהקובץ XML הזה. נו טוב, נזרוק איזה Label בשם lblHeadline בתוך ה-MasterPage:
// MasterPage.master (desginer) <%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %> <!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:Label ID="lblHeadline" runat="server" Text="Label"></asp:Label><br /> <asp:contentplaceholder id="ContentPlaceHolder1" runat="server"> </asp:contentplaceholder> </div> </form> </body> </html>​
יפה, עכשיו ניקח את הקוד המאוד מאוד מסובך שמכניס את הכותרת של הדף לתוך ה-Label:
public partial class MasterPage : System.Web.UI.MasterPage { protected void Page_Load(object sender, EventArgs e) { lblHeadline.Text = SiteMap.CurrentNode.Title; } }​
ניצור דף Content Page בשם Content.Aspx (שמוגדר בקובץ ה-sitemap שלנו) שה-MasterPage שלו הוא ה-MasterPage שלנו. וכאשר נריץ את Content.aspx באמת נקבל שמוצג לנו Hello world. כל זה טוב ויפה ומדגים עבודה עם SiteMapProvider שנהדרת ל-Demoים, אבל איך זה בכלל קשור לשאלה שלך? זה לא. בערך כאן לרוב מפסיקים המאמרים. אז בוא ניקח את זה צעד קדימה, יש לנו דף Content2.aspx והוא לא מוצהר ב-SiteMapProvider (כלומר, אין לו אלמנט ב-web.sitemap) וה-MasterPage שלו הוא ה-MasterPage שלנו. אם ננסה להריץ את הקוד שלנו עכשיו נקבל שגיאה ש-SiteMap.CurrentNode הוא בכלל null והרי זה נכון כי לא הגדרנו SiteMapNode לדף הנוכחי החדש שלנו. לשם הדוגמה שלנו נחליט שאנחנו רוצים שהדף הרגיל שלנו (Content2.aspx) יקבע את הכותרת ב-MasterPage. אנחנו רוצים ש-Content2.aspx יוכל לקבוע את מה שיוצג ב-label ב-MasterPage. דבר ראשון, צריך שננסה לקבוע כותרת רק אם היא קיימת ב-SiteMapProvider:
protected void Page_Load(object sender, EventArgs e) { if (SiteMap.CurrentNode != null) lblHeadline.Text = SiteMap.CurrentNode.Title; }​
זה היה החלק הפשוט, עכשיו נחשוף מאפיין (גם באנגלית: Property) מתוך ה-MasterPage שיאפשר לשנות את הטקסט של ה-Label:
public string HeadlineText { get { EnsureChildControls(); return lblHeadline.Text; } set { EnsureChildControls(); lblHeadline.Text = value; } }​
לא עשינו כאן הרבה, אמרנו "תקבל מחרוזת, תהיה בטוח שבאמת קיים Label ולתוך ה-Label הזה תכניס את הערך שקיבלת, אההה ואתה גם יכול להחזיר את הערך הנוכחי ב-Label". עכשיו בתוך הדף עצמו נרצה לגשת ל-MasterPage שלנו. חשוב לשים כאן שיש שתי מחלקות שהטיפוס שלהן הוא MasterPage: יש לנו את System.Web.UI.MasterPage ויש לנו את המחלקה שיורשת ממנה בשם MasterPage שיושבת בפרוייקט שלנו. ההבדל הוא שהמחלקה שלנו היא היחידה שמכילה את המאפיין הזה וכל דף באשר הוא מכיל הפנייה לטיפוס של הפריימוורק ולא לטיפוס שלנו. אז בתוך הדף שלנו (Content2.aspx) נמיר את ה-MasterPage שלו לטיפוס MasterPage שלנו ואז ניגש למאפיין.
public partial class Content2 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { MasterPage myMasterPage = (MasterPage) this.Master; myMasterPage.HeadlineText = "www.JustinAngel.Net"; } }​
ואכן בתור כותרת נקבל www.JustinAngel.Net. ניגשנו ל-MasterPage של הדף הנוכחי, המרנו אותו מהטיפוס של הפריימוורק לטיפוס שלנו וניגשנו למאפיין שהוספנו לו. וזאת התשובה לשאלה שלך - מאפיינים. עכשיו ניקח את זה עוד צעד קדימה. אני לא רוצה בכל מקום שאני צריך לעבוד מול ה-Properties של ה-MasterPage שלי להמיר תמיד ידנית ל-MasterPage מהטיפוס שלנו. אני רוצה ש-this.Master תמיד יוביל ל-MasterPage שלנו. אז נגיד ככה: כל טופס באפליקציה לא יירש ישירות מ-System.Web.UI.Page אלא יירש ממחלקה שלנו שיורשת מ-Page ותיקרא BasePage.
// BasePage.cs public class BasePage : System.Web.UI.Page { public BasePage() { } } // every *.aspx.cs in the app public partial class Content2 : BasePage​
עכשיו נגיד לאפליקציה שכל פעם שפונים ל-this.Master אנחנו רוצים את ה-MasterPage שלנו.
public class BasePage : System.Web.UI.Page { public BasePage() { } public new MasterPage Master { get { return (MasterPage) base.Master; } } }​
שים לב לזה שהשתמנו במילת המפתח new ששמנו את התיאור של המאפיין. הסיבה היא שרצינו להשתמש במשהו שלא קיים נכון להיום בדוט נט - Convient types. לא נסביר בדיוק מה זה, אבל נסתפק בזה שאין את זה וכפתרון עקרוני אני משתמש ב-new שצריך להחזיר טיפוס ספציפי שיורש מטיפוס שמוגדר יותר למעלה בהיררכיה. ועכשיו נוכל לכתוב ככה:
// content2.aspx.cs public partial class Content2 : BasePage { protected void Page_Load(ob​
 

Justin Angel

New member
../images/Emo26.gif

שים לב לזה שהשתמנו במילת המפתח new ששמנו את התיאור של המאפיין. הסיבה היא שרצינו להשתמש במשהו שלא קיים נכון להיום בדוט נט - Convient types. לא נסביר בדיוק מה זה, אבל נסתפק בזה שאין את זה וכפתרון עקרוני אני משתמש ב-new שצריך להחזיר טיפוס ספציפי שיורש מטיפוס שמוגדר יותר למעלה בהיררכיה. ועכשיו נוכל לכתוב ככה:
// content2.aspx.cs public partial class Content2 : BasePage { protected void Page_Load(object sender, EventArgs e) { //MasterPage myMasterPage = (MasterPage) this.Master; //myMasterPage.HeadlineText = "www.JustinAngel.Net"; this.Master.HeadlineText = "www.JustinAngel.Net"; } }​
זאת הייתה דוגמה מלאה של איך ומתי צריך לעבוד מול MasterPages. זה אומר לחשוף ברמת ה-MasterPage מאפייני גישה (גם באנגלית: Properties) , זה אומר לבצע ירושה מ-BasePage, וזה אומר לחשוף ברמת ה-BasePage את הטיפוס הספציפי של ה-Master כדי שלא נצטרך כל הזמן לבצע המרה.
 
לגבי השאלה השנייה...

בדיוק מה שאמרת ,"קוד מינימלי", קח קוד מינימלי, של שני דפים שעובדים עם CrossPostBack, כל קוד שעבדת איתו, או שראית באיזשהוא אתר על זה, הכי פשוט שיש. תיצור פרוייקט חדש, רק שהפעם תיצור את אותם הדפים עם MasterPage והפלא ופלא, זה לא עובד, מצאתי על זה כמה קישורים בגוגל אבל זה לא ממש עזר.
 

Justin Angel

New member
קה פרובלמה מואי צ'יקיטה?

שאני אומר קוד מינימלי ורלוונטי אני כמובן מתייחס לקוד מינימלי ורלוונטי. הבעיה היא בקוד הרלוונטי והמינימלי שלך. FindControl אינו מבצע חיפוש היררכי בתוך Container אלא מבצע חיפוש בן רמה אחת בלבד בתוך בניו הישירים של Container. כאשר אין MasterPage אז this.PreviousPage יבציע ל-Form שהוא ה-Container העליון בדף הכתוב. כאשר יש MasterPage אז this.PreviousPage יצביע ל-MasterPage עצמו שהוא בתוכו מכיל את ה-Form וכך הלאה. זאת אחת מהסיבות הרבות שלא צריך להשתמש ב-FindControl (התלות במיקום היחסי של פקדים). עוד סיבה טובה היא העובדה ש-FindControl הוא loosly typed שמבוסס על מחרוזות ולא על דברים שבאמת עוברים קומפילציה. ככה שאם נכתוב אפליקציה שמשתמשת ב-FindControl ואז חס וחלילה נשנה את שם הפקד או מיקומו האפליקציה לא תעבוד, אבל גם תעבור קומפילציה. הפתרון הפשוט והבעייתי של המצב הזה הוא לחפש את אלמנט ה-form בתוך ה-MasterPage ובתוכו לחפש את הפקד שלך (וכך הלאה בכל רמת קינון נוספת שבתוכו יושב הפקד). תוכל לגלות את הנתיב "הנכון" (ביחס לאותה שנייה בה אתה מפתח את האפליקציה) באמצעות נבירה וחיפוש ב-PreviousPage.Controls במצב דיבאג עד שתגיע לפקד שאתה מחפש. הפתרון הנכון במקרה הזה הוא שוב עבודה עם מאפיינים. נגיד ויש לנו טופס Content1.aspx שיש בו כפתור ואנחנו רוצים לגלות מה הטקסט של הכפתור בדף Content2.aspx שאליו נבצע Cross Page PostBack. ברמת Content1 נחשוף מאפיין שיציג את הטקסט של הכפתור שאותה ינצל Content1.
public partial class Content1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } public string ButtonText { get { return Button1.Text; } } }​
בטעינה ב-Content2 נבדוק שבאמת מדובר ב-Cross Page PostBack ואם כן ניקח את הדף הקודם ונמיר אותו לסוג Content1 ובכך נקבל גישה למאפיין החדש שלנו.
public partial class Content2 : BasePage { protected void Page_Load(object sender, EventArgs e) { if (this.PreviousPage != null) { Content1 myPreviousPage = (Content1) PreviousPage; Response.Write(myPreviousPage.ButtonText); } } }​
הסיבה לביצוע ההמרה היא כמובן ש-PreviousPage הוא מסוג System.Web.UI.Page שאינו מכיל את המאפיין החדש שלנו ואלינו לדאוג לבצע את ההמרה מ-Page לסוג דף הספציפי שלנו בכדי שנקבל גישה תכנותית למאפיין החדש. בדומה לדוגמה הקודמת נדאג גם לא להעתיק ממקום למקום את ההמרה של Previous Page ונדאג ליצור אותו מחדש ברמת Conten2.
public partial class Content2 : BasePage { protected void Page_Load(object sender, EventArgs e) { //if (this.PreviousPage != null) //{ // Content1 myPreviousPage = (Content1)PreviousPage; // Response.Write(myPreviousPage.ButtonText); //} if (this.PreviousPage != null) Response.Write(this.PreviousPage.ButtonText); } private new Content1 PreviousPage { get { if (this.PreviousPage == null) return null; return (Content1) this.PreviousPage; } } }​
יש גם סיבה הרבה יותר תיאורטית ללמה זאת צורת עבודה נכונה והיא קשורה למילים גבוהות וגדולות כמו Cohesion גבוה בתוך כל דף (שהוא מארז בפני עצמו לפי מודל הקומפילציה) ו-Coupling נמוך בין הדפים עצמם. במילים של בן-אדם נורמלי, זה אומר שאנחנו רוצים שכל דף יהיה אחראי כמה שיותר לעצמו ולא ליצור תלות מיותרת בין הדפים. למשל אם נשתמש ב-FindControl בדף Content2.aspx נקבל מצב ששינוי במבנה ההירכי של פקדים ב-Content1.aspx או בשם הפקדים יגרור לכך ש-Content2.aspx יכשל בצורה שקטה. לעומת זאת כאשר Content1.aspx חושף רק את המידע הרלוונטי לדפים שמשתמשים בו והוא אחראי שהמידע הנכון יחשף אנחנו יוצרים תוכנה שיהיה הרבה יותר קל לתחזק והרבה יותר עמידה לשינויים.
 
שאלה על WebRequest והגבלת זמן

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