DCSIMG
February 2012 - Posts - שלמה גולדברג (הרב דוטנט)

שלמה גולדברג (הרב דוטנט)

מרצה בסלע ויועץ בעולם ה - net.

February 2012 - Posts

Introduction to ASP.NET MVC 3

 

הקדמה: 
לאחרונה יצא לי להיות בחברה מסויימת בה הייתי אמור להוביל פיתוח אתר ב - ASP.NET MVC 3, היה שם צוות נהדר וחכם, הבעייה שאף אחד מהצוות מאותה חברה לא הכיר את עולם ה - Web וכמובן לא את פלטפורמת asp.net mvc.
 
לכן הקדשנו תקופה מסויימת ללמוד את הטכנולוגיה, ובניתי רשימת תרגילים אשר הם צריכים לפתור, ודרך תרגילים אלו הם התחילו להכיר את asp.net mvc, התרגילים הינם תרגילים מתגלגלים, כלומר - כל תרגיל הינו המשך לתרגיל הקודם.
 
היות שאני מניח שיש עוד אנשים אשר זקוקים למדריך למידה על asp.net mvc, החלטתי שבמקום לכתוב מדריך כפי שאני עושה בדרך כלל, הפעם אני אכתוב פוסט יומי (בתקווה) אשר יכלול תרגיל אחד (לפי הסדר) והפיתרון שלו כולל ההסברים. בצורה זו מי שירצה להכנס לעולם ה - asp.net mcv יוכל לעשות זאת יחסית בקלות.
 
אני ממליץ למי שקורא סדרת פוסטים אלו כדי ללמוד, לא להסתכל ישר על הפיתרון - אלא לנסות לפתור לבד ורק לאחר מכן להסתכל על הפיתרון וההסברים.
 
 
תרגיל מס' 1. - דף לוגין בסיסי.
 
תיאור התרגיל: יצירת אפליקציית MVC עם דף לוגין המאפשרת הכנסת שם משתמש וסיסמא במידה והכול תקין יש לעבור לדף ברוך הבא.
 
מטרת התרגיל: הכרת סביבת העבודה, היכרות עם View, Controller, מציאת האלמנטים ב – dom ועבודה עם ולידציות ב – java script.
 
שלבים:
  1. פתחו פרוייקט חדש מסוג MVC 3 (אם לא קיים התקינו אותו).
  2. בחרו ב template מסוג Empty.
  3. מחקו את כל הסקריפטים המגיעים תחת תיקיית Scripts.
  4. מחקו את כל הקבצים תחת תיקיית Content.
  5. מחקו את ההפניות לסקריפט ול – CSS מתוך קובץ _Layout.cshtml.
  6. הוסיפו View חדש (תמיד השתמשו ב - Layout.cshtml כמסטר) המציג UI עם שני תיבות טקסט ולחצן.
  7. כדי לעשות זאת הוסיפו תגית form ל – view שיכיל את אותם שני תיבות טקסט, לכל אחד מהם תנו name, הלחצן יהיה מסוג submit.
  8. הוסיפו Controller חדש.
  9. הוסיפו Action שכל מה שיחזיר את ה – View.
  10. הוסיפו Action נוסף שיקבל את [HttpPost] כ – Attribute. והשם שלו יהיה זהה לשם של ה – Action הקודם, כמו כן יקבל שני מחרוזות (שמות המשתנים כפי שהגדרתם את מאפיין ה – name ל – input ב – view).
  11. בדקו האם הערכים תקינים וחוקיים (תקינים – האם יש ערכים, חוקיים – האם קיים משתמש שזהוי הסיסמא שלו (בשלב זה בדיקה hard code)), במידה ותקין יש להעביר את המשתמש לדף אחר, אחרת להציג הודעת שגיאה (בעזרת שימוש ב – ViewBag).
  12. הוסיפו רישום ל – onsubmit בצד הלקוח כדי לוודא לפני שהערכים נשלחים לשרת האם המשתמש אכן הכניס ערכים לתיבות הטקסט, במידה ולא יש לבטל את הקריאה לשרת ולהציג alert.
 
 
הסיבה למחיקת כל מה שמגיע כברירת מחדל היא כדי להבין יותר את המשמעות של כל דבר.
 
 
 
פיתרון:
את דוגמת הקוד ניתן להוריד כאן.
 
 
תחת תיקיית Controller נייצר חדש ונקרא לו בשם Start (חשוב - השם צריך להיות StartController). הוא יראה כך:
 

public class StartController : Controller

{

    public ActionResult Index()

    {

        return View();

    }

 

    [HttpPost]

    public ActionResult Index(string name, string password)

    {

        if (name == null || name.Trim() == string.Empty || password == null || password.Trim() == string.Empty)

        {

            ViewBag.Error = "Parameters required";

            return View();

        }

 

        if (name != "shlomo" && password != "goldberg")

        {

            ViewBag.Error = "User name or password incorrect";

            return View();

        }

 

        return View("Welcome");

    }

}

 
 
ה - action הראשון לא מקבל פרמטרים ומחזיר ActionResult שזה האבא של האובייקטים שאמורים לחזור מתוך Action, במקרה שלנו אנחנו מחזירים View שלמעשה מחזיר אובייקט מסוג ViewResult (שיורש ממנו), כברירת מחדל ה - Action עובד ב - HttpGet, ה - view שהחזרנו לא קיבל שום פרמטר שיגדיר לו איזה view להחזיר, ולכן הוא יחפש תחת תיקיית ה - Views תיקייה בשם ה - Controller שלנו (Start) ותחתיה View בשם ה - Action שכרגע הוא נמצא בו (Index).
 
ה - action השני נקרא גם כן index, אך מקבל פרמטרים ומוגדר לעבוד ב - HttpPost, אנחנו בודקים את הנכונות של המידע שהתקבל, במידה ומשהו לא תקין, נשתמש ב - ViewBag (שהוא אובייקט מסוג dynamic) כדי להוסיף מאפיינים (Error, הוא מאפיין שהמצאתי, ניתן להוסיף כל מאפיין שנרצה) ולהחזיק את הודעת השגיאה, במידה ויש שגיאה, נחזיר את ה - View הנוכחי, במידה והכול תקין נחזיר View אחר שנקרא Welcome (גם כן בתיקיית Start).
 
 
כעת נוסיף את התיקייה Start תחת Views, ונוסיף, View חדש בשם Index, בחלון שיפתח נבחר את ה - Layout שלנו (שנמצא תחת Shared) - בתוך Layout אנחנו יכולים לכתוב קוד משותף שיהיה בכל הדפים שמשתמשים בו (כמו master page ב - web forms)
 
נשנה טיפה את הקוד של Layout.cshtml והוא יראה כך:
 
 

<!DOCTYPE html>

<html>

<head>

    <title>@ViewBag.Title</title>

    @RenderSection("scripts", required: false)

</head>

<body>

    @RenderBody()

</body>

</html>

 

 
הסימנים של @, מגדירים שקטע הבלוק הבא הינו קוד צד שרת (זהו סינטקסט שנקרא razor).
 
עבור ה - title נכניס את הערך שיהיה בתוך ViewBag.Title.
 
נשתמש בפונקציית RenderSection שמאפשר לנו להגדיר שם (במקרה הזה scripts) והאם ה - section הוא חובה, במידה והדפים המשתמשים ב - Layout הזה יגדירו section עם השם הזה (scripts) התוכן שלהם יכנס לכאן.
 
תחת ה - body נשתמש בפונקציית RenderBody שבפועל תעתיק את התוכן של הדף האמיתי לכאן.
 
 
הקוד של Index.cshtml יראה כך:
 

@{

    ViewBag.Title = "Index";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

 

@section scripts

{

    <script type="text/javascript">

        function validate() {

            var name = document.getElementById('name').value;

            var password = document.getElementById('password').value;

 

            if (name == '' || password == '') {

                alert('Parameters required');

                return false;

            }

 

            return true;

        }

    </script>

}

 

<h2>Index</h2>

 

 

<form method="post" onsubmit="return validate()">

    <table>

        <tr>

            <td>User Name</td>

            <td><input type="text" name="name" id="name" /></td>

        </tr>

         <tr>

            <td>Password</td>

            <td><input type="password" name="password" id="password" /></td>

        </tr>

        <tr>

            <td colspan="2">

                <input type="submit" value="Login" />

            </td>

        </tr>

    </table>

 

    <h3>@ViewBag.Error</h3>

 

</form>

 
 
בהתחלה נפתח בלוק צד שרת, ונגדיר ערך עבור ה - Title, וכן נגדיר מיהו ה - Layout שהדף משתמש בו.
 
לאחר מכן נגדיר את התוכן עבור script section, ונוסיף פונקציית בדיקה הצד הלקוח, האם הערכים אכן נכונים.
 
ובסוף נגדיר טופס html שיוגדר כ - post (כך שיגיע למתודת ה - index עם ה - HttpPost), ובזמן submit לבדוק את פונקציית validate ורק אם היא החזירה true להמשיך לשרת.
 
הסיבה לבדיקה הכפולה היא עבור משתמשים תמימים שכל מה שהם רוצים זה להשתמש באפליקצייה, הסיבה לבדיקה בצד השרת היא עבור מתחכמים והאקרים שינסו לעקוף את הבדיקה בצד הלקוח.
 
מאפיין ה - name של ה - inputs חייב להתאים לפרמטרים המוגדרים במתודת Index בצד השרת, כך שהערכים שהמתשמש יכניס אכן יועברו בצורה נכונה.
 
בסוף הדף נשפוך את התוכן מתוך ViewBag.Error, כמובן שבמידה ואין Error (כמו בפעם הראשונה) לא יוצג שום דבר.
 
נוסיף View חדש בשם Welcome שתהיה הו הודעת ברכה כלשהי.
 
 
 שינוי אחד נוסף, נשנה את global.asax, כך שברירת המחדל תהיה ה - Controller שלנו.
 

public static void RegisterRoutes(RouteCollection routes)

{

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

 

    routes.MapRoute(

        "Default", // Route name

        "{controller}/{action}/{id}", // URL with parameters

        new { controller = "Start", action = "Index", id = UrlParameter.Optional } // Parameter defaults

    );

 

}

 
לא נכנס בפוסט זה לכל הקוד, אבל ניתן לראות שברירת המחדל היא Start וה - Action הוא Index.
 
 
סיכום:
בפוסט זה יצרנו אפליקציית mvc ראשונה, ראינו כיצד מוסיפים Controller ומשתמשים ב - Action, וכיצד מעבירים מידע מה - Controller ל - View.
בפוסט הבא נשנה את הקוד שלנו כך שיעבוד מול בסיס נתונים.

DataGridViewFontColumn

 

את הקוד המלא ניתן להוריד מכאן
 
יצא לי הצורך להגדיר מחלקה הנראית כך:
 

public class EnvironmentFont

{

    public string Name { get; set; }

    public Font Font { get; set; }

}

 
היה לי מערך של אובייקטים ממחלקה זו, והייתי צריך להציג אותם על גבי DataGrid בסביבת WinForm.
 
הגדרת המשימה הייתה להציג על גבי גריד ולחיצה על התא תפתח את ה - FontDialog כדי לאפשר לבחור גופן אחר.
 
 
מסתבר שלייצר עמודה מסוג מיוחד לגריד בסביבת WinForms היא לא תהליך פשוט - קיימים מאמרים שונים שמסבירים איך לעשות זאת - בפוסט זה נראה כיצד להגדיר את העמודה.
 
 
ראשית נכתוב מחלקה היורשת מ - DataGridViewTextBoxCell שתראה כך:
 

public class DataGridViewFontCell : DataGridViewTextBoxCell

{

    public override Type ValueType

    {

        get

        {

            return typeof(Font);

        }

    }

 

    public override Type EditType

    {

        get

        {

            return typeof(GridFontPicker);

        }

    }

 

    public override object DefaultNewRowValue

    {

        get

        {

            return new Font("arial", 12, FontStyle.Regular);

        }

    }

 

    public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)

    {

        base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);

 

        GridFontPicker fontPicker = this.DataGridView.EditingControl as GridFontPicker;

        if (fontPicker != null)

        {

            fontPicker.EditingControlFormattedValue = initialFormattedValue;

        }

    }

}

 
 
ישנם הרבה מתודות שניתן לדרוס עבור פונקציונליות שנרצה לקבל, במקרה שלנו נדרוס את המאפיין ValueType בו נגדיר מהי סוג העמודה, את המאפיין EditType בו ניתן את הפקד שיפתח בזמן עריכת התא (מחלקה היורשת מ - Control ומממשת את הממשק IDataGridViewEditingControl), נדרוס את המאפיין DefaultNewRowValue בו נגדיר את פונט ברירת המחדל, ונדרוס את המתודה הכי חשובה InitializeEditingControl בו נפנה לפקד שייצרנו בשביל העריכה (נראה בהמשך) ונשלח את הערך הנוכחי שיש בתא.
 
 
כעת נייצר מחלקה שתירש מ - DataGridViewColumn שתיראה כך:
 

public class DataGridViewFontColumn : DataGridViewColumn

{

    public DataGridViewFontColumn()

        : base(new DataGridViewFontCell())

    {

    }

}

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

dataGridView1.Columns.Add(new DataGridViewFontColumn());

 
 
הדבר האחרון שנשאר לראות, זה את המחלקה עבור עריכת התא.
 
למעשה מה שנעשה הוא יצירת UserControl שבזמן שצריך הוא יקפיץ את ה - FontDialog.
 
אני לא מציג כאן את כל המתודות - רק את החשובות, תוכלו להוריד את כל הפרוייקט מהלינק בתחילת הפוסט.
 

public partial class GridFontPicker : UserControl, IDataGridViewEditingControl

{

    public void PrepareEditingControlForEdit(bool selectAll)

    {

        if (fontDialog1.ShowDialog() == DialogResult.OK)

        {

            label1.Text = fontDialog1.Font.ConvertToString();

            EditingControlValueChanged = true;

            this.EditingControlDataGridView.NotifyCurrentCellDirty(true);

        }

    }

 

    public object EditingControlFormattedValue

    {

        get

        {

            return fontDialog1.Font.ConvertToString();

        }

        set

        {

            if (value != null)

            {

                Font font1 = value.ToString().ConvertToFont();

 

                fontDialog1.Font = font1;

                label1.Text = value.ToString();

            }

        }

    }

}

 
למעשה בגלל שאנחנו לא באמת מאפשרים למשתמש לערוך משהו רק בעזרת FontDialog הקוד הוא הרבה יותר פשוט.
 
יש את המאפיין EdititngControlFormattedValue שבו נקבל את הערך ונחזיר אותו בזמן סיום העריכה.
 
ההמרה בין מחרוזת למחלקת Font, מתבצעת בעזרת הקוד הבא:
 

public static class Converters

{

    private static TypeConverter _fontConverter = TypeDescriptor.GetConverter(typeof(Font));

 

    public static Font ConvertToFont(this string value)

    {

        return (Font)_fontConverter.ConvertFromString(value);

    }

 

    public static string ConvertToString(this Font font)

    {

        return _fontConverter.ConvertToString(font);

 

 
 
בזמן מתודת PrepareEditingControlForEdit אנחנו מקפיצים את הדיאלוג, ונותנים למשתמש את האפשרות לבחור, לאחר בחירה אנחנו מודיעים לגריד (בעזרת מאפיין שלא מוצג כאן בפוסט) שהיה שינוי.
 
 
ישנם עוד מאפיינים ומתודות שאפשר להשתמש בהם - בפוסט הבא אני אראה את DataGridViewColorColumn שבו יש שימוש בעוד מתודות
Posted: Feb 25 2012, 08:15 PM by Shlomo | with no comments
תגים:,

XmlSerializable and System.Drawing.Color

 

יצא לי לכתוב מחלקה שנראית כך:
 

public struct EnviormentColor

{

    public string Name { get; set; }

    public Color ForeColor { get; set; }

    public Color BackColor { get; set; }

}

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

XmlSerializer serializer = new XmlSerializer(typeof(List<EnviormentColor>));

serializer.Serialize(File.OpenWrite("colors.xml"), list);

 
כעת קרה דבר מעניין, כך נראתה התוצאה:
 
 

<?xml version="1.0"?>

<ArrayOfEnviormentColor>

  <EnviormentColor>

    <Name>Shlomo</Name>

    <ForeColor />

    <BackColor />

  </EnviormentColor>

  <EnviormentColor>

    <Name>Noam</Name>

    <ForeColor />

    <BackColor />

  </EnviormentColor>

  <EnviormentColor>

    <Name>Yossi</Name>

    <ForeColor />

    <BackColor />

  </EnviormentColor>

  <EnviormentColor>

    <Name>david</Name>

    <ForeColor />

    <BackColor />

  </EnviormentColor>

</ArrayOfEnviormentColor>

 
כפי שאפשר לראות רק השמות נכתבו בקובץ אך לא הערכים של הצבעים.
 
לאחר קצת שיטוטים בגוגל, הבנתי שמנגנון הסירלזיציה של xml עושה זאת רק עבור מאפיינים שיש להם get;set; ולמאפיין Name של Color אין set.
 
ישנם כמה דרכים מוזרות לתקן זאת, אני בחרתי בצורה שנראתה לי הכי הגיונית, מימוש של IXmlSerializable, הממשק הזה מאפשר לנו להחליט כיצד יתבצע שיטוח של האובייקטים שלנו.
 
ולכן נשנה את הקוד שלנו קצת.
 

public struct EnviormentColor : IXmlSerializable

{

    public string Name { get; set; }

    public Color ForeColor { get; set; }

    public Color BackColor { get; set; }

 

    #region IXmlSerializable

    public XmlSchema GetSchema()

    {

        throw new NotImplementedException();

    }

 

    public void ReadXml(XmlReader reader)

    {

        Name = reader.ReadElementString();

        BackColor = Color.FromName(reader.ReadElementString());

        ForeColor = Color.FromName(reader.ReadElementString());

 

        reader.ReadEndElement();

    }

 

    public void WriteXml(XmlWriter writer)

    {

        writer.WriteElementString("Name", Name);

        writer.WriteElementString("BackColor", BackColor.Name);

        writer.WriteElementString("ForeColor", ForeColor.Name);

    }

    #endregion

}

 
כעת ההרצה של הקוד המבצע שיטוח של האובייקט ייתן את התוצר הבא:
 

<?xml version="1.0"?>

<ArrayOfEnviormentColor>

  <EnviormentColor>

    <Name>Shlomo</Name>

    <BackColor>Red</BackColor>

    <ForeColor>#aa33ff</ForeColor>

  </EnviormentColor>

  <EnviormentColor>

    <Name>Noam</Name>

    <BackColor>Red</BackColor>

    <ForeColor>#331100</ForeColor>

  </EnviormentColor>

  <EnviormentColor>

    <Name>Yossi</Name>

    <BackColor>Green</BackColor>

    <ForeColor>Blue</ForeColor>

  </EnviormentColor>

</ArrayOfEnviormentColor>

 
 
חשוב לשים לב לשורת ה - reader.ReadEndElement בפונקציית ReadXml, בלי שנקדם בכל פעם את ה - reader לסוף האלמנט הנוכחי, מנגנון הסירליזצייה יחשוב שהגענו לסוף הקובץ ויחזיר רק אלמנט אחד במערך.

Google search from code

 

הגיעה אלי בקשה מעניינת.
 

"יש לנו בסיס נתונים המכיל ספקים, לכל ספק יש עשרות מוצרים ואנחנו לא מעוניינים להכניס לבסיס הנתונים את הקשר בין מוצרים לספקים מכיוון שיש יותר מידי, מה ניתן לעשות כדי לחסוך את הצורך להכניס ידנית את המידע."
 
הפיתרון שהצעתי להם הוא אמנם לא מושלם אבל בהחלט מספק אותם.
 
כידוע לגוגל יש אפשרות חיפוש בתוך אתר, לדוגמא: shlomo goldberg site:sela.co.il יחפש את השם shlomo goldberg בתוך האתר של סלע.
 
הרעיון היה להכניס לבסיס הנתונים רק את כתובת האתר של כל ספק, ובזמן שהמשתמש יחפש את המוצר, מקוד נבצע פנייה לגוגל עבור כל אחד מהספקים ונחזיר את התוצאה הראשונה (כשכמובן מודעים לכך שייתכן שתחזור תשובה לא רלוונטית).
 
 
אז איך מחפשים בגוגל מקוד, הקוד עצמו והדרך לבצע אותו הגיע מכאן.
ותודה ליהלום המוכשר עידן לנגר על העזרה בכתיבת הפיתרון.
 
 
הקוד המלא ניתן להורדה מכאן, (בפוסט אני מציג רק חלק מהקוד הרלוונטי)
 
 
כשנפנה לגוגל מקוד נקבל תוצאה בפורמט json, כדי שנוכל לעבוד בקלות נגדיר אובייקטים עובר התוצאה.
 
האובייקט העיקרי נראה כך:
 

[DataContract]

public class Result

{

    [DataMember(Order = 0)]

    public string GsearchResultClass { get; set; }

 

    [DataMember(Order = 1, Name = "unescapedUrl")]

    public string UnescapedUrl { get; set; }

 

    [DataMember(Order = 2, Name = "url")]

    public string Url { get; set; }

 

    [DataMember(Order = 3, Name = "visibleUrl")]

    public string VisibleUrl { get; set; }

 

    [DataMember(Order = 4, Name = "cacheUrl")]

    public string CacheUrl { get; set; }

 

    [DataMember(Order = 5, Name = "title")]

    public string Title { get; set; }

 

    [DataMember(Order = 6, Name = "titleNoFormatting")]

    public string TitleNoFormatting { get; set; }

 

    [DataMember(Order = 7, Name = "content")]

    public string Content { get; set; }

}

 
 
מתוך כל המאפיינים, החשובים ביותר הם ה - Title המכיל את הכותרת כפי שמופיע בגוגל, וה - UnescapeUrl המכיל את הכתובת לדף, ומאפיין ה - Content המכיל את התוכן מתוך החיפוש.
 
יש עוד מחלקות שעטופות את המחלקה הזו (כדי לראות אותם הורידו את הקוד המלא).
 
כעת נכתוב את המחלקה הבאה:
 

public static class Search

{

    private const string URL_TEMPLATE = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q={0}";

    private static WebClient _client;

    private static DataContractJsonSerializer _jsonSerializer;

 

    static Search()

    {

        _client = new WebClient();

        _jsonSerializer = new DataContractJsonSerializer(typeof(GoogleResponseWrapper));

    }

 

    public static GoogleResponseWrapper GetSearchResults(string query, string webSite = null)

    {

        AddWebSiteIfNeeded(ref query, ref webSite);

        string searchUrl = string.Format(URL_TEMPLATE, query);

 

        byte[] answer = _client.DownloadData(searchUrl);

 

        using (MemoryStream ms = new MemoryStream(answer))

        {

            GoogleResponseWrapper results = (GoogleResponseWrapper)_jsonSerializer.ReadObject(ms);

            return results;

        }

    }

 

    private static void AddWebSiteIfNeeded(ref string query, ref string webSite)

    {

        if (!string.IsNullOrEmpty(webSite))

        {

            webSite = webSite.Replace("http://www.", string.Empty).Replace("www.", string.Empty);

            query += " site:" + webSite;

        }

    }

}

 
 
המתודה העיקרית GetSearchResult מקבלת מחרוזת ואתר לחפש בו (פרמטר לא חובה) מורידה את ה - json מגוגל, ומפרססת אותו לאובייקטים.
 
כעת ניתן לכתוב את קוד ה - aspx הבא:
 

<asp:Repeater ID="rpt" runat="server">

    <HeaderTemplate>

        <dl>

    </HeaderTemplate>

    <ItemTemplate>

        <dt>

            <a href='<%#Eval("UnescapedUrl") %>'><%#Eval("Title") %></a>       

        </dt>

        <dd>

            <%#Eval("Content") %>

        </dd>

        <br />

    </ItemTemplate>

    <FooterTemplate>

        </ul>

    </FooterTemplate>

</asp:Repeater>

 
ובצד השרת:
 

var res = Search.GetSearchResults(txtSearch.Text, txtSite.Text);

rpt.DataSource = res.ResponseData.Results;

rpt.DataBind();

 
 
אם לדוגמא נחפש "שלמה" באתר של סלע, נקבל תוצאה כזו:
 
shlomo in sela
 
 

background-size in IE 8

 

בפורום החדש של msdn על עולם ה - web, עלתה שאלה כיצד ניתן לאפשר שימוש ב -background-size גם בגרסאות ישנות של ie,
 
במידה וכל מה שרוצים הוא להתאים את התמונה לפקד שעוטף אותה - ניתן להשתמש ב - filter, בצורה הבאה:
 
 

filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='1.gif', sizingMethod='scale');

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

background-size: 90px 90px;

 
 
אחרי הרבה חיפושים הגעתי למסקנה שאי אפשר באמת ב - IE8 לתמוך בזה הצורה רגילה, לכן כתבתי את ה - handler עם הקוד הבא:
 
 

public void ProcessRequest(HttpContext context)

{

    context.Response.ContentType = "image/png";

 

    int width = int.Parse(context.Request.QueryString["width"]);

    int height = int.Parse(context.Request.QueryString["height"]);

    string url = context.Request.QueryString["url"];

 

    byte[] image = new WebClient().DownloadData(url);

    using (MemoryStream msToRead = new MemoryStream(image))

    {

        Bitmap newImage = new Bitmap(Image.FromStream(msToRead), new Size(width, height));

        using (MemoryStream msToWrite = new MemoryStream())

        {

            newImage.Save(msToWrite, ImageFormat.Png);

            image = new Byte[msToWrite.Length];

            msToWrite.Seek(0, SeekOrigin.Begin);

            msToWrite.Read(image, 0, image.Length);

 

            context.Response.OutputStream.Write(image, 0, image.Length);

        }

    }

}

 
 
כעת נכתוב את הקוד הבא ב - css
 

<style>

    div

    {

        background-image: url(1.jpg);

        background-size: 90px 90px;

        background-position: center center;

        background-repeat: no-repeat;

        width: 115px;

        height: 134px;

    }

</style>

<!--[if lte IE 8]>

<style>

    div

    {

        background-image: url(resize.ashx?url=http://localhost:49842/1.jpg&width=90&height=90) !important;

    }

</style>

<![endif]-->

 
 
 
במידה ומדובר בדפדפן מתקדם הוא ישתמש ב - backdround-size, אחרת התמונה תשלח ל - handler שתוריד את התמונה ותשנה את גודל התמונה לפי מה שהמשתמש רצה.

html lists -> back to basic

הפוסט הזה מיועד למתחילים בעולם ה - html, בפוסט זה נכיר את הסוגים השונים של רשימות בעולם ה - html.
 
בהרבה מקרים אנו רוצים להציג למשתמש רשימות של מידע, ואנחנו רוצים שזה יאורגן בצורה מסודרת מבלי שנרצה לעבוד קשה מדי כדי לבצע זאת, רשימות יכולות להיות רשימה של פיצ'רים שנתמוך בהם, מסמך משפטי המכיל סעיפים ותתי סעיפים, ואפילו תפריטים ותתי תפריטים.
כדי לעשות זאת יש לנו כמה סוגים של רשימות.
 
Unorderd List - מייצר רשימה לא ממוספרת, אלא משתמשת בסימנים (בולטים) כדי להגדיר את כל הרשימה.
Orderd List - מייצר רשימה ממוספרת המסומנת בעזרת מספרים או אותיות.
Definition List - מייצר רשימה בה לכל שורה יש שני ערכים (כותרת ותוכן).
 
 
שני הראשונים מכילים תגיות מסוג List Item, לעומת האחרון המכיל תגיות מסוג Definition Description ו - Definition Term.
 
 
דוגמא. נניח שנרצה רשימה המכילה שפות, נכתוב:

    7 <ul>

    8     <li>אנגלית</li>

    9     <li>עברית</li>

   10     <li>רוסית</li>

   11     <li>יידיש</li>

   12 </ul>

 
ul
 
במידה ונרצה תתי תפריטים נוכל בכל li להגדיר עוד ul פנימי, לדוגמא:

<ul>

    <li>קובץ

        <ul>

            <li>הדפסה</li>

            <li>שמירה</li>

            <li>פתיחה</li>

        </ul>

    </li>

    <li>עריכה

        <ul>

            <li>העתק</li>

            <li>הדבק</li>

        </ul>

    </li>

    <li>כלים</li>

    <li>עזרה

        <ul>

            <li>אודות</li>

            <li>מרכז העזרה

                <ul>

                    <li>המרכז העולמי</li>

                    <li>בלוגים</li>

                </ul>

            </li>

        </ul>

    </li>

</ul>

 
 
ul 2
לתג ul יש מאפיין בשם type המאפשר לנו להחליט על סוג הבולט שיוצג. הוא יכול לקבל את הערכים: circle, disc, square (או לפי התקנים ב - css במאפיין list-style-type).
 
 
הסוג השני הוא Orderd List, המאפשר לייצר רשימות ממוספרות, לדוגמא:
 

<ol>

    <li>סעיף שמדבר על רשימות</li>

    <li>סעיף שמדבר על רשימות</li>

    <li>סעיף שמדבר על רשימות

        <ol>

            <li>תת סעיף על רשימות</li>

            <li>תת סעיף על רשימות</li>

        </ol>

    </li>

    <li>סעיף שמדבר על רשימות</li>

</ol>

 
 
ol
 
באותה צורה כמו ב - ul ניתן לקנן ol בתוך li, וכמובן אפשר בתוך li להכניס ul ולקנן כל מה שנרצה.
 
 
סוג נוסף של רשימה הוא Definition List, המאפשר לנו להגדיר רשימות שיש לכל תוכן כותרת., למשל:
 
 

<dl>

    <dt>כותרת אחד</dt>

    <dd>

        התוכן של האלמנט הראשון<br />

        התוכן של האלמנט הראשון<br />

        התוכן של האלמנט הראשון<br />

    </dd>

    <dt>כותרת שנייה</dt>

    <dd>

        התוכן של האלמנט השני<br />

        התוכן של האלמנט השני<br />

        התוכן של האלמנט השני<br />

    </dd>

    <dt>כותרת שלישית</dt>

    <dd>

        התוכן של האלמנט השלישית<br />

        התוכן של האלמנט השלישית<br />

        התוכן של האלמנט השלישית<br />

    </dd>

</dl>

 
dl
 
 
כמובן שניתן לעצב רשימה כזאת לבד עם אלמנטים אחרים, אבל האלמנט מאפשר לנו לעשות זאת יותר בקלות.
 
כשנרצה להשתמש בהם עבור תפריטים, בדרך כלל זה ישולב עם קוד JS המסתיר ומציג את האלמנטים במעבר עכבר וכד'. (אולי אדגים בפוסט הבא)
Posted: Feb 07 2012, 08:48 PM by Shlomo | with no comments
תגים:,