[Tapuz .Net] ASP.Net 2.0 Wizard Control SideBar Rendering
שאלה:
האם ניתן להזיז את רשימת הצעדים ב-Wizard כך שתופיע מעל הפקדים (כמו טאבים)?
תשובה:
שאלה מצויינת שבאמצעותה נוכל לחפור קצת לתוך מערכת ה-Rendering של ASP.Net 2.0.
בואו נראה מה השלבים שניקח כדי לרדת לשורש הבעיה ולבסוף לפתור אותה:
ניצור דף ASP.Net חדש שעליו נדגים את הבעיה.
נוסיף לטופס שלנו פקד Wizard.
וקיבלנו את הפקד Wizard הברירת מחדל:
בשביל הדוגמה נעשה כמה שינויים קוסמטיים קטנים.
נגדיל את הפקד.
נעשה לו Auto-Format שיהיה יותר וויזאולי ברור מה אנחנו מנסים לעשות.
נוסיף עוד שני Steps ל-Wizard.
ולבסוף, נוסיף קצת טקסט לכל Wizard Step שלנו.
כנ"ל נעשה גם לצעד 2 וצעד 4.
זהו, הקמנו דוגמה קטנה ל-Wizard של ASP.Net 2.0.
נריץ את הדוגמה.
בלחיצה על Next נעבור לשלב הבא.
עכשיו, השאלה היא איך ניתן להעביר את ה-Sidebar לחלק העליון של פקד ה-Wizard.
אין אפשרות מובנית כזאת.
בואו נביט על ה-HTML שמייצר ה-Wizard שלנו.
אני אשתמש ב-Internet Explorer Developer Toolbar בשביל זה.
גולשי FireFox יכולים להשתמש ב-FireBug.
ואלו מכם שגולשים בדפדפנים אחרים יכולים להשתמש ב-Notepad לעיין ב-HTML.
נבחר ב-Select Element By Click.
עכשיו אפשר לעבור עם העכבר ולבחור אלמנט על הטופס.
המסגרת הכחולה מעידה על האלמנט שכרגע נבחר.
בחרנו את ה-Wizard שלנו ונוכל לראות את ההיררכיית פקדים שלו ובתוכו.
אפשר לראות ש-Wizard1 מתרגם לתגית <table> ב-HTML.
בתוך ה-<table> הראשי יש שורה אחת של תגית <tr> ובתוכו יש שני תאי <td> ובתוך כל אחד מאלו עוד טבלה <table>.
למי מאתנו שמבין באיך מעצבים HTML ומזועזע מהחוסר בשימוש בתגיות <div>ים כאן, אני מסכים. אם כי, שמפתחים פריימוורק צריך לרוב לבחור באפשרות שיותר קל לתמוך ולקסטם.
צריך לדאוג שה-Wizard ירנדר בטבלה שלו שתי שורות עם תא אחד בכל אחד מהם, ולא שורה אחת עם שני תאים.
נפתח את Reflector על System.Web.UI.WebControls.Wizard.
הדבר הראשון שתופס לנו את העין צריך להיות הירושה מ-CompositeControl.
CompositeControl היא סוג ספציפי של Custom Control ב-ASP.Net.
הרעיון הוא ש-Custom Control הוא פקד "יחיד" ו-Composite Control הוא איחוד בין מספר פקדים.
מהיכרות עם Composite Controls אנחנו יודעים לפתוח ישר את מתודת ה-CreateChildControls שבתוכה מפרטים תמיד איזה פקדים מתווספים להיררכיה.
אפשר לראות כאן קריאה למתודה בשם CreateControlHierachy.
אם נביט לתוכה נראה את הקוד המבהיל הבא.
מסכימים איתי זה לא קוד סימפטי ויחסית קשה לעקוב אחריו?
נוכל אם נרצה לפתוח את ה-Source Code של דוט נט ולקרוא אותו בצורה יותר מסודרת, אבל לא נראה לי שזה יעזור.
אני הבנתי מהקוד הזה דבר אחד ל-this.Controls מוסיפים טבלה ראשית כלשהי.
אז לפי דעתי, אם נירש (גם באנגלית: Inherit) את Wizard ונדרוס את המתודה הזאת נוכל לשנות את אוסף הפקדים הפנימי.
נתחיל בלפתוח פרוייקט חדש מסוג Web Control Library.
שימו לב שב-Visual Studio 2008 שם הפרוייקט השתנה מ-ASP.Net Web Control Library ל-ASP.Net Server Control.
נוסיף פקד myWizard שיורש מ-Wizard.
ונשנה את הקוד ברירת מחדל ככה שיכיל את הירושה הריקה מ-Wizard.
using System.Web.UI.WebControls;
namespace WebControlLibrary_VS2008
{
public class myWizard : Wizard
{
}
}
רק נדאג להוסיף את ה-Reference בין הפרוייקט Web שלנו ל-Web Control Library ונתקדם עם הפקד.
הגענו עכשיו לכתיבת הפקד שלנו.
נדרוס את המתודה CreateControlHierachy וכרגע גם הוספתי אליו BreakPoint כדי שעוד מעט נתחקר את this.Controls.
עכשיו נשנה את הקוד ASP.Net הקיים שלנו שיעבוד עם myWizard ולא ה-Wizard הברירת מחדל.
בקובץ ה-web.config שלנו נוסיף הרשמה ל-Web Control Library שלנו.
<pages>
<controls>
<add assembly="WebControlLibrary_VS2008" namespace="WebControlLibrary_VS2008" tagPrefix="my" />
</controls> </pages>
ובמקום להשתמש ב-<asp:Wizard> נשתמש בתגית ה-<my:myWizard>.
<asp:Wizard ID="Wizard1" runat="server">
...
</asp:Wizard>
<my:myWizard ID="Wizard1" runat="server">
...
</my:myWizard>
עכשיו שנריץ את האפליקציה Visual Studio עצר בשורה שביקשנו שיעצור.
נפתח את חלון ה-Quick Watch באמצעות קיצור המקלדת Ctrl + Alt + Q ונבקש ממנו לבדוק את this.Controls.
נחפור קצת בהיררכית הפקדים.
אפשר לראות שיש לנו WizardControlCollection שמכיל WizardChildTable שמכיל RowControlCollection שמכיל TableRow וזה מכיל בתורו AccessiableTableCell שהוא ה-SideBar ו-TableCell נוסף שזה ה-Wizard עצמו.
דבר ראשון, ניצור פרמטרים בפונקציה הזו שמפנים לכיוון ה-RowControlCollection ושני התאים.
protected override void CreateControlHierarchy()
{
base.CreateControlHierarchy();
Control wizardRows = this.Controls[0];
TableCell sideBar = (TableCell) wizardRows.Controls[0].Controls[0];
TableCell wizard = (TableCell) wizardRows.Controls[0].Controls[1];
}
דבר שני שנרצה לעשות זה להוריד את ה-sideBar מהשורה שהוא כרגע נמצא בה.
protected override void CreateControlHierarchy()
{
base.CreateControlHierarchy();
Control wizardRows = this.Controls[0];
TableCell sideBar = (TableCell) wizardRows.Controls[0].Controls[0];
TableCell wizard = (TableCell) wizardRows.Controls[0].Controls[1];
sideBar.Parent.Controls.Remove(sideBar);
}
ולבסוף, ניצור TableRow חדש שלתוכו נכניס את ה-TableRow ונשים את ה-TableRow כשורה הראשונה בטבלת השורות.
protected override void CreateControlHierarchy()
{
base.CreateControlHierarchy();
Control wizardRows = this.Controls[0];
TableCell sideBar = (TableCell) wizardRows.Controls[0].Controls[0];
TableCell wizard = (TableCell) wizardRows.Controls[0].Controls[1];
sideBar.Parent.Controls.Remove(sideBar);
TableRow sideBarRow = new TableRow();
sideBar.Height = new Unit(10);
sideBarRow.Controls.Add(sideBar);
wizardRows.Controls.AddAt(0, sideBarRow);
}
אם נריץ את הדוגמה כעת, זה יראה כך:
ואם נלחץ על Next נקבל:
עוד צעד שהייתי רוצה לעשות זה לתחום את השינוי שביצענו להעברת ה-SideBar להיות TopBar זה לשים מאפיין על הפקד ששואל אם באמת צריך לעשות את זה.
זאת גם הזדמנות טובה להדגים Encapsulate Field Refactoring ו-Extract Method Refoactoring.
נוסיף שדה בוליאני בשם showToBar_ ונאתחל אותו כברירת מחדל ל-false.
public class myWizard : Wizard
{
protected override void CreateControlHierarchy()
{
...
}
private bool _showTopBar = false;
}
בתוך Visual Studio 2008\2005 נלחץ Ctrl + R, Ctrl + E או כפתור ימני על שם השדה ונבחר Refactor --> Encapsulate Field.
נלחץ פעמיים על מקש האנטר ונקבל את המאפיין מסביב לשדה.
public class myWizard : Wizard
{
protected override void CreateControlHierarchy()
{
...
}
private bool _showTopBar = false;
public bool ShowTopBar
{
get { return _showTopBar; }
set { _showTopBar = value; }
}
}
נוסיף גם קצת מאפיינים ל-Design Time כדי שלמאפיין יהיה ערך ברירת מחדל ויופיע במקום הנכון ב-Properties של הפקד.
private bool _showTopBar = false;
[Category("Behavior"), DefaultValue(false)]
public bool ShowTopBar
{
get { return _showTopBar; }
set { _showTopBar = value; }
}
עכשיו נוציא את כל הקוד שלנו למתודה יעודית. נסמן את כל הקוד בתוך CreateControlHierachy.
נלחץ Ctrl + R, Ctrl + M או נלחץ כפתור ימני, אז Refactor ובסוף Extract Method.
נקבע שם חדש למתודה שתכיל את כל הקוד שסימנו.
ככה נראה הקוד של המחלקה כעת:
public class myWizard : Wizard
{
protected override void CreateControlHierarchy()
{
base.CreateControlHierarchy();
moveSideBarToTopBar();
}
private void moveSideBarToTopBar()
{
Control wizardRows = this.Controls[0];
TableCell sideBar = (TableCell)wizardRows.Controls[0].Controls[0];
TableCell wizard = (TableCell)wizardRows.Controls[0].Controls[1];
sideBar.Parent.Controls.Remove(sideBar);
TableRow sideBarRow = new TableRow();
sideBar.Height = new Unit(10);
sideBarRow.Controls.Add(sideBar);
wizardRows.Controls.AddAt(0, sideBarRow);
}
private bool _showTopBar = false;
[Category("Behavior"), DefaultValue(false)]
public bool ShowTopBar
{
get { return _showTopBar; }
set { _showTopBar = value; }
}
}
נוסיף תנאי if על המאפיין הבוליאני שלנו לפני שמבצעים את המתודה.
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebControlLibrary_VS2008
{
public class myWizard : Wizard
{
protected override void CreateControlHierarchy()
{
base.CreateControlHierarchy();
if (ShowTopBar)
moveSideBarToTopBar();
}
private void moveSideBarToTopBar()
{
Control wizardRows = this.Controls[0];
TableCell sideBar = (TableCell)wizardRows.Controls[0].Controls[0];
TableCell wizard = (TableCell)wizardRows.Controls[0].Controls[1];
sideBar.Parent.Controls.Remove(sideBar);
TableRow sideBarRow = new TableRow();
sideBar.Height = new Unit(10);
sideBarRow.Controls.Add(sideBar);
wizardRows.Controls.AddAt(0, sideBarRow);
}
private bool _showTopBar = false;
[Category("Behavior"), DefaultValue(false)]
public bool ShowTopBar
{
get { return _showTopBar; }
set { _showTopBar = value; }
}
}
}
נחזור לפרוייקט ASP.Net שלנו ושנה את ShowTopBar ל-true.
אכן עכשיו שקבענו את ShowTopBar=True נקבל את ה-SIdeBar מעל ה-Wizard.

נסכם את הגישה שלנו לבעיה והצעדים שלקחנו.
ניתן להוריד את הקוד שעבדנו עליו מכאן - http://www.JustinAngel.Net/files/Example-Wizard-TopBar.zip.
קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=112978594