Question from Tapuz .Net forum: Custom ASP.Net Calendar Control that Highlights Days
שאלה:
איך אפשר לצבוע ימים נבחרים בפקד Calendar?
ראיתי שיש אירוע DayRender ושם אפשר לצבוע ימים, זאת דרך טובה?
תשובה:
שאלה מצויינת שנותנת לנו לראות איך ירושה ב-Web Controls יכולה לתרום המון לבהירות של הקוד שלנו.
בואו קודם נבין את הבעיה.
ניצור דף חדש ונוסיף לו פקד Calendar.
נוסיף את פקד ה-Calendar לדף:

וככה זה יראה בדפדפן:
נעשה שינוי קטן שנוכל לראות את הלוח-שנה בצורה קצת יותר ברורה ונשנה את הצבעים למשהו יותר בולט.
ובדפדפן:
עכשיו נרצה למשל להדגיש את ה-25.1.2008 כי יש לנו בו פגישה.
נראה איך עושים את זה ב-Event Handlers ואז נראה איך אפשר להפוך את הפתרון לכללי יותר דרך הורשה.
נפתח את רשימת האירועים של הפקד וניתן לחיצה כפולה על DayRender.
קיבלנו Event Handler לאירוע ה-DayRender בו נכתוב את הקוד שלנו.
protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
{
}
עכשיו נבדוק אם התאריך הוא ה-25.1.2008 ואם כן נכתוב את הטקסט ב-Bold ונצבע את הרקע של התא.
protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
{
if (e.Day.Date == new DateTime(2008, 1, 25))
{
e.Cell.Font.Bold = true;
e.Cell.BackColor = Color.LightCoral;
}
}
נריץ את זה בדפדפן.
ובאמת אפשר לראות שה-25.1.2008 נצבע באדום והפך להיות Bold.
נו, אז מה הבעיה? הפתרון מאוד ספציפי לנקודה מאוד ספציפית.
הקוד של הפתרון עבור הלוח-שנה מעורבב בקוד של הדף.
וגם אין כאן התייחסות לזה שבעולם האמיתי נרצה אולי לקסטם בצורה גרפית את ה-Style של התא המודגש ורק לספק לו רשימת תאריכים.
אז בואו ניצור מחלקה מיוחדת וכללית שמיועדת לטפל רק בנושא הזה.
זה הממשק שאני שואף שיהיה לנו בסוף לפרט ימים.
protected void Page_Load(object sender, EventArgs e)
{
HighlightedCalendar1.HighlightedDates.Add(new DateTime(2008, 1, 14));
HighlightedCalendar1.HighlightedDates.Add(new DateTime(2008, 1, 15));
HighlightedCalendar1.HighlightedDates.Add(new DateTime(2008, 1, 25));
HighlightedCalendar1.HighlightedDates.Add(new DateTime(2008, 1, 29));
}
וככה תיראה התוצאה:

נתחיל בליצור פרוייקט מסוג Web Control Library, או ב-Visual Studio 2008 לפרוייקט קוראים ASP.Net Server Control.
וזה מה שקיבלנו ב-Solution Explorer.
נמחק את ServerControl1.cs (כי הוא לא קשור) וניצור קובץ חדש בשם HighlightedCalendar.
ונראה את הקוד שקיבלנו כברירת מחדל ואת ה-Solution Explorer.
OK, דבר ראשון שנרצה לעשות זה להפוך את המחלקה ל-Public כדי שהפרוייקט Web שלנו יוכל לראות אותה ולדאוג שהיא יורשת מ-Calendar של הפריימוורק.
using System.Web.UI.WebControls;
namespace WebControlLibrary_VS2008
{
public class HighlightedCalendar : Calendar
{
}
}
בואו נחשוב ביחד מה המחלקה הזאת אמורה לקבל ומה היא אמורה להציג.
OK, אז גילינו שאנחנו צריכים לקבל רשימה של תאריכים להדגיש, ואיך להדגיש את התאים.
נתחיל ברשימת התאריכים להדגיש.
private List<DateTime> _highlightedDates = new List<DateTime>();
public List<DateTime> HighlightedDates
{
get { return _highlightedDates; }
set { _highlightedDates = value; }
}
וסגנון התא להדגשה.
private TableItemStyle _highlightedDayStyle = new TableItemStyle();
public TableItemStyle HighlightedDayStyle
{
get { return _highlightedDayStyle; }
set { _highlightedDayStyle = value; }
}
עכשיו נרצה לתפוס את אירוע ה-DayRender (מתוך הפקד) ולבדוק אם היום שרוצים לצבוע ברשימת הימים, ואם כן - להכיל עליו את הסגנון.
נירשם לאירוע בקונסטרקטור של המחלקה.
public HighlightedCalendar()
{
this.DayRender += new DayRenderEventHandler(HighlightedCalendar_DayRender);
}
void HighlightedCalendar_DayRender(object sender, DayRenderEventArgs e)
{
}
נבצע את הבדיקה האם התאריך נמצא ברשית התאריכים להדגשה. רק קודם נראה את ה-Intellisense על ה-EventArgs באירוע.
void HighlightedCalendar_DayRender(object sender, DayRenderEventArgs e)
{
if (HighlightedDates.Contains(e.Day.Date))
{
}
}
עכשיו, נרצה להשתמש בסגנון שקיבלנו ולצבוע את e.Cell. נכתוב את שם הפונקציה שתבצע את הצביעה ונעביר את e.Cell ואת הסגנון.
void HighlightedCalendar_DayRender(object sender, DayRenderEventArgs e)
{
if (HighlightedDates.Contains(e.Day.Date))
{
ApplyStyleToCell(e.Cell, this.HighlightedDayStyle);
}
}
חשוב להדגיש שהפונקציה ApplyStyleToCell עדיין לא קיימת ונצטרך ליצור אותה בעצמנו.
קיבלנו את:
private void ApplyStyleToCell(TableCell tableCell, TableItemStyle tableItemStyle)
{
}
נמלא אותו בקוד שדואג להעתיק מידע מה-Style וה-Font לתוך התא הנוכחי.
private void ApplyStyleToCell(TableCell cellToColor, TableItemStyle styleToApply)
{
cellToColor.BackColor = styleToApply.BackColor;
cellToColor.BorderColor = styleToApply.BorderColor;
cellToColor.BorderStyle = styleToApply.BorderStyle;
cellToColor.BorderWidth = styleToApply.BorderWidth;
cellToColor.CssClass = styleToApply.CssClass;
cellToColor.ForeColor = styleToApply.ForeColor;
cellToColor.Height = styleToApply.Height;
cellToColor.HorizontalAlign = styleToApply.HorizontalAlign;
cellToColor.VerticalAlign = styleToApply.VerticalAlign;
cellToColor.Width = styleToApply.Width;
cellToColor.Wrap = styleToApply.Wrap;
cellToColor.Font.CopyFrom(styleToApply.Font);
}
הייתי רוצה לעשות פה עוד צעד אחד אחרון וזה להוציא את הלוגיקה של חיפוש הימים החוצה למתודה נפרדת.
נסמן את הלוגיקה של התנאי IF שלנו.
נלחץ כפתור ימני ונבחר Refactor --> Extract Method.
וככה יראה הקוד שלנו בסוף:
void HighlightedCalendar_DayRender(object sender, DayRenderEventArgs e)
{
if (IsHighlightDate(e))
{
ApplyStyleToCell(e.Cell, this.HighlightedDayStyle);
}
}
private bool IsHighlightDate(DayRenderEventArgs e)
{
return HighlightedDates.Contains(e.Day.Date);
}
אם תקראו את הקוד בתוך HighlightedCalendar_DayRender תוכלו ממש להבין את האלגוריתם והסיבה למה אנחנו מבצעים כל פעולה.
זה צורת כתיבת קוד מאוד ברורה שמאוד קל לתחזק.
בשלב זה נרצה להוסיף Reference בין הפרוייקט Web שלנו ל-Web Control Library.
נחזור לטופס ASP.Net שלנו ועכשיו נראה את הפקד החדש בצד שמאל ב-Toolbox.
נזרוק אותו על הטופס.
נעשה לו אותו Auto-Format שעשינו לפקד Calendar מעליו.
עכשיו הגיע החלק המעניין - להשתמש במאפיינים (גם באנגלית: Properties) שיצרנו.
נרצה לעשות כמה שינויים במאפיינים הציבוריים שחשפנו.
הראשון, נרצה להעלים את HighlightedDates בכלל מה-Properties ככה שנוכל לקבוע אותו רק מה-Code Behind.
private List<DateTime> _highlightedDates = new List<DateTime>();
[Browsable(false), DefaultValue((List<DateTime>) null)]
public List<DateTime> HighlightedDates
{
get { return _highlightedDates; }
set { _highlightedDates = value; }
}
השני, נרצה ש-HighlightedDayStyle ישב עם כל שאר החברים שלו ב-Styles.
private TableItemStyle _highlightedDayStyle = new TableItemStyle();
[PersistenceMode(PersistenceMode.InnerProperty),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
DefaultValue((string) null),
Category("Styles")]
public TableItemStyle HighlightedDayStyle
{
get { return _highlightedDayStyle; }
set { _highlightedDayStyle = value; }
}
שאר הקוד מיועד כדי לגרום ל-Designer לתת ערכי Default למאפיינים שיצרנו, ולדאוג לשמור את ה-Style בתגית פנימית מקוננת ב-ASP.Net.
נראה איך זה נראה ב-Designer.
העלמנו לחלוטין את SelectedDates מה-Designer והוספנו את HighlightedDayStyle לקטגוריית Styles.
עכשיו נרצה לשנות כמה מאפיינים בתצוגה, למשל לקבוע צבע רקע ולקבוע שה-Font הוא Bold.
ב-Code Behind שלנו נפרט איזה ימים נרצה שיודגשו.
protected void Page_Load(object sender, EventArgs e)
{
HighlightedCalendar1.HighlightedDates.Add(new DateTime(2008, 1, 14));
HighlightedCalendar1.HighlightedDates.Add(new DateTime(2008, 1, 15));
HighlightedCalendar1.HighlightedDates.Add(new DateTime(2008, 1, 25));
HighlightedCalendar1.HighlightedDates.Add(new DateTime(2008, 1, 29));
}
נריץ את הדוגמה ונראה מה קיבלנו.
הקוד של הפקד
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebControlLibrary_VS2008
{
public class HighlightedCalendar : Calendar
{
public HighlightedCalendar()
{
this.DayRender += new DayRenderEventHandler(HighlightedCalendar_DayRender);
}
void HighlightedCalendar_DayRender(object sender, DayRenderEventArgs e)
{
if (IsHighlightDate(e))
{
ApplyStyleToCell(e.Cell, this.HighlightedDayStyle);
}
}
private bool IsHighlightDate(DayRenderEventArgs e)
{
return HighlightedDates.Contains(e.Day.Date);
}
private void ApplyStyleToCell(TableCell cellToColor, TableItemStyle styleToApply)
{
cellToColor.BackColor = styleToApply.BackColor;
cellToColor.BorderColor = styleToApply.BorderColor;
cellToColor.BorderStyle = styleToApply.BorderStyle;
cellToColor.BorderWidth = styleToApply.BorderWidth;
cellToColor.CssClass = styleToApply.CssClass;
cellToColor.ForeColor = styleToApply.ForeColor;
cellToColor.Height = styleToApply.Height;
cellToColor.HorizontalAlign = styleToApply.HorizontalAlign;
cellToColor.VerticalAlign = styleToApply.VerticalAlign;
cellToColor.Width = styleToApply.Width;
cellToColor.Wrap = styleToApply.Wrap;
cellToColor.Font.CopyFrom(styleToApply.Font);
}
private TableItemStyle _highlightedDayStyle = new TableItemStyle();
[PersistenceMode(PersistenceMode.InnerProperty),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
DefaultValue((string) null),
Category("Styles")]
public TableItemStyle HighlightedDayStyle
{
get { return _highlightedDayStyle; }
set { _highlightedDayStyle = value; }
}
private List<DateTime> _highlightedDates = new List<DateTime>();
[Browsable(false), DefaultValue((List<DateTime>) null)]
public List<DateTime> HighlightedDates
{
get { return _highlightedDates; }
set { _highlightedDates = value; }
}
}
}
נסכם את הקונספט שראינו כאן.
במקום לכתוב ב-Event Handlers קוד ספציפי לטיפול נקודתי כתבנו Custom Control.
הפקד הכללי הזה טיפל בנושא בצורה כללית ובכך יצר קוד שהרבה יותר קל לתחזק, לקרוא ולדבאג.
כמו כן, העבודה הכללית הביאה לנו יותר יכולות בסופו של דבר.
בנוסף, ראינו בקצרה איך כותבים Custom Control בסיסי.
יש עוד הרבה דברים שיכולנו להוסיף כאן ולפתח את הדוגמה, אבל אני מקווה שהקונספט והפרקטיקה עברה.
קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=111772766