בעיה קטנה עם FormatString

IgalR

New member
בעיה קטנה עם FormatString

אני רוצה להוסיף עמודת קישור mailto :
<asp:HyperLinkField DataTextField="Email" HeaderText="דוא'ל" DataNavigateUrlFields="Email" DataNavigateUrlFormatString="mailto:{0}" />​
איך עושים קידוד לתו ":" ?
 

Justin Angel

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

שאלה מאוד טובה ובאמת התשובה מעניינת. נסביר את הבעיה קצת יותר לעומק, בואו נניח ויש לי את הקישור הבא
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="mailto:[email protected]">HyperLink</asp:HyperLink>​
במצב הזה הקישור יתרנדר לקישור רגיל לכל דבר שברגע שנלחץ עליו הדפדפן שלנו יפתח את תוכנת הדוא"ל שלנו עם אי-מייל שמכוון ל[email protected]. ניקח את זה צעד קדימה ונניח שיש לנו DataTable (למשל) שמכילה כתובות דוא"ל ונרצה ליצור עמודה עם קישורי mailto אליהם. נכתוב את הקוד הבא בדף ה-ASP.Net שלנו:
// in myForm.aspx <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"> <Columns> <asp:BoundField DataField="Link" /> <asp:HyperLinkField DataNavigateUrlFormatString="mailto:{0}" DataNavigateUrlFields="Link" DataTextField="Link" DataTextFormatString="{0}" /> </Columns> </asp:GridView> // in myForm.aspx.cs protected void Page_Load(object sender, EventArgs e) { GridView1.DataSource = GetLinks(); GridView1.DataBind(); } private DataTable GetLinks() { DataTable dt = new DataTable(); dt.Columns.Add("Link", typeof (string)); dt.Rows.Add("[email protected]"); dt.Rows.Add("[email protected]"); dt.Rows.Add("[email protected]"); dt.Rows.Add("[email protected]"); return dt; }​
עד עכשיו יחסית פשוט, הגדרנו עמודה מסוג BoundField שפשוט תציג את האי-מיילים שלנו כטקסט פשוט לכל דבר. הגדרנו גם לכאורה עמודת קישורים שאמורה לקחת את הכתובת שכתובה בתוך עמודת Link ולהכניס אותה כל פעם לתבנית "mailto:myLink". נסביר מה כתוב שם. DataTextField הוא השדה של הטקסט שיוצג למשתמש בתור טקסט ו-DataTextFormatString אומר לקחת את השדה הזה ופשוט להציג אותו (הינו יכולים כאן לפרמט כאן את התוכן למספר, לכסף, לתאריך וכך הלאה). בצורה מאוד דומה יש לנו את DataNavigateUrlFields שאומר איזה שדות של מקור המידע נרצה בשביל התהליך של לחבר את מחרוזת הקישור עצמו, אז אמרנו לו "קח את המילים mailto: ותוסיף להם את השדה Link". מה שאמור לכאורה להתרנדר כרגיל כמו הקישור ולפתוח את אוטלוק שנלחץ עליו. אבל לא, זה יתרנדר כטקסט רגיל לכל דבר. אז עכשיו המשימה שלנו היא - לחשוב למה? למה אנחנו לא מקבלים Mailto? בואו נבחן את ה-HTML שמפיקה אחת השורות של ה-GridView:
<tr> <td>[email protected]</td> <td><a>[email protected]</a></td> </tr>​
מול ה-HTML שמפיק אחד הקישורים שלנו:
<a id="HyperLink1" href="mailto:[email protected]">HyperLink</a>​
ננסה להבין איך עובד כל הסיפור הזה של טורים ב-GridView. מצד אחד יש לנו מחלקה בשם xxxField ומצד שני יש לנו את המחלקות המוכרות של ASP.Net. במקרה שלנו, יש לנו HyperLinkField שמכיל הרבה מאוד תאים ובכל תא כזה יש HyperLink. ואיך הקשר הזה ממומש? מסתבר שלכל xxxField יש מתודה בשם InitlizeCell והיא זו שמכניסה את הפקדים לתוך התא ביחס לטור שלנו. לפי Reflector הנה אותה מתודה של HyperLinkField:
public override void InitializeCell(DataControlFieldCell cell, DataControlCellType cellType, DataControlRowState rowState, int rowIndex) { base.InitializeCell(cell, cellType, rowState, rowIndex); if (cellType == DataControlCellType.DataCell) { HyperLink link1 = new HyperLink(); link1.Text = this.Text; link1.NavigateUrl = this.NavigateUrl; link1.Target = this.Target; if (((rowState & DataControlRowState.Insert) == DataControlRowState.Normal) && base.Visible) { if ((this.DataNavigateUrlFields.Length != 0) || (this.DataTextField.Length != 0)) { link1.DataBinding += new EventHandler(this.OnDataBindField); } cell.Controls.Add(link1); } } }​
אפשר לראות שלכל תא בשורה שהיא שורת מידע (בניגוד למשל לשורה שהיא Header או Footer או סתם שורה ריקה) אנחנו מאתחלים HyperLink ומוסיפים אותו לתא שלנו. בנוסף, אנחנו שואלים כל מיני שאלות מוזרות ואם הם נכונות, אז נקרא למתודה OnDataBindField לכל קישור שלנו שעובר DataBinding. אז הבנו מה הקשר בין HyperLink ובין HyperLinkField ועכשיו נצטרך למצוא את הסיבה למה אנחנו לא מקבלים Mailto! כל מה שעשינו עד עכשיו עדיין לא נגע בפועל להכניס HyperLink.NavigateURL, אלא רק אתחול של ה-HyperLink. הקישור עצמו נקבע בזמן ה-DataBinding ולכן אנחנו צריכים להביט לתוך המתודה הזה OnDataBindField. בתוך אותה מתודה נוכל למצוא את הקטע קוד הבא:
string text3 = string.Empty; if (((this.urlFieldDescs != null)​
 

Justin Angel

New member
../images/Emo26.gif המשך

קטע קוד מפחיד ומזעזע לכל הדעות (ועוד בלי השמות האמיתיים של הפרמטרים), אבל נוכל לשים לב כאן בערך מה הולך. יש משתנה בשם text3 והוא הופך להיות ה-NavigateURL שלנו. אז נביט עליו קצת. הוא מתאתחל בתחילת הקטע קוד הזה ומקבל ערך רק של text4. המשתנה text4 עובר כל מיני מניפולציות מפחידות משהו ובדיקה הגיונית יחסית של CrossSiteScriptingValidation.IsDangerousUrl. בואו נביט על התוכן של המתודה שעושה את הבדיקה הזו:
internal static bool IsDangerousUrl(string s) { if (string.IsNullOrEmpty(s)) { return false; } s = s.Trim(); int num1 = s.Length; string maltioPrefix = "mailto:"; if (((((num1 > 4) && ((s[0] == 'h') || (s[0] == 'H'))) && ((s[1] == 't') || (s[1] == 'T'))) && (((s[2] == 't') || (s[2] == 'T')) && ((s[3] == 'p') || (s[3] == 'P')))) && ((s[4] == ':') || (((num1 > 5) && ((s[4] == 's') || (s[4] == 'S'))) && (s[5] == ':')))) { return false; } int num2 = s.IndexOf(':'); if (num2 == -1) { return false; } return true; }​
והנה, גילינו את האשם שלנו! לפני קביעת NavigateURL מתבצעת בדיקה שנועדה להגן עלינו שהמידע שאנחנו מכניסים לתוך הקישור, הוא אכן קישור שמתחיל בטקסט "http://". זאת אכן דרך מוזרה משהו לבדוק את זה, אבל ניחא. אז עכשיו, גילינו שאין לנו שום דרך לעבוד עם HyperLinkField ובאמת להוסיף תחילית של mailto. אז נעשה מה שתמיד צריך לעשות במצב הזה - נירש את HyperLinkField ונשנה את ההתנהגות שלו.
namespace Controls { public class MailtoHyperLinkField : HyperLinkField { public MailtoHyperLinkField() { } } }​
ראינו שכל הקוד הבעייתי נמצא במתודה InitlizeCell, אז נדרוס אותה. וניגש לאותו HyperLink שהוסיפה המתודה של InitlizeCell ששייך ל-HyperLinkField המקורי. כלומר, ניתן ל-HyperLinkField.InitlizeCell להתבצע, אבל אחר-כך אנחנו נתפוס שליטה וניגש ל-HyperLink שהיא הוסיפה. למה חטפנו את השליטה? כדי להשיג את היכולת לבצע בעצמנו את OnDataBindField.
public override void InitializeCell(DataControlFieldCell cell, DataControlCellType cellType, DataControlRowState rowState, int rowIndex) { base.InitializeCell(cell, cellType, rowState, rowIndex); if (cellType == DataControlCellType.DataCell) { HyperLink link1 = (HyperLink) cell.Controls[0]; if (((rowState & DataControlRowState.Insert) == DataControlRowState.Normal) && base.Visible) { if ((this.DataNavigateUrlFields.Length != 0) || (this.DataTextField.Length != 0)) { link1.DataBinding += new EventHandler(this.OnDataBindFieldWithMailTo); } cell.Controls.Add(link1); } } }​
נשנה בגירסה שלנו של OnDataBindField את בדיקת האותיות התחיליות שיבדוק אם המחרוזת היא Mailto.
public static bool IsDangerousUrl(string s) { if (string.IsNullOrEmpty(s)) { return false; } s = s.Trim(); int num1 = s.Length; string maltioPrefix = "mailto:"; if ((((((num1 > 4) && ((s[0] == 'h') || (s[0] == 'H'))) && ((s[1] == 't') || (s[1] == 'T'))) && (((s[2] == 't') || (s[2] == 'T')) && ((s[3] == 'p') || (s[3] == 'P')))) && ((s[4] == ':') || (((num1 > 5) && ((s[4] == 's') || (s[4] == 'S'))) && (s[5] == ':')))) || ((num1 > maltioPrefix.Length) && (s.StartsWith(maltioPrefix)))) { return false; } int num2 = s.IndexOf(':'); if (num2 == -1) { return false; } return true; }​
ועכשיו במקום הבדיקה של CrossSiteScriptingValidation.IsDangerousUrl נשתמש ב-IsDangerousUrl שלנו.
if (!IsDangerousUrl(text4)) text3 = text4;​
אז הכל טוב ויפה, יש לנו פקד חדש שעושה בדיוק מה שרצינו. נרצה עכשיו לפרוס אותו כדי שבאמת נוכל להשתמש בו. אפשרות אחת ולא מומלצת היא לזרוק את קובץ ה-cs שלו לתיקיית App_Code של האתר שלנו. אפשרות שנייה
 

IgalR

New member
השעה היא 4 בערך יום חמישי

אני מקבל טלפון : "יגאל ... תחליף לי בבקשה את תצוגת הדואל ללינק שאוכל ללחוץ ולשלוח דואל בקלות" "אין בעיה חמש דקות זה בשרת ...."
 

EdotK

New member
שאלת תם

די חשוב להבין את המנגנון הזה שהסברת, אבל כפתרון לא עדיף להשתמש בTemplateField פשוט?
 
למעלה