יום שני, 11 באוקטובר 2010

יצירת User Control ושימוש ב-delegate


1) ניצור תוכנית רגילה Windows Application, ניתן שם לטופס ברירת המחדל: mainFrm.cs

2) לחיצה ימנית על solution, ואז addnew project → Windows Control Library
    ייווצר
User Control, נבחר ונשנה לו את השם – לדוגמה WCL, נוסיף לחצנים, תיבות וכדומה.

3) כעת ניתן להוסיף את הפרויקט WCL ב-reference של הטופס הראשי,
    אך כדי שיופיע גם ב-
Toolbox נעשה build לפחות ל-WCL.

4) לחיצה ימנית על reference שנמצא בבלוק של הטופס הראשי, נבחר Add Reference….
     נוסיף את ההפניה לפרויקט  שיצרנו בלשונית Projects או נבחר קובץ dll חיצוני בלשונית Browse

5) UserControl שיצרנו אמור להופיע ב-Toolbox.
    אם לא, נוסיף אותו ע"י לחיצה ימנית ←
Choose Items ← לשונית Net Framework ←נלחץ Browse
    ונבחר בקובץ ה-
dll שנמצא בפרויקט, בדרך כלל ב- ProjectName/SolutionName/bin/debug
   או  במקרה שלנו:
UcApp/UcApp/bin/debug




שימוש ב-delegate
עד כאן יצרנו UserControl והוספנו אותו לטופס הראשי בתור פקד.


כדי ליצור תקשורת בין אותו פקד לטופס הראשי נשתמש ב-delegate :
(את הקוד הבא נוסיף בתוך ה-UserControl)
public delegate void mydelegate(string s);
public event mydelegate strsent;
public virtual void onSendString(string s)
{
            strsent(s);
}

נלך לטופס הראשי, נבחר ב-WCL שמיקמנו בטופס, נבחר ב-Properties ואז נבחר בצורת הברק כדי להיכנס להגדרת 'Events' ונמצא את השם שהגדרנו ל-Event שיצרנו - כלומר strsent .
נלחץ לחיצה כפולה ובתוכו נגדיר אילו פעולות יתבצעו על המחרוזת שנקבל בהתרחש האירוע. למשל:
(הקוד הבא נמצא בטופס הראשי)
private void userControl11_strsent(string s)
{
            MessageBox.Show(s);
}


עד כאן הגדרנו לטופס הראשי שבהתקבל מחרוזת מתוך הפקד WCL שהגדרנו, אזי תוקפץ הודעה עם המחרוזת.
כעת עלינו לקשר בין הכפתור שנמצא ב-WCL לבין שליחת המחרוזת:
(הקוד הבא נמצא ב-UserControl)
private void button1_Click(object senderEventArgs e)
{
            onSendString(textBox1.Text);
           // strsent(textBox1.Text);
}


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


ניתן להוריד את ההסבר כולו להדפסה בתמונה:




ProcessDir - פונקציה רקורסיבית לסריקת תיקיות



//define at the program head
int  HowDeepToScan = 3;  //how deep the algo should enter:
string file_ext = ".exe";   //the file extension
List<string> fName_list = new List<string>(); //save the file list in this 'List<string>'


public void ProcessDir(string sourceDir, int recursionLvl)
{
            if (recursionLvl <= HowDeepToScan)
            {
                // Process the list of files found in the directory. 
                string[] fileEntries = Directory.GetFiles(sourceDir);

                foreach (string fileName in fileEntries)
                {
                    // do something with fileName
                    if (fileName.ToLower().Contains(file_ext))
                        fName_list.Add(fileName);
                }


                // Recurse into subdirectories of this directory.
                string[] subdirEntries = Directory.GetDirectories(sourceDir);

                foreach (string subdir in subdirEntries)
                {
                    // Do not iterate through reparse points
                    if ((File.GetAttributes(subdir) &
                          FileAttributes.ReparsePoint) !=
                            FileAttributes.ReparsePoint)
                        //than, recurs: 
                        ProcessDir(subdir, recursionLvl + 1);
                }
            }
}


הפונקציה הזאת מקבלת שם תיקיה ראשית ומספר המציין את עומק הכניסה של האלגוריתם אל תוך תיקיות המשנה, ומבצעת פעולה כלשהיא בתוך תיקיות המשנה.
קריאה לפונקציה תהיה למשל:
ProcessDir("c:\\", 1);
בקוד המוצג למעלה הדגמתי ריצה על קבצים exe
במקרה ששם הקובץ מכיל (מסתיים) עם סיומת  .exe
אז תוסיף אותו לרשימה המכילה את שמות הקבצים.



Drag -n- Drop - 'זריקת' קבצים אל תיבת רשימה (listbox) או תיבת טקסט


קובץ דוגמא:  
https://sites.google.com/site/elsnir/Home/dragDrop.Example.zip

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

ראשית עלינו לבחור control-פקד שיכול לשמש אותנו. דוגמא ראשונה תהיה ListBox.
בחלונית צד Properties נשנה את אפשרות AllowDrop ל-True.
נלחץ על ה'ברק' בראש החלונית כדי לעבור להגדרת Events ונבחר באפשרויות DragEnter, DragDrop.
נגדיר אותן כך:

private void listBox1_DragEnter(object sender, DragEventArgs e)
{
   if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
   {
      e.Effect = DragDropEffects.Copy; //.All;
   }
}

private void listBox1_DragDrop(object sender, DragEventArgs e)
{
   foreach (string fileName in 
           (string[])e.Data.GetData(DataFormats.FileDrop))
   {
      listBox1.Items.Add(fileName);
   }
}


עבור TextBox - תיבת טקסט נשנה רק את האירוע השני, DragDrop:

private void textBox1_DragDrop(object sender, DragEventArgs e)
{
   textBox1.Text
         ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
}
נשים לב לחלקים שהדגשתי:
נקבל את המידע מאירוע ה'זריקה' של הקבצים על הטופס בתור מערך של שמות הקבצים.
כיוון שכאן מתבצעת הזריקה על תיבת טקסט פשוטה היכולה להכיל שם קובץ אחד בלבד נבחר באיבר הראשון [0] שיש במערך.
ניתן כמובן להגדיר תיבת טקסט בעלת אפשרות שורות מרובות Multiline ולשנות את הקוד כך שתיבת הטקסט תקבל את כל שמות הקבצים הנזרקים עליה, עם הפרדה של רווחים בין השמות.
}




קבע את האייקון של התוכנה


 - הוסף אייקון כלשהו לפרויקט .
    בחלונית-צד Properties של האייקון, שנה את Build Action
    (בשורה הראשונה)
    ובחר ב- Embedded Resource  ('מקור' משובץ) -
    כלומר, כאן בחרנו שהאייקון יצורף לתוכנית בזמן  העיבוד.

- קביעת האייקון של החלונית עצמה תיעשה ע"י
    פתיחת המאפיינים (Properties) של החלונית עצמה בתצוגת עיצוב
    ובחירת סמל באפשרות Icon.
    לחלופין, ניתן לעשות זאת ע"י הקוד:
this.Icon = Properties.Resources.my_icon;
 או ע"י:
this.Icon = global::myprogram_namespace.Properties.Resources.my_icon
 


- עבור בחירת הסמל של קובץ התוכנה,
   בחר ב-Properties עבור הפוריקט עצמו   (ע"י לחיצה ימנית)
   בלשונית הראשונה (Application) בשורה הלפני-אחרונה תהיה אפשרות Icon and manifest.
   עבור אפשרות-משנית Icon תוכל לבחור בין האייקונים שכבר הוספת לתוכנית בתור Resource.

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



העלמת אייקונים משולחן העבודה - #C


בעבר הרחוק מצאתי (nאוד נוח) אפליקציה קטנה שבמקרה שהעכבר לא נמצא מעל שולחן העבודה מעלימה את כל האייקונים, ומחזירה אותם כאשר העכבר חוזר להיות מעל שולחן העבודה.
http://www.donationcoder.com/Software/Skrommel/index.html#HideDesktop

לאחר שוטטות קצרה בפורומים של MSDN מצאתי שהעלמת/החזרת האייקונים יכולה להיעשות בשפת #C (או ב-NET. בכלל) בעזרת שימוש ב-DLL של מערכת ההפעלה.
ראשית יש להשתמש בספרייה:
using System.Runtime.InteropServices;
ולהוסיף את הקוד -
[DllImport("user32.dll")]
    static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
     static extern IntPtr FindWindowEx(IntPtr hwndParent,
     IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

private void checkBox1_CheckedChanged
                                                      (object sender, EventArgs e)
{
    IntPtr hWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, 
                                                                       "Progman", null);

    if (checkBox1.Checked)
        ShowWindow(hWnd, 0);
    else
        ShowWindow(hWnd, 5);
}
כפי שאפשר לראות הקוד מיושם ע"י  תיבת סימון (CheckBox).
אפשר לשנות את האפשרות מתיבת סימון ללחצן, או כל פקד אחר,
או להגדיר טיימר עבור כמות הזמן שהעכבר נמצא מעל שולחן העבודה למשל, וכו'.

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

עם זאת, ידוע שהאפשרות קיימת ידנית ע"י תפריט-לחצן-ימני על שולחן העבודה, אבל אני רוצה שהפעולה תהיה אוטומטית, ובכך להחליף את התוכנה של skrommel
http://www.donationcoder.com/Software/Skrommel/index.html#HideDesktop



יצירת תוכנת מייל C# - mail client


נתייחס לג'ימייל, אך השימוש נכון גם לכל שרת מייל אחר.
מיקמתי את הקוד המלא  כאן:

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



ראשית, נוסיף בראש הדף התייחסויות שימוש במחלקות של רשת ומייל:

using System.Net;
using System.Net.Mail;
את הקוד כדאי לתחום בתוך try..catch, כדי למנוע נפילות של התוכנית במקרה פשוט, למשל- מקרה של חוסר חיבור לאינטרנט.
כעת אציג את כל הקוד של הפעולה הבסיסית של לחיצה על 'שלח':
MailMessage msg = new MailMessage();
SmtpClient myServer = new SmtpClient("smtp.gmail.com", 587);
myServer.EnableSsl = true; // SSL ג'ימייל דורש
SmtpServer.Credentials = new NetworkCredential("user@gmail.com", "password");

msg.From = new MailAddress(txtb_from.Text, txt_name.Text, System.Text.Encoding.UTF8);

נשים לב, כי ג'ימייל דורש חיבור SSL. מעבר לכך הגדרנו את הקידוד של הדף ל-UTF8 כדי שיישלח בעברית קריאה.
לשם הוספת כתובות רבות לשליחה אליהן, נשתמש בטריק. נקבע שהכתובות יופרדו בפסיקים (,), ונחלק את המחרוזת. ()Trim מסיר רווחים מיותרים:
String[] adresses = txtb_to.Text.Split(',');
for (int i = 0; i &lt; adresses.Length; i++)
      msg.To.Add(new MailAddress(adresses[i].Trim() ) );

נמשיך בהוספת נושא מייל, ואת גוף המייל:
msg.Subject = txtBox_Subject.Text;
msg.Body = richTextBox.Text; 
msg.ReplyTo = new MailAddress(txtb_From.Text);

msg.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;
msg.DeliveryNotificationOptions = DeliveryNotificationOptions.OnSuccess;
SmtpServer.Send(msg);

כדאי להוסיף לסוף הקוד חיווי שנדע שהמייל נשלח בהצלחה:
MessageBox.Show("מייל נשלח בהצלחה");
וכמו כן להוסיף חיווי על כשלון בשליחה, בתוך catch: 
catch (System.Exception ex)
{
    MessageBox.Show("שגיאה - \n" + ex.Message);
}

הוספת קבצים למייל - באחד הפוסטים הבאים.

הקוד המלא:
private void btn_Send_Click(object sender, EventArgs e)
{
   try
   {
      MailMessage msg = new MailMessage();
      SmtpClient SmtpServer = new SmtpClient("smtp.gmail.com", 587);
      SmtpServer.EnableSsl = true; 
 
      SmtpServer.Credentials = new NetworkCredential(
                           txtb_Account.Text.Trim().ToString(), 
                           txtb_Password.Text.Trim().ToString());

      msg.From = new MailAddress(
                           txtb_From.Text.Trim(), txtb_Name.Text, 
                           System.Text.Encoding.UTF8);

      String[] adresses = txtb_To.Text.Split(',');
      for (int i = 0; i &lt; adresses.Length; i++)
      msg.To.Add(new MailAddress(adresses[i].Trim()));

      msg.Subject = txtb_Subject.Text;
      msg.Body = richTextBox1.Text;
      msg.ReplyTo = new MailAddress(txtb_From.Text.Trim());

      msg.DeliveryNotificationOptions = 
                           DeliveryNotificationOptions.OnFailure;
      msg.DeliveryNotificationOptions = 
                           DeliveryNotificationOptions.OnSuccess;

      SmtpServer.Send(msg);
   }
   catch (System.Exception ex)
   {
      MessageBox.Show("error - " + ex.Message);
   }
}
קוד לקבלת מייל -
כדי להתחבר לחשבון גימייל עם תוכנה פשוטה צריך קודם כל לאפשר "גישה לאפליקציות ברמת אבטחה נמוכה" בג'ימייל, אחרת נקבל תשובה ששם המשתמש או הסיסמה אינן נכונים.
אבל,
כדי לשמור על אבטחת החשבון, החל מתאריך 30 במאי 2022 תפסיק ​​Google לתמוך באפליקציות או במכשירים של צד שלישי שבהם אפשר להיכנס לחשבון Google באמצעות הזנה של שם המשתמש והסיסמה בלבד.
כלומר כדי לכתוב תוכנת מייל עם ג'ימייל יהיה צורך לעבוד עם google api.
עד אז... אפשר להשתמש בקוד הבא.
protected void ReadMail(string address, string password, string server = "pop.gmail.com")
{
    // create an instance of TcpClient 
    TcpClient tcpclient = new TcpClient();
    tcpclient.Connect(server, 995);
    System.Net.Security.SslStream sslstream = new SslStream(tcpclient.GetStream());
    sslstream.AuthenticateAsClient(server);
    StreamWriter sw = new StreamWriter(sslstream);
    System.IO.StreamReader reader = new StreamReader(sslstream);
    sw.WriteLine("USER " + address);
    sw.Flush();
    sw.WriteLine("PASS " + password);
    sw.Flush();
    sw.WriteLine("RETR 1");
    sw.Flush();
    sw.WriteLine("Quit ");
    sw.Flush();
    string str = string.Empty;
    string strTemp = string.Empty;
    int max_emails = 20;
    while ((strTemp = reader.ReadLine()) != null || max_emails >= 0)
    {
      if (".".Equals(strTemp))
      {
          break;
      }
      if (strTemp.IndexOf("-ERR") != -1)
      {
          textBox1.Text += strTemp;
          break;
      }
      textBox1.Text += strTemp + Environment.NewLine;
      listView1.Items.Add(str);

      max_emails--;
      if (max_emails <= 0) break;
    }
    //Console.Write(str);
    reader.Close();
    sw.Close();
    tcpclient.Close(); // close the connection
}
מידע על משמעות מילות הקוד של שליחת/קבלת מייל:
https://www.codeproject.com/Articles/14304/POP3-Email-Client-NET-2-0

טיפים שימושיים - #C


הפעלת תוכנית או כתובת אינטרנט ע"י התוכנית שמוגדרת כברירת מחדל:
System.Diagnostics.Process.Start("http://elsnir.blogspot.com");
System.Diagnostics.Process.Start("c:\\Program Files");
פקודה זו תריץ את  מה שיינתן לה בין הסוגריים, כאילו כתבתם את הפקודה ב'התחל'&gt;הפעלה' והרצתם.
למשל, הפקודה הראשונה תפתח את הכתובת של הבלוג שלי בדפדפן האינטרנט שמוגדר כברירת מחדל,
הפקודה השניה תפתח חלון ספרייה של Program Files. ניתן לכתוב כתובת של תוכנה במחשב, למשל.

שימו לב ל'סלש' כפול.


פקודת יציאה ישירה מתוך userControl:
((Form)this.Parent).Close();


הוספת תכונות משפה אחרת: VisualBasic
בסי-שארפ קיימת האפשרות הפשוטה להציג חלונית הודעה (MessageBox) אך מעבר לקבל אירועי לחצנים - כן,לא,ביטול וכו' - לא ניתן לקבל מחרוזת טקסט למשל.
אפשרות זו קיימת ב-VB :
ראשית עלינו להוסיף הפניה ל-VB ע"י פתיחת לשונית References, בחירה ב: Add Reference ובלשונית הראשונה NET. לבחור ב- Microsoft.VisualBasic .
כעת הוספנו יכולת שימוש בפקדים ובתכונות של VB.

הקוד להוספת חלונית המקבלת מחרוזת טקסט:


string str
   Microsoft.VisualBasic.Interaction.InputBox("הקלד כאן מחרוזת:",
                                                                "כותרת החלונית"
                                                                "ערך ברירת מחדל",
                                                                100, 100);

if (str.Trim().Length &gt; 0)
{
    AddTo_ListView(str);
    AddTo_ContextMenuStrip_FromPath(str);
}

המספרים 100,100 הם המיקום ההתחלתי של החלונית.





מזער תוכנית לשורת המערכת





מזעור חלון לשורת המשימות:1. פתח Windows Form קיים (או צור חדש).
    נניח ששמו Form1 .

2. פתח את Visual Studio Toolbox (בחירת control – פקדים) וגרור אל היישום שלך את NotifyIcon.
הפקד ימוקם בתחתית החלון , כיוון שאין לו דרך תצוגה על היישום עצמו.

3. עבור NotifyIcon , קבע בחלונית Properties את ה-Text של הפקד שהוספת הרגע.
זה יהיה הטקסט שיופיע כאשר העכבר ירחף מעל אייקון החלון הממוזער.

כמו כן, קבע גם מה יהיה האייקון של התוכנית כאשר היא ממוזערת, ע"י לחיצה על Choose Icon.

4. בתוך Properties של החלונית עצמה, בהגדרת ארועים (סימן ברק צהוב) הגדר את אירוע Resize:
private void Form1_Resize(object sender,
EventArgs e)
{
   if (FormWindowState.Minimized == WindowState)
   Hide();
}

5. נוסיף עוד אירוע (Event Handler) עבור NotifyIcon ללחיצה על האייקון בשורת המערכת, כדי לשחזר את החלון לגודלו המקורי:
 private void notifyIcon1_MouseClick(object sender,
 MouseEventArgs e)
{
   Show();
   WindowState = FormWindowState.Normal;
}


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

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

3. נערוך את אפשרויות אלה (שים לב לשנות את שם הפריטים שהוספת בתפריט לשפה האנגלית, במקום העברית ששויכה לכל פריט על פי תוכנו) .
    עבור פעולת 'שחזור' נעתיק את הקוד ששיכנו לאירוע (event) של לחיצה כפולה,
Show();
WindowStatr = FormWindowsState.Normal;

    עבור פעולת סגירה נשתמש באחת הדרכים בהן השתמשנו, למשל:
this.Close();  -  עבור יציאה מטופס ראשי
((Form)this.Parent).Close();  -  user control-עבור יציאה מ

4. לבסוף, קבע בחלונית צד של properties את שיוף contextMenuStrip1 של notifyIcon ,
    אל התפריט שכרגע יצרת.




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



הגדרת registery לריצה של תוכנית עם אתחול המערכת -#C


כתיבה לרגיסטרי מחייבת הוספת הפניה של סביבת NET. לערכים של כתיבה שכזו:
using Microsoft.Win32;
נשים לב לכך שיש להגדיר את שם התוכנית.
אפשרי לבחור בשם הקובץ שמורץ:
string appName = Environment.GetCommandLineArgs()[0];
appName appName .Substring(appName .LastIndexOf("\\") + 1);

או בשם שבחרנו:
string appName = "Snir.App";
ניצור מפתח כתיבה לרגיסטרי:
RegistryKey runAPP_onStartup = Registry.LocalMachine.OpenSubKey ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);

באתחול התוכנה' נוכל לבצע בדיקה האם היא כבר רשומה לעליה באתחול המחשב:
if (runAPP_onStartup.GetValue(appName) == null)
{
     // The value doesn't exist, the application is not set to run at startup
     chkRun.Checked = false;
}
else
{
     // The value exists, the application is set to run at startup
     chkRun.Checked = true;
}
 ובשביל לבצע שינוי בערכים אלו של הרגיסטרי, נשתמש בפקודות הבאות.
נניח שנעשה שימוש בכפתור לשם עדכון הערכים:
 private void btnOk_Click(object sender, EventArgs e)
{
    if (chkRun.Checked)
    {
        // Add the value in the registry so that the application runs at startup
        runAPP_onStartup .SetValue(appName, Application.ExecutablePath.ToString());
    }
    else
    {
        // Remove the value from the registry so that the application doesn't start
        runAPP_onStartup .DeleteValue(appName, false);
    }
}

את הדברים האלו למדתי מהאתר של geekPedia
תוכלו להוריד את תוכנית הדוגמא מכאן


הגדרת אירוע יציאה מטופס


ידוע לכולנו שפקודת היציאה מתוכנית היא ()this.Close .
אך אולי רצינו דווקא שהתוכנית לא תצא מיד, אלא תציג הודעה למשתמש לפני כן?

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

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


לאירוע שיצרנו נשייך קוד:

private void MyBr_FormClosing(object sender,
                                                              FormClosingEventArgs e)
{
    if (MessageBox.Show("האם אתם בטוחים שברצונכם לצאת") ==
                                                                            DialogResult.No)
    {
        e.Cancel = true;
    }
}


לעיתים נרצה לבצע יציאה מטופס משני או מתוך user control:
((Form)this.Parent).Close();