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

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

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

jQuery progress bar label

בקשו ממנו להשתמש ב – jquery progress bar ולהציג את האחוזים מתחת ל – progress bar צמוד לערך שלו, כלומר משהו כזה:

image

 

הדרך לעשות זאת היא פשוטה, בהינתן שיש לנו את ה – html הבא:

Code Snippet

<div class="wrapper">
    <div class="progressbar" data-value="20"></div>
    <div class="value">20</div>
</div>
<br />
<div class="wrapper">
    <div class="progressbar" data-value="50"></div>
    <div class="value">50</div>
</div>
<br />
<div class="wrapper">
    <div class="progressbar" data-value="10"></div>
    <div class="value">10</div>
</div>

<input type="button" value="Make" onclick="make()" />

<input type="button" value="Add 10" onclick="add10()" />

 

כל אלמנט wrapper מכיל div שיהפוך להיות progress bar, ומכיל ב – data-value את הערך.

ובנוסף עוד div שמכיל בטקסט את האחוזים.

כעת נכתובת את הסקריפט שמייצר את ה  - progress bar

Code Snippet
function make() {
    $('.progressbar').each(function (idx, elm) {
        var $this = $(this);
        var $value = $this.parent().find('.value');

        $this.progressbar({
            value: $this.data('value'),
            change: function (e) {
                $value.css('margin-left', $this.find('.ui-progressbar-value').width());
                $value.text($this.progressbar('value'));
            }
        });
        $value.css('margin-left', $this.find('.ui-progressbar-value').width());
    });
}

נמצא בעזרת jquery selector את כל ה – progress, עבור כל אחד מהם נשמור בצד את ה – האלמנט וה – div שמכיל את הטקסט כ – jquery objects.

כעת נהפוך כל אחד מהלאמנטים ל – progress bar, כשהערך של value יגיע ממה שנכתב על ה – div, ונרשם לארוע change שבו נשנה את הערך שכתוב על ה – label ונוודא שהמיקום שלו יהיה בהתאם לערך.

 

רק כדי שנראה שזה עובד נוסיף את הלחצן שיוסיף לכולם עוד 10 בכל לחיצה.

Code Snippet
function add10() {
    $('.progressbar').each(function (idx, elm) {
        var $this = $(this);
        var current = parseInt($this.progressbar('value'));
        $this.progressbar('value', current + 10);
    });
}

Control iis from code

בעבר כתבתי כיצד לעשות restart ל – application pool מקוד בעזרת DirectoryEnrty, לאחרונה הייתי צריך לייצר application עבור site קיים מקוד.

מסתבר שיש api טוב יותר מ – DirectoryEntry בשם Microsoft.Web.Administration

ה – dll יושב תחת: C:\Windows\SysWOW64\inetsrv\Microsoft.Web.Administration.dll או תחת system32 (תלוי במערכת ההפעלה)

לאחר מכן ניתן לכתוב קוד כזה:

Code Snippet
ServerManager iisManager = new ServerManager();
var site = iisManager.Sites["mySite"];
string currentPhysicalPath = site.Applications["/"].VirtualDirectories["/"].PhysicalPath;


var app = site.Applications.Add("/newApp", currentPhysicalPath + "/folder");
iisManager.CommitChanges();

 

 למאמר המסביר על ה – API (יש לשים לב שהוא מדבר על API ישן יותר המשתמש בפונקצית Update אשר לא קיימת יותר, ובמקומה יש את CommitChanges)

</DIV<

Posted: Apr 26 2013, 06:24 AM by Shlomo | with no comments
תגים:, ,

מתנדבים לשימוש במערכת שעות אינטרנטית

 

אני מחפש מתנדבים לשימוש במערכת שעות אינטרנטית שהעליתי בימים אלו לאוויר.

http://www.lpage.co.il/

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

  • האתר עדיין לא עבר עיצוב וגרפיקה.
  • השרת טיפה איטי.
  • נבדק כרגע רק בכרום.
  • היות שזה עדיין בפיתוח, בכל כמה ימים מתווספים פיצ'רים חדשים.

 

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

Posted: Apr 23 2013, 12:57 PM by Shlomo | with no comments
תגים:,

Send to mail, sql query result as csv file

התקבשתי לפתח תוכנה קטה שתדע להריץ שאילתא בבסיס נתונים, לקבל את התוצאה ולשלוח אותה במייל כקובץ csv.

מייד נכתוב אותה יחד. (ניתן להוריד קבצי מקור – כאן לא נראה את כל הקוד, רק את החלקים החשובים).

 

למעשה הפתרון שלנו יהיה מורכב משלושה פרוייקטים.

  • SqlReportLib, יכיל את כל הקוד עצמו (הלוגיקה השליחה במייל, ההרצה ב – DB.
  • SqlReportApp, אפליקצייה מסוג Console הגדירה את כל ההגדרות בקונפיג, ופשוט מריצה את הקוד.
  • UI, יודעת גם להריץ את הקוד, אך נותנת UI עבור הפרמטרים.

 

נתחיל:

בפרוייקט SqlReportLib יהיו לנו שלושה אובייקטים מרכזיים:

  • Dal – אחראי על העבודה מול בסיס הנתונים.
  • MailHelper - אחראי על שליחת המייל.
  • Worker – הוא המקשר בין כל האובייקטים.

במחלקת Worker ישנם שני פונקציות מרכזיות בשם Execute, הנראות כך:

Code Snippet
public static void Execute()
{
    WorkerParameters parameters = new WorkerParameters()
    {
        DalParams = ReadDalParamsFromConfig(),
        MailParams = ReadMailParamsFromConfig()
    };

    Execute(parameters);
}

public static void Execute(WorkerParameters parameters)
{
    ReadParametersFromConfig(parameters);

    DataTable table = DAL.GetData(parameters.DalParams);
    MailHelper.Send(parameters.MailParams, table);
}

המתודה הראשונה, לא מקבלת שום פרמטר, ויוצאת מתוך הנחה שכל המידע נמצא בקונפיג, המתודה השנייה מקבלת את האובייקט (או מאותחלת מהקונפיג בעזרת המתודה הקודמת, או עם פרמטרים כפי שנעשה מה – UI)

נסתכל לרגע על קובץ הקונפיג (הנמצאת באפליקצית SqlReportApp – שזוהי אפליקציית ה – Console, שלדוגמא רוצים להריץ אותה בעזרת windows scheduler)

Code Snippet
<connectionStrings>
  <add name="SqlReportConnection" connectionString="Data Source=.;Initial Catalog=hours;Integrated Security=True" />
</connectionStrings>

<appSettings>
  <!-- Text, StoredProcedure -->
  <add key="CommandType" value="Text"/>
  <add key="Query" value="SELECT * FROM Users"/>

  <add key="Body" value="Read the attach report, if you have any question, please mail back."/>
  <add key="CC" value="cc1@server.com"/>
  <add key="From" value="user@gmail.com"/>
  <!-- Embedded, Csv -->
  <add key="ReportType" value="Embedded"/>
  <add key="Subject" value="User Report"/>
  <add key="To" value="to1@gmail.com;to2@gmail.com"/>
</appSettings>

<system.net>
  <mailSettings>
    <smtp deliveryMethod="Network" from="user@gmail.com">
      <network enableSsl="true" host="smtp.gmail.com" port="587" userName="user@gmail.com" password="PWD" />
    </smtp>
  </mailSettings>
</system.net>

 

ראשית יש ה – ConnectioString שמכיל את הכתובת לבסיס הנתונים, לאחר מכן ב – AppSettings יש הגדרות עבור ה – Dal (CommandType, Query) כלומר מה השאילתא להרצה וכיצד להריץ אותה – האפליקצייה משתמשת ב Ado.net בסיסי, להסברים נוספים.

לאחר מכן יש פרמטרים עבור המייל (הפרטרים הם פשוטים, למי לשלוח, ממי וכד’ – הפרמטר היחידי שדורש הסבר זה ה – ReportType, שמגדיר האם הדוח יגיע כחלק מהמייל או יצורף כקובץ)

בסוף יש הגדרות smtp בדוגמא כאן נשתמש ב – gmail (כמובן שצריך להכניס שם משתמש וסיסמא של gmail)

 

נחזור למחלקות הפרמטרים שנראות כך: (הסברים בהמשך)

Code Snippet
public class WorkerParameters
{
    public DalParameters DalParams { get; set; }
    public MailParameters MailParams { get; set; }
}

public class NotifyObject : INotifyPropertyChanged
{
    protected void OnPropertyChanged(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }

    [field: NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;
}

public class DalParameters : NotifyObject
{
    private string connectionString;
    public string ConnectionString
    {
        get { return connectionString; }
        set
        {
            connectionString = value;
            OnPropertyChanged("ConnectionString");
        }
    }

    private string query;
    public string Query
    {
        get { return query; }
        set
        {
            query = value;
            OnPropertyChanged("Query");
        }
    }

    private CommandType commandType;
    public CommandType CommandType
    {
        get { return commandType; }
        set
        {
            commandType = value;
            OnPropertyChanged("CommandType");
        }
    }
}

public class MailParameters : NotifyObject
{
    private string from;
    public string From
    {
        get { return from; }
        set
        {
            from = value;
            OnPropertyChanged("From");
        }
    }

    private string to;
    public string To
    {
        get { return to; }
        set
        {
            to = value;
            OnPropertyChanged("To");
        }
    }

    private string cc;
    public string CC
    {
        get { return cc; }
        set
        {
            cc = value;
            OnPropertyChanged("CC");
        }
    }

    private string subject;
    public string Subject
    {
        get { return subject; }
        set
        {
            subject = value;
            OnPropertyChanged("Subject");
        }
    }

    private string body;
    public string Body
    {
        get { return body; }
        set
        {
            body = value;
            OnPropertyChanged("Body");
        }
    }

    private ReportType reportType;
    public ReportType ReportType
    {
        get { return reportType; }
        set
        {
            reportType = value;
            OnPropertyChanged("ReportType");
        }
    }

    public SMTPParameters SMTP { get; set; }
}

public class SMTPParameters : NotifyObject
{
    private string host;
    public string Host
    {
        get { return host; }
        set
        {
            host = value;
            OnPropertyChanged("Host");
        }
    }

    private int port;
    public int Port
    {
        get { return port; }
        set
        {
            port = value;
            OnPropertyChanged("Port");
        }
    }

    private string userName;
    public string UserName
    {
        get { return userName; }
        set
        {
            userName = value;
            OnPropertyChanged("UserName");
        }
    }

    private string password;
    public string Password
    {
        get { return password; }
        set
        {
            password = value;
            OnPropertyChanged("Password");
        }
    }
}

public enum ReportType
{
    Embedded,
    Csv
}

 

המחלקה הראשית (WorkerParameters) מכילה את שני המחלקות של הפרמטרים Mailparameters, DalParameters.

כל אחד מהם מכיל את ההגדרות שראינו בקונפיג – הסיבה שהם יורשות מ – NotifyObject וכל המאפיינים מרימים את האירוע בכל set של כל מאפיין, היא כדי לאפשר binding באפליקציית ה – ui (כפי שנראה בהמשך)

 

נחזור למתודת Execute

Code Snippet
public static void Execute(WorkerParameters parameters)
{
    ReadParametersFromConfig(parameters);

    DataTable table = DAL.GetData(parameters.DalParams);
    MailHelper.Send(parameters.MailParams, table);
}

 

דבר ראשון המתודה מקבלת DataTable מה – Dal, הקוד הוא מאוד פשוט (הגדרת הפרמטרים הפעלת המתודה והחזרת הטבלה).

Code Snippet
internal static class DAL
{
    private static SqlConnection connection;
    private static SqlCommand command = new SqlCommand();

    static DAL()
    {
        connection = new SqlConnection();
        command = new SqlCommand();

        command.Connection = connection;
    }

    internal static DataTable GetData(DalParameters parameters)
    {
        connection.ConnectionString = parameters.ConnectionString;
        command.CommandText = parameters.Query;
        command.CommandType = parameters.CommandType;


        connection.Open();
        DataTable table = new DataTable();
        table.Load(command.ExecuteReader());
        connection.Close();

        return table;
    }
}

 

בסוף מתודת GetData לאחר שיצרנו את הטבלה השתמשנו במתודת Load שיודעת לקבל כפרמטר DataReader ולבנות טבלה אוטומטית מכל המידע שנמצא בה.

 

לאחר שאנחנו מקבלים את הטבלה (במתודת Execute) אנחנו פונים ל – MailHelper ושולחים את הטבלה.

Code Snippet
internal static void Send(MailParameters parameters, DataTable table)
{
    MailMessage message = CreateMailMessage(parameters);
    AttachReport(message, table, parameters.ReportType);
    SmtpClient client = CreateSmtpClient(parameters);

    client.Send(message);
}

 

ראשית ניצור אובייקט מסוג MailMessage מתוך הפרמטרים, לאחר מכן נוסיף את הטבלה למייל (כקובץ או כחלק מהמיל) ולאחר מכן ניצור אובייקט מסוג SmtpClient ונשלח את המייל.

מתודת CreateMailMessage

Code Snippet
private static MailMessage CreateMailMessage(MailParameters parameters)
{
    MailMessage message = new MailMessage();
    message.From = new MailAddress(parameters.From);

    foreach (var item in parameters.To.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
    {
        message.To.Add(item);
    }

    if (parameters.CC != null)
    {
        foreach (var item in parameters.CC.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
        {
            message.CC.Add(item);
        }
    }

    message.Body = parameters.Body;
    message.Subject = parameters.Subject;
    message.IsBodyHtml = true;

    return message;
}

כדי לאפשר לשלוח ב – to, cc) ליותר מאחד, המחרוזת (בקונפיג) יכולה להכיל את התו ; ולכן יש כאן split למאפיינים הללו.

 

מתודת AttachReport נראית כך:

Code Snippet
private static void AttachReport(MailMessage message, DataTable table, ReportType reportType)
{
    if (reportType == ReportType.Csv)
        message.Attachments.Add(GetCsvFile(table));
    else
        message.Body += GetHtmlFromData(table);
}

private static string GetHtmlFromData(DataTable data)
{
    string tab = "\t";
    StringBuilder sb = new StringBuilder();

    sb.AppendLine("<html>");
    sb.AppendLine(tab + "<body>");
    sb.AppendLine(tab + tab + "<table>");

    // headers.
    sb.Append(tab + tab + tab + "<tr>");

    foreach (DataColumn dc in data.Columns)
    {
        sb.AppendFormat("<td>{0}</td>", dc.ColumnName);
    }

    sb.AppendLine("</tr>");

    // data rows
    foreach (DataRow dr in data.Rows)
    {
        sb.Append(tab + tab + tab + "<tr>");

        foreach (DataColumn dc in data.Columns)
        {
            string cellValue = dr[dc] != null ? dr[dc].ToString() : "";
            sb.AppendFormat("<td>{0}</td>", cellValue);
        }

        sb.AppendLine("</tr>");
    }

    sb.AppendLine(tab + tab + "</table>");
    sb.AppendLine(tab + "</body>");
    sb.AppendLine("</html>");

    return sb.ToString();
}

private static Attachment GetCsvFile(DataTable data)
{
    StringBuilder sb = new StringBuilder();

    foreach (DataColumn dc in data.Columns)
    {
        sb.AppendFormat("{0},", dc.ColumnName);
    }

    sb.AppendLine("\r\n");

    foreach (DataRow dr in data.Rows)
    {
        foreach (DataColumn dc in data.Columns)
        {
            string cellValue = dr[dc] != null ? dr[dc].ToString() : "";
            sb.AppendFormat("{0},", cellValue);
        }

        sb.AppendLine("\r\n");
    }

    MemoryStream ms = new MemoryStream();
    StreamWriter sw = new StreamWriter(ms, Encoding.UTF8);
    sw.Write(sb.ToString());
    sw.Flush();
    ms.Seek(0, SeekOrigin.Begin);

    Attachment att = new Attachment(ms, "Report.csv");
    return att;

}

 

ראשית נוודא האם רוצים לצרף כקובץ או לשלוח כחלק מה – body, במידה ואכן רוצים לשלוח כחלק מה – body, נקרא למתודת GetHtmlFromData, שרצה על הטבלה ומייצרת טבלת html שמתווספת ל – body של המייל, אחרת (כלומר ורצים את הדוח כקובץ מצורף) נוסיף למאפיין Attachments את הקובץ המגיע ממתודת GetCsvFile, הקוד במתודה זו מאוד דומה לקודמת, אבל בסוף נכניס את כל המידע לאובייקט מסוג MemeoryStream ונייצר אובייקט מסוג Attachment שיקבל את השם Report.csv.

לאחר כל התהליך, נייצר את ה – SmtpClient שיקבל את המאפיינים מהאובייקט (או מהקונפיג) ונשלח את המייל.

כדי להפעיל את כל הקוד, כל מה שנצטרך לעשות ב – Console Application לכתוב את הקוד הבא:

Code Snippet
static void Main(string[] args)
{
    try
    {
        Worker.Execute();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

כמובן שכדי שזה יעבוד הקונפיג צריך להיות מוגדר כמו שצריך.

 

כדי להיות נחמד יותר ללקוח, החלטתי גם לכתוב אפליקציית UI שתאפשר מילוי של הערכים (ואפילו טעינה ושמירה שלהם לקובץ).

לא נעבור כאן על הקוד (תוכלו כמובן להוריד את האפליקצייה) המסך נראה כך:

image

Basic C# - Random

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

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

 

Random rnd = new Random();

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

int n1 = rnd.Next();

int r2 = rnd.Next(100);

int r3 = rnd.Next(10, 30);

 

השורה הראשונה תחזיר משתנה מסוג int עם מספר אקראי שלם – כלומר מספר בין 0 ל – 2147483647, השורה הבאה תחזיר מספר בין 0 ל – 100, והשורה האחרונה תחזיר מספר בין 10 ל – 30.

נראה כעת דוגמת קוד קצת יותר אמיתית, נגיד שיש לנו את קוד ה – xaml הבא:

Code Snippet
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
       
    <Button Click="Button_Click">Add Rectangle</Button>
       
    <Canvas Grid.Row="1" Height="900" Width="900" x:Name="can" Background="Silver"></Canvas>
</Grid>

זה אמור לתת לנו את התמונה הבאה:

image

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

image

 

ראשית נראה כיצד מוסיפים ריבועים מקוד (בשלב זה ללא שום אקראיות)

 

Code Snippet
private void Button_Click(object sender, RoutedEventArgs e)
{
    Rectangle rect = new Rectangle();
    rect.Width = 50;
    rect.Height = 50;

    rect.Fill = new SolidColorBrush(Color.FromRgb(50, 147, 200));

    Canvas.SetTop(rect, 200);
    Canvas.SetLeft(rect, 200);

    can.Children.Add(rect);
}

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

הרצה של הקוד אמורה לתת את התוצאה הבאה:

image 

כעת בכל מקום שנתנו מספר נרצה לתת מספר אקראי והקוד יראה כך:

Code Snippet
private void Button_Click(object sender, RoutedEventArgs e)
{
    Random rnd = new Random();

    Rectangle rect = new Rectangle();
    rect.Width = rnd.Next((int)can.Width);
    rect.Height = rnd.Next((int)can.Height);

    byte r = (byte)rnd.Next(255);
    byte g = (byte)rnd.Next(255);
    byte b = (byte)rnd.Next(255);
    rect.Fill = new SolidColorBrush(Color.FromRgb(r, g, b));

    Canvas.SetTop(rect, rnd.Next((int)(can.Height - rect.Height)));
    Canvas.SetLeft(rect, rnd.Next((int)(can.Width - rect.Width)));

    can.Children.Add(rect);
}

 

בהשמה של הורחב והגובה, אנחנו מגדירים את המספר המקסימלי לרוחב והגובה של ה – Canvas, בהשמה של הצבע אנחנו מגדירים שלושה משתנים מסוג byte, היות שהפונקציה FromRgb מקבלת byte בלבד. ובסוף אנחנו מגדירים מיקום אקראי.

Posted: Mar 19 2013, 03:24 PM by Shlomo | with no comments
תגים:,

עבודה עם הדפדפן בפיתוח VsPlugIn

כשמפתחים Visual Studio plugin, לפעמים נרצה לפתוח את הדפדפן הפנימי של Visual studio, ולנווט אותו לדף כלשהו.

כדי לבצע זאת, נצטרך לקבל מופע של IVsWebBrowsingService (מתוך Microsoft.VisualStudio.Shell.Interop.dll, v7.1.40304.0), הדרך לקבל אותו היא בצורה דומה כפי שמקבלים הרבה שירותים פנימיים של הסביבה. בעזרת GetService

Code Snippet
IVsWebBrowsingService wbSvc =
    (IVsWebBrowsingService)ServiceProvider.GlobalProvider.GetService(typeof(SVsWebBrowsingService));

 

כעת למשתנה wbSvc, יש מספר פונקציות, המאפשרות לגלוש בצורות שונות, כדאי לעבור עליהם, אנחנו נתמקד בפונקציה היוצרת דפדפן.

Code Snippet
Guid guidNull = Guid.Empty;
uint dwCreateFlags = (uint)__VSCREATEWEBBROWSER.VSCWB_AutoShow | (uint)__VSCREATEWEBBROWSER.VSCWB_StartCustom;

IVsWebBrowser browser;
IVsWindowFrame browserFrame;
wbSvc.CreateWebBrowser(dwCreateFlags, ref guidNull, "", url, null, out browser, out browserFrame);

המשתנה dwCreateFlags מכיל את ההגדרה כיצד לייצר אותו, המשתנה browser מכיל (לאחר הקריאה של CreateWebBrowser) אובייקט שמאפשר להמשיך לנווט (כמובן שנשמור אותו כמשתנה גלובלי).

המשתנה browserFrame מכיל את האובייקט שעוטף את הדפדפן  (איתו נוכל לסגור את החלון כשנרצה).

 

הסיבה שבחרתי בדרך זו לייצר את הגלישה, היא מכיוון שבאחד מהפוסטים הבאים אדגים כיצד ניתן לגלוש ולהרשם מתוך ה – plugin לאירועים המתבצעים בתוך ה – html שאליו גלשנו.

קבלת הנתיב המלא באפליקציית MVC וב - WebForms

לפעמים צריך לקבל נתיב מלא לכתובת אינטרנט, לדוגמא כדי לשלוח במייל.

באפליקציית MVC הקוד יראה כך:

Code Snippet
public static class UrlHelperExtension
{
    public static string AbsolutePath(this UrlHelper url, string virtualPath)
    {
        Uri requestUrl = url.RequestContext.HttpContext.Request.Url;

        return string.Format("{0}://{1}{2}",
                                requestUrl.Scheme,
                                requestUrl.Authority,
                                VirtualPathUtility.ToAbsolute(virtualPath));
    }
}

ואילו השימוש יראה כך:

Url.AbsolutePath("~/Controller/action");

התוצאה תהיה כזאת “http://localhost:7791/Controller/action” (כמובן שבסביבה האמיתית התוצאה תהיה מתאימה).

 

קוד מאוד דומה יהיה ב - WebForms

Code Snippet
public static string AbsolutePath(this HttpRequest request, string virtualPath)
{
    Uri requestUrl = request.Url;

    return string.Format("{0}://{1}{2}",
                            requestUrl.Scheme,
                            requestUrl.Authority,
                            VirtualPathUtility.ToAbsolute(virtualPath));
}

 

הסיבה היחידה לא להשתמש באותה פונקציה גם ב – MVC, מכיוון שהמשתנה Url (שהוא מסוג UrlHelper) כבר מוגדר בכל ה – Views, וניתן להשתמש בו.

שאיבת משתמשים וקבוצות ממערכת ההפעלה

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

כדי לבצע זאת צריך להוסיף reference ל - System.DirectoryServices.AccountManagement.

הקוד יהיה פשוט ביותר, דוגמת הקוד הבאה תשאב את שמות הקבוצות

Code Snippet
private void BindGroups()
{
    if (Session["groups"] == null)
    {
        PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Machine);
        GroupPrincipal oGroupPrincipal = new GroupPrincipal(oPrincipalContext);
        PrincipalSearcher oPrincipalSearcher = new PrincipalSearcher(oGroupPrincipal);
        PrincipalSearchResult<Principal> oPrincipalSearchResult = oPrincipalSearcher.FindAll();

        Session["groups"] = oPrincipalSearchResult.ToList();
    }

    ddl.DataTextField = "Name";
    ddl.DataSource = Session["groups"];
    ddl.DataBind();
}

 

(במאמר מוסגר – מסתבר שניתן לשאוב גם מה – Active Directory בלי צורך להגיע לשאילתות LDAP – וכל השינוי בקוד יהיה ה – ContextType)

 

הקוד הבא יציג את כל המשתמשים של הקבוצה:

Code Snippet
protected void ddl_SelectedIndexChanged(object sender, EventArgs e)
{
    List<Principal> list = (List<Principal>)Session["groups"];

    Principal principal = list.FirstOrDefault(x => x.Name == ddl.Text);

    ddl2.DataTextField = "Name";
    ddl2.DataSource = ((GroupPrincipal)principal).GetMembers(true);
    ddl2.DataBind();
}

 

שליחת חלק מה – html למייל.

בעבר כתבתי על שליחת דף html מצורף למייל, כשהטכנולוגיה השלטת הייתה asp.net web forms, היום העולם כמובן השתנה, ואנחנו עובדים הרבה בצד הלקוח.

הדרישה הנוכחית הייתה לשלוח קטע html מסויים למייל ללקוח.

במידה ומספיק לשלוח את ה – html, ללא עיצוב הכול יותר פשוט, נניח שיש לנו את ה – html שנותן את התוצאה הבאה:

image

 

בלחיצה על הלחצן נכתוב את הקוד הבא: (בהנחה שאתם מכירים jQuery)

Code Snippet
function sendMail() {
    var str = escape($('#myDiv').parent().html());
    var obj = { data: str };
    
    $.post('home/send', obj);
}

הקוד ייקח את התוכן ה – html, נשתמש במתודת escape כדי לתמוך בעברית ולמנוע בעיות אבטחה ונשלח אותו לשרת.

בצד השרת נכתוב את הקוד הבא (נשתמש בשרת של gmail לשלוח מיילים)

Code Snippet
public void Send(string data)
{
    string str = GlobalObject.unescape(data);

    MailMessage mm = new MailMessage("from@server.co.il", "to@server.com");
    mm.Body = str;
    mm.Subject = "Mail from client";
    mm.IsBodyHtml = true;

    SmtpClient smtp = new SmtpClient();

    smtp.Send(mm);

}

הפונקציה unescape מגיעה מ - Microsoft.JScript.dll.

לאחר מכן נשלח פשוט את המייל, הגדרות ה – smtp יושבות בקונפיג.

 

Code Snippet
<system.net>
  <mailSettings>
    <smtp>
      <network host="smtp.gmail.com" port="587" userName="USERNAME" enableSsl="true" password="PASSWORD" />
    </smtp>
  </mailSettings>
</system.net>

 

זהו קוד פשוט, אבל במקרה שנרצה לשלוח קוד html מעוצב בעזרת css, נצטרך לייצר קובץ אמיתי ולצרף אליו את תוכן ה – css, הקוד יראה כך:

Code Snippet
public void Send(string data)
{
    string str = GlobalObject.unescape(data);
    string css1 = string.Format("<style> {0} </style> ",
                    File.ReadAllText(Server.MapPath("~/Content/Site.css")));
                   
    string all = css1 + str;


    MemoryStream ms = new MemoryStream();
    StreamWriter sw = new StreamWriter(ms);
    sw.Write(all);
    sw.Flush();
    ms.Seek(0, SeekOrigin.Begin);


    MailMessage mm = new MailMessage("from@server.co.il", "to@server.com");
    mm.Body = "This is a mail";
    mm.Subject = "Mail from client";
    mm.IsBodyHtml = false;

    mm.Attachments.Add(new Attachment(ms, "file.html", "text/html"));

    SmtpClient smtp = new SmtpClient();
    smtp.Send(mm);
}

 

כעת נקרא גם את תוכן קובץ ה – css שנרצה לצרף, ונייצר מחרוזת אחת גדולה עם תוכן ה – html/css.

נכתובת את כל התוכן לתוך MemoryStream בעזרת StreamWriter, ולאחר מכן נצרף את התוכן למייל ונקרא לקובץ בשם file.html, כעת מי שיקבל את המייל יפתח את הקובץ, הוא יראה תוכן html מעוצב.

 

משימה שלישית עם (3) knockout

בהמשך לפוסטים הקודמים על KO, נראה הפעם עבודה עם ולידציות.

נכתוב את ה – html הבא:

Code Snippet
<table id="tblContact">
    <tr>
        <td>שם:</td>
        <td>
            <input type="text" data-bind="value: Name" /></td>
    </tr>
    <tr>
        <td>אימייל:</td>
        <td>
            <input type="text" title="email" data-bind="value: Email" /></td>
    </tr>
    <tr>
        <td>תוכן הפנייה:</td>
        <td>
            <textarea rows="6" data-bind="value: Content"></textarea>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <input type="button" value="שליחה" data-bind="click: sendContact" />
        </td>
    </tr>
</table>

הקוד הנ”ל יביא לתוצאה הבאה:

image

 

כעת נרצה לוודא שלחיצה על “שליחה” תפעיל ולידציות לפני השליחה האמיתית לשרת.

כדי שזה יקרה נוסיף לפרוייקט את KO.Validation (כמובן שלפני זה נוודא שיש לנו KO)

 

קוד ה – JS יראה כך:

 

Code Snippet
var contactViewModel = ko.validatedObservable(
{
    Name: ko.observable('').extend({ required: true }),
    Email: ko.observable('').extend({ required: true, email: true }),
    Content: ko.observable('').extend({ required: true }),

    sendContact: function () {
        if (!this.isValid()) {
            this.errors.showAllMessages(true);
            return;
        }

        $.post(urls.CONTACT, JSON.parse(ko.toJSON(this)), function (res) {
            
        });
    }
})

 

עבור כל המאפיינים השתמשנו במתודת extend שמקבלת פרמטרים שונים המגדירים את הולידציות על המאפיינים (לפירוט המלא).

בזמן הפעלת הפונקציה sendContact, יש שימוש בשני מתודות isValid, errors.showAllMessages, שני מתודות אלו התווספו למודל עקב יצירת המודל בעזרת פונקציית validatedObservable.

במידה והמודל לא עבר ולידצייה, המסך יראה כך:

image

 

ברירת המחדל של הודעות השגיאה הם כמובן באנגלית, כאן יש קובץ עבור עברית.

במידה והכול עבר בהצלחה, נפנה לשרת ב – ajax עם המידע.

 

your mini url

כפי שהבטחתי בפוסט זה אספר על הפיתרון שאני בעזרת מכללת סלע מציע.

לפני הכול אציין שזו ביתא ראשונית, ויתכן באגים – כך שתקחו אותי בעדינות – אשמח כמובן לקבל פידבק –למייל: shlomog@sela.co.il

 

המערכת שאנחנו מפתחים נקראת (לעת עתה) yourminiurl, והיא תומכת ברשימת הפיצ’רים הבאים.

  • חינמי לחלוטין למשתמשים רגילים (אין כרגע תמיכה לחברות או למשתמשים מתקדמים).
  • שליטה מלאה על הכינוי.
  • סטסטיסטיקות מלאות על השימוש.
  • אפשרות לעריכה/מחיקה של קיצור הדרך.
  • כניסה למערכת באמצעות Facebook/Microsoft או באמצעות שם משתמש וסיסמא.
  • כמה חשבונות למשתמש אחד (מאפשר כמה קידומות לקיצור דרך – הסבר בהמשך).
  • API מלא לייצר ולתשאל מקוד. (עדיין לא קיים)
  • במידה ובעל החשבון מעוניין להציג פרסומת לפני הכניסה לאתר – יש באפשרותו להוסיף פרסומות (של בעל החשבון). (לא עובד במלואו).
  • אפשרות להגדיר את הדומיין שלכם עבור הקידומת לקיצור הדרך (קיים חלקית).

 

הכתובת לאתר היא: http://yourminiurl.com/editor.

המסך הראשון נראה כך (החלק החשוב):

image

 

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

בפעם הראשונה לאחר הכניסה תגיעו למסך הבא:

 

image

 

במסך זה יש צורך לבחור את הקידומת, כלומר בהנחה שתבחרו בקידומת shlomo, ולאחר מכן תייצרו קיצור דרך בשם blog שיפנה לבלוג שלכם, קיצור הדרך יראה http://yourminiurl.com/shlomo/blog (כפי שכתבתי – ניתן להגדיר את הדומיין שלכם במקום http://yourminiurl.com הוראות חלקיות כאן)

 

לאחר מכן תגיעו למסך יצירת קיצור דרך.

image

 

בקומבו של “חשבון” ניתן לבחור את החשבון עליו רוצים לעבוד (לכל משתמש אפשרי מספר חשבונות)

הנושא מאפשר לסדר את הקיצורים במסך הסטטיסטיקות והעריכה.

הכתובת היא הכתובת האמיתית.

קיצור הדרך – זהו הכינוי, ניתן לבחור פרסמות.

וזה הכול למעשה.

 

יש עוד מסכים של עריכה/פרסומות/סטטיסטיקות ועוד.

אני אשאיר לכם את הכיף בלשחק – ואודה לפידבק.

Posted: Jan 30 2013, 07:48 AM by Shlomo | with 3 comment(s)
תגים:

SerializationInfo TryGetValue

כתבתי מספר Extensions עבור מחלקת SerializationInfo.

Code Snippet
namespace System.Runtime.Serialization
{
    public static class SerializationExtensions
    {
        public static IEnumerable<SerializationEntry> AsEnumerator(this SerializationInfo info)
        {
            foreach (var item in info)
            {
                yield return item;
            }
        }

        public static T TryGetValue<T>(this SerializationInfo info, string name, T defaultValue = default(T))
        {
            foreach (var item in info)
            {
                if (item.Name == name)
                    return (T)item.Value;
            }

            return defaultValue;
        }

        public static T TryGetValue<T>(this SerializationInfo info, string[] names, T defaultValue = default(T))
        {
            foreach (var name in names)
            {
                foreach (var item in info)
                {
                    if (item.Name == name)
                        return (T)item.Value;
                }
            }

            return defaultValue;
        }
    }
}

 

כעת ניתן לכתוב קוד כזה:

Code Snippet
[Serializable]
class MyClass : ISerializable
{
    public string Name { get; set; }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Name", Name);
    }

    private MyClass(SerializationInfo info, StreamingContext context)
    {
        Name = info.TryGetValue<string>(new string[] { "<Name>k__BackingField", "Name" });
    }
}

 

המטרה של הקוד הנ”ל, היא כדי לאפשר בקלות להוציא את המידע במידה ועשיתם בעבר סריליזציה של המידע לפני שממשתם את הממשק ISerializable, מה שיגרום לכך שהשם יהיה השם של ה – backing field.

shortcut url, tiny url

ישנם הרבה מאוד שירותים (חינמיים ובתשלום) של shortcut url, 

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

מה זה אומר:

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

לדוגמא: בהנחה שיש לנו את הכתובת הבאה: http://blogs.microsoft.co.il/blogs/shlomo/ שזו הכתובת של הבלוג שלי, כמובן. הגדרתי עבורו (במנגנון של google) את הכינוי http://goo.gl/LZZRv, כעת מי שיגלוש ללינק המקוצר יגיע למעשה לבלוג שלי (כמובן שבשורת הכתובת יראו תמיד את הכתובת האמיתית).

למה צריך את זה בכלל:

רשימה קצרה של דוגמאות למה שנרצה כזה שירות ומה נקבל מזה שנשתמש בזה.

  • היכולת שלנו להחזיק כתובת קצרה במקום כתובת ארוכה, זה עוזר במיילים, בהודעות טקסט (sms) בשמירה של נתונים (DB) וכמובן בזכירה של הכתובת (לגבי הזיכרון, זה בדרך כלל רלוונטי רק אם אפשר לשלוט על יצירת הכינוי)
  • היכולת לשנות להיכן הכינוי מצביע, לדוגמא: בהנחה שלסלע יהיה קיצור דרך בשם http://sela.co.il/sdp, כשלמעשה הכינוי SDP לא יהיה דף אמיתי, אלא כינוי לדף אחר, בכל פעם שסלע תרצה לארגן כנס חדש, הם לא יצטרכו לשלוח כתובות חדשות, או לדאוג לכך שמישהו נכנס בטעות לאתר של הכנס הקודם, הרבה יותר פשוט יהיה, לשנות את ההפנייה של הכינוי SDP לאתר העדכני (כמובן לא עוזר במידה ומישהו העתיק את הכתובת האמיתית שמופיע בדפדפן).
  • היכולת לקבל בקלות סטטיסטיקות מי ומהיכן הגיעו לעמוד (היות שכדי להגיע לעמוד האמיתי צריך לעבור דרך איזשהו מתווך, הוא יכול לעשות עוד כמה דברים בדרך – כמו למשל שמירת סטטיסטיקות).
  • הרבה שירותים משתמשים בו לצורכי פרסומות (ולהרויח כסף – כמובן), בזמן מעבר לדף האמיתי, הגולש המסכן נאלץ להסתכל בפירסומת כלשהו, יש שירותים ברשת המאפשרים לכל אחד להרויח כסף על ידי יצירת קיצורי דרך דרכם, עבור כל גולש שאולץ להסתכל בפרסומת, יוצר קיצור הדרך מקבל X אחוזים.
  • היכולת למחוק קיצורי דרך – בהינתן שרוב האנשים לא שומרים את הכתובת מהדפדפן, זאת דרך די טובה להעלים כתובות שאנחנו לא רוצים שיסתכלו עליהם יותר.
  • חלק גדול מהשירותים הקיימים מאפשרים לייצר קיצורי דרך מקוד, (חושפים API) מה שכמובן נותן למפתחים יותר כוח).

הפתרונות הקיימים: (הנפוצים שבהם)

Service URL Features & Benefits
Google url shortener http://goo.gl/ Free
    Statistics
     
     
    URL Masking
    Statistics
    Link Referrer reports
    Path Forwarding
    Sub.Sub.Domains
    Mirror management
    No confusing characters ( 0, O, I, l, 1 )
    Preview
    WWW support
    Frame Kill
    Non-Public WHOIS
     
bitly https://bitly.com/ Free
    Grouping
    Statistics
    Chrome Extension
    Public/Private
    Developers API
    Save by mail (email link to save@bitmark.me)
     
TinyURL http://tinyurl.com/ Free
    Preview
     
ow.ly http://ow.ly Free
    File Uploading
    Developers API
     
Tiny http://tiny.cc/ Free
    Statistics
    Custom URL
    Preview
    QR Codes
    Link Referrer reports
    Traffic filter
    Link expiration
    Export links & stats
    Developers API
     
MacAfee http://mcaf.ee/ Free
    Secure Links
     
Cligs http://cli.gs/ Free
    Custom URL
    Statistics
     
Yep http://yep.it/ Free
    Developers API
     
surf.to http://www.surf.to/ Free
    Email Forwarding
    Multiple Addresses
     
thinfi http://thinfi.com/ Free
    Password Protection

 

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

Posted: Jan 27 2013, 01:01 AM by Shlomo | with 1 comment(s)
תגים:

DateTime in JSON

אחד הדברים המעצבנים בעבודה עם ajax, זה הדרך שבה חוזר אובייקט DateTime.

בהנחה שהאובייקט נראה כך:

 

Code Snippet
public class Person
{
    public int MyProperty { get; set; }
    public DateTime Time { get; set; }
}

 

ויש לנו Action הנראה כך:

 

Code Snippet
[HttpGet]
public JsonResult GetPerson()
{
    return Json(new Person()
    {
        MyProperty = 2,
        Time = DateTime.Now
    }, JsonRequestBehavior.AllowGet);
}

 

כשנקרא ל – action בעזרת jQuery ajax:

 

Code Snippet
var personFromServer = null;

// Get data from server
$.getJSON('home/GetPerson', function (data) {
    personFromServer = data;
});

 

מה שנקבל בחזרה יראה:

 

Code Snippet
personFromServer = {
    MyProperty: 2,
    Time: "/Date(1358020217238)/"
};

 

מה שבהחלט יכול להציק, מכיוון שצריך להמיר את המחרוזת לאובייקט Date של javascript.

אחד מהחברה הטובים של roomixer הפנה אותי לבעיה זו, והנה הפתרון שלנו.

 

הבעיה נובעת מכך שהקוד הפנימי של Json יוצר אובייקט מסוג JavaScriptSerializer, ולא חושף אותו החוצה, במידה והם היו חושפים אותו החוצה ניתן היה להוסיף לו JavaScriptConverter עבור כל type שנרצה, ולכן ראשית נכתוב אובייקט חדש שיורש מ – JsonResult.

 

Code Snippet
public class JsonResultWithConverters : JsonResult
{
    public List<JavaScriptConverter> Converters { get; private set; }

    public JsonResultWithConverters(params JavaScriptConverter[] converters)
    {
        Converters = new List<JavaScriptConverter>(converters);
    }

    public override void ExecuteResult(ControllerContext context)
    {
        HttpResponseBase response = context.HttpContext.Response;
        if (!string.IsNullOrEmpty(this.ContentType))
        {
            response.ContentType = this.ContentType;
        }
        else
        {
            response.ContentType = "application/json";
        }
        if (this.ContentEncoding != null)
        {
            response.ContentEncoding = this.ContentEncoding;
        }
        if (this.Data != null)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            serializer.RegisterConverters(Converters);
            response.Write(serializer.Serialize(this.Data));
        }
    }
}

 

למעשה העתקתי את כל הקוד של המימוש הפנימי של JsonResult (בעזרת relector כמובן), מה שהוספתי הוא שב – ctor של המחלקה אני מאפשר לשלוח מערך של JavaScriptConverter ובשורה אחת לפני האחרונה אני שולח אותם ל – JavaScriptSerializer.

(על הדרך גם התפטרנו מהצורך לשלוח כל פעם את הערך AllowGet.)

כעת הקוד יראה כך:

 

Code Snippet
[HttpGet]
public JsonResult GetPerson()
{
    return new JsonResultWithConverters(new DateTimeJSONConverter())
    {
        Data = new Person()
        {
            MyProperty = 2,
            Time = DateTime.Now
        }
    };
}

 

מיד נראה את ה – Converter שלנו, אך בהינתן שהוא יודע להמיר תאריכים, הקוד אמור לעבוד עכשיו.

 

Code Snippet
public class DateTimeJSONConverter : JavaScriptConverter
{
    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            return new List<Type>() { typeof(DateTime) };
        }
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        return new Dictionary<string, object>()
        {
            {
                "Value", ((DateTime)obj).ToString("dd/MM/yyyy hh:mm:ss")
            }
        };
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        string str = dictionary["Value"].ToString();
        return DateTime.Parse(str);
    }
}

 

המאפיין הראשון Supportedtypes מחזיר את כל ה – types שה – converter הנוכחי אחראי עליו (במקרה זה רק DateTime),

המתודה השנייה נגיע אליה בזמן סרילזצייה, והיא תמיר את התאריך לפורמט שנרצה, המתדוה האחרונה נגיע אליה בזמן שיחזור ממחרזות, ונמיר בחזרה ל – DateTime, כעת כשנריץ מ – javscript נקבל:

 

Code Snippet
personFromServer = {
    MyProperty: 2,
    Time: {
        Value: '12/01/2013 10:34:37'
    }
};

 

הבעייה כרגע היא איך נחזיר את המידע לשרת, הרי הוא לא יודע שהוא צריך לעבוד עם ה – JavaScriptConverter שכתבנו.

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

 

Code Snippet
public void SetPerson(string person)
{
    JavaScriptSerializer jss = new JavaScriptSerializer();
    jss.RegisterConverters(new JavaScriptConverter[] { new DateTimeJSONConverter() });

    var obj = jss.Deserialize<Person>(person);
}

 

ובצד הלקוח נשלח מחרוזת לשרת, כדי שיעשה המרה נכונה מהמחרוזת,

 

Code Snippet
$.post('home/SetPerson', { person: JSON.stringify(person) });

 

כעת כשהמידע יגיע לשרת בצורת מחרוזת, הוא יגיע למתודה שלנו ואנחנו נדאג להמיר אותו בצורה הנכונה בעזרת ה – Converter שלנו.

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

 

Code Snippet
<system.web.extensions>
  <scripting>
    <webServices>
      <jsonSerialization>
        <converters>
          <add name="DateTimeConverter" type="MvcApplication4.Controllers.DateTimeJSONConverter"/>
        </converters>
      </jsonSerialization>
    </webServices>
  </scripting>
</system.web.extensions>

 

ובהנחה שעל ה – WebService יהיה את ScriptServiceAttribute, ה – converter שלנו יעבוד אוטומטית בלי צורך לעשות כלום (בשני הכיוונים), אבל ב – action של mvc זה לא עובד כך.

כדי לפתור זאת גם ב – mvc נוסיף model binder, זה מאפשר לנו לשלוט על הדרך בה אנחנו מפרסרים את המידע שמתקבל מצד הלקוח, הקוד יהיה כזה

 

Code Snippet
public class DateTimeModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        string name = bindingContext.ModelName;
        return DateTime.Parse(bindingContext.ValueProvider.GetValue(name + "[Value]").AttemptedValue);
    }
}

 

כמובן שכדי לדאוג שנגיע למימוש בכל פעם שיש DateTime מצד הלקוח, נרשום אותו ב – global.asax, כפי שמיד נראה.

לפני כן נשים לב למשתנה name הערך שלו יהיה Time, וכדי להוציא את הערך הפנימי מוסיפים לו את המילה Value (שזה השם של המשתנה ב – Dictionary שהוכנס בתהליך הסירלזציה.

את השורה הבאה צריך לרשום ב – Application_Start

Code Snippet
ModelBinders.Binders.Add(typeof(DateTime), new DateTimeModelBinder());

Get Selected Text and Position – VS plug in

לפעמים צריך בתוך vs plug in לקבל את ה – selected text והמיקום שלו.

הדרך הפשוטה לכאורה לקבל את המידע היא השורה הבאה:

Code Snippet
DTE2 dte = (DTE2)ServiceProvider.GlobalProvider.GetService(typeof(DTE));
            
if (dte.ActiveDocument != null)
{

    var selection = (EnvDTE.TextSelection)dte.ActiveDocument.Selection;

די בקלות אפשר להוציא את הטקסט הנבחר, אבל זה ממש לא פשוט לדעת את ה – index המדוייק שהטקסט מתחיל ומסתיים, לקריאה נוספת איך בכל זאת לעשות זאת.

 

הדרך הקלה יותר היא זאת:

Code Snippet

IVsTextView textView = GetTextView(dte);

string selectedText;
textView.GetSelectedText(out selectedText);

int piAnchorLine, piAnchorCol, piEndLine, piEndCol;
textView.GetSelection(out piAnchorLine, out piAnchorCol, out piEndLine, out piEndCol);

int startPosition, virtualPosition;
textView.GetNearestPosition(piAnchorLine, piAnchorCol, out startPosition, out virtualPosition);

int endPosition;
textView.GetNearestPosition(piEndLine,piEndCol, out endPosition, out virtualPosition);


TextSelection textSelection = new TextSelection()
{
    Text = selectedText,
    StartIndex = startPosition < endPosition ? startPosition : endPosition,
    EndIndex = startPosition < endPosition ? endPosition : startPosition
    
};

ראשית נקבל אובייקט מסוג IVsTextView (בהמשך נראה את המימוש), לאחר מכן נפעיל את פונקציית GetSelectedText ונקבל את הטקסט הנבחר.

כדי לקבל את השורה והעמודה שהטקסט מתחיל ומסתיים, נפעיל את פונקציית GetSelection, ולבסוף כדי לקבל את המיקום המדוייק, נשתמש ב – GetNearestPosition.

במידה והמשתמש בחר את הטקסט מימין לשמאל – ה – startPosition יהיה גדול מה – endPosition, ולכן נהפוך ביניהם.

 

הדבר האחרון שנשאר לראות הוא – כיצד מקבלים TextView מתוך ActiveDocument, למעשה יש כמה דרכים, כאן מצאתי אחת קצרה

Code Snippet
private static IVsTextView GetTextView(DTE2 dte)
{
    ServiceProvider sp = new ServiceProvider((Interop.IServiceProvider)dte);

    IVsUIHierarchy uiHierarchy;
    uint itemID;
    IVsWindowFrame windowFrame;

    VsShellUtilities.IsDocumentOpen(sp, dte.ActiveDocument.FullName,
                                    Guid.Empty, out uiHierarchy,
                                    out itemID, out windowFrame);

    IVsTextView textView = VsShellUtilities.GetTextView(windowFrame);
    return textView;
}

More Posts Next page »