en-UShe-IL
You are here:   Blog
Register   |  Login

Blog Archive:

Maximize
* Can be used in order to search for older blogs Entries

Search in blogs


Blog Categories:

Maximize
* Can be used in order to search for blogs Entries by Categories

Blog Tags:

Maximize
* Can be used in order to search for blogs by keywords

TNWikiSummit


Awared MVP 


 


Microsoft® Community Contributor 


Microsoft® Community Contributor


Read first, before you you use the blog

Maximize
אוק5

Written by: ronen ariely
05/10/2013 11:25 RssIcon

הקדמה

הרעיון של שימוש בתבניות של פרויקטים מסוגים המוגדרים מראש בתוכנת ה visual studio, מתבסס על כך שתבניות אלו מוגדרות בתוכנה. כל פרויקט שאנחנו רוצים אפשר לפתח מאפס בלי להיעזר בתבנית מוכנה, זו רק דרך לזרז עניינים. לפעמים שוכחים זאת ו/או לא מודעים לקוד שמיוצר עבורנו בתוכנת ה VS מאחורי הקלעים, בזמן יצירת פרויקט חדש. במאמר קצר זה נדון בפיתוח בטכנולוגיית winform, וניגע מעט ב"מאחורי הקלעים" של הפיתוח.

ביצירת פרויקט חדש מסוג WinForm, תוכנת ה Visual Studio בונה לנו פרויקט התחלתי הכולל טופס ראשון. הטופס משמש כטופס ראשי של האפליקציה. בזמן הרצת התוכנית משמש טופס זה חלון העבודה של המשתמש או הממשק הגרפי הראשוני של המשתמש (GUI). הטופס נוצר במתודה Main על ידי הפעלת המתודה Run, אשר אחראית על התחלת האפליקציה.

הבעיה / המטרה

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

הגיע הזמן לעבודה

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

שלב 1: יצירה והבנה בסיסית של מאחורי הקלעים של פרוייקט חדש

* פתח את תוכנת ה Visual Studio

* בחר ביצירת פרויקט חדש מסוג Windows Forms Application

* תן לפרויקט את השם WinformAppWhioutMainform

Open New Application

* לחץ OK והמתן ליצירת ופתיחת הפרויקט לעבודה

New Application First Look

בצד ימין בתמונה מעל אפשר לראות את חלון ה solusion Expolorer. חלון זה מציג את רשימת הקבצים והתקיות של הפרויקט שלנו. ניתן לראות שהפרויקט שלנו כולל כבר בהתחלה מספר תקיות וקבצים. למעשה הפרויקט מוכן להרצה וכאמור מתבסס על תבנית מוכנה של פרויקט. ברשימת הקבצים נדון כאן ב 2 קבצים מרכזיים: Form1.cs וכן Program.cs

באמצע התמונה אפשר לראות טופס בשם עם הכותרת Form1. תוכנת ה VS כוללת ממשק GUI להצגת הטפסים. הטופס שאנחנו רואים כאן הוא למעשה ההצגה הגרפית של המחלקה Form1 המוגדרת בקובץ Form1.cs.

* פתיחת קבצי טפסים בהצגה גרפית

סגור את הקובץ במרכז המסך. לחץ על שם הקובץ Form1.cs בכפתור השמאלי פעמיים על מנת לפתוח את הקובץ שוב. כפי שניתן לראות הקובץ נפתח בברירת המחדל במצב ההצגה הגרפית של הטופס. זו היא ברירת המחדל של ה VS להצגה של קבצי טפסים. באופן דומה בפיתוח אתרי אינטרנט אם נפתח קובץ מסו VIEW כלשהו כמו קוד HTML אבל ברירת המחדל של ה VS תהיה לתת לנו הצגה גרפית של הקובץ על מנת שנוכל להתחיל לעבוד בשיטת רירת אלמנטים אל ומהמסך. 

* פתח את הקובץ Form1.cs בהצגת קוד.

על מנת לפתוח את הטופס בהצגה הטקטואלית שלו על מנת לצפות בקוד ניתן ללחוץ על f7 או ללחוץ בכל נקודה בטופס במצב ההצגה הגרפית שלו ולבחור View Code. כמו כן אפשר לפתוח את הקובץ ישירות בהצגה טקסטואלית על ידי בחירה שלו מהרשימה בצד ימין בעזרת הכפתור הימני של העכבר ובחירה ב View Code. שמו לב שמדובר בסך הכל במחלקה בשם Form1 שנגזרת מהמחלקה Form. המחלקה כוללת בנאי שמפיל מתודה בשם InitializeComponent. ניתן לקרוא יותר על המתודה בקישור הבא.

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

* פתח את הקובץ Program.cs על ידי לחיצה כפולה על הקובץ מהרשימה

בניגוד לקובץ הקודם, תוכנת ה VS מזהה שקובץ זה אינו קובץ VIEW (לא נועד לתצוגה... אני נעזר במושגים בארכיטקטורת MVC או MVVC תוך כדי ההדרכה... אבל ההרחבה על נושא זה חורגת ממסגרת מאמר זה). לכן הקובץ נפתח בברירת המחדל בהצגה הטקסטואלית שלו כשאר הקוד מוכן לעריכה.

ניתן לראות שהקובץ מכיל מחלקה בשם Program. ברירת המחדל של סביבת ההרצה שלנו (Dot.Net Framework) היא לקבוע את המחלקה Program כנקודת הפתיחה של האפליקציה. אפשר לשנות את ההגדרה הזו במאפייני הפרויקט ולקבוע כל מחלקה אחרת שרוצים. מכיוון שכרגע אין לנו סיבה לשנות שם זה, אז אנחנו נשאר עם ברירת המחדל והמוסכמות.

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

///
/// The main entry point for the application.
///

מה קורה כאשר האפליקציה מתחילה לרוץ?

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

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

האפליקציה מתחילה לרוץ ומפעילה את המתודה Application.Run. מתודה זו מחברת את האפליקציה שלנו (implements) ל Win32 Message Loop. פעולה זו הכרחית על מנת לקבל הודעות ממערכת ההפעלה. מערכת ההפעלה מעבירה הודעות (Win32 Messages) על אירועים כגון שמוש בעכבר או במקלדת או כל ממשק אחר של המשתמש.

אל מתודה זו העברנו כפרמטר טופס חדש שנוצר על ידי יצירת INSTANCE של המחלקה Form1 תוך שימוש בפקודה ()new Form1. המתודה Application.Run פותחת את הטופס שהועבר אליה כפרמטר ומחברת אותו אל נקודת ה Message Loop. זהו הטופס הראשי של האפליקציה שלנו. סגירת טופס זה על ידי המשתמש או על ידי פקודת קוד, תגרור יציאה וסגירת האפלקיציה. זו הבעיה מולה אנחנו עומדים!

שלב 2: הוספת טופס חדש לאפליקציה

צור טופס חדש בשם Form2.

שלב 3: הוספת כפתור בטופס הראשי לפתיחת הטופס השני

* הוסף לטופס הראשי כפתור חדש.

* לחץ פעמיים על הכפתור על מנת להוסיף קוד לאירוע הלחיצה.

* המתודה button1_Click הכנס את הקוד הבא:

Form2 MyForm2 = new Form2();
MyForm2.Show();

* הרץ את התוכנית ובדוק שאכן לחיצה על טופס פותחת את הטופס החדש.

* נסה להוסיף את שורת הקוד הבאה למתודה button1_Click לשם סגירת הטופס הראשי שלנו בנוסף לפתיחת הטופס השני:

this.Close();

* הרץ שוב את האפליקציה ובדוק מה קורה בלחיצה על הכפתור.

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

שלב 4: [פתרון 1] הוספת כפתור בטופס השני על מנת להסתיר א הטופס הראשון

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

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

this.Hide();

* הרץ את התוכנית שוב ובדוק אם פתרון זה מתאים לך.

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

על מנת לאפשר למשתמש לסור את האפליקציה בצורה נוחה עלינו לשנות מעט את התוכנית שלנו. פתרון אחד יכול להיות למשל להוסיף לטופס השני כפתור שיבצע פעולה הפוכה. ז"א הכפתור יפתח את הטופס הראשי חזרה ויסתיר את הטופס השני הפעם. נוסיף כפתור לטופס השני ושוב נלחץ על הכפתור עפמיים על מנת ליצור את האירוע של הלחיצה על הכפתור וניגש למתודה המתאימה (קובץ Form2.cs). הוסף את הקוד הבא במתודה button1_Click של המחלקה Form2:

FormCollection MyFormsList = Application.OpenForms;
foreach (Form F in MyFormsList) {
    if (F.Name == @"Form1") F.Show();
}
this.Close();

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

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

יש חשיבות גדולה מאוד בתכנון האפליקציה ובהבנה של מה המשמעות של טופס פתוח, מה המשמעות של טופס מוסתר, ומה המשמעות של סגירת טופס וכו'. אני לא אוכל להרחיב בנקודה זו במסגרת המאמר הנוכחי. אם ננסה לסכם במשפט אחד נוכל להגיד: ההסתרה היא רק עניין ויזואלי והטופס ממשיך לרוץ ברקע, בעוד סגירה של טופס מבצע 2 פעולות עקריות (א) שליחת הודעת windows messages לסגור את Win32 windows, (ב) אם הפעלנו את החלון על ידי הרצת המתודה Show ולא על ידי הרצת המתודה ShowDialog אז יבוצע גם פעולת Dispose.

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

Application.Exit();

.

שלב 5: [פתרון 2] פתיחת האפליקציה ללא טופס ראשי

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

נחזור לקוד של המתודה Main במחלקה Program (המתודה שמהווה כאמור נקודת כניסה של האפליקציה). נוריד את כל הקוד הקיים שם ובמקומו נכניס רק את ההפעלה של המתודה Application.Run, ללא פרמטרים (ללא שם של טופס). באופן זה האפליקציה רצה ללא טופס ראשי.

המתודה Main מקבלת אם כן את הצורה הבאה:

///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
    //Application.EnableVisualStyles();
    //Application.SetCompatibleTextRenderingDefault(false);
    //Application.Run(new Form1());
 
    Form1 MyForm1 = new Form1();
    MyForm1.Show();
    Application.Run();
}

את פתיחת הטופס הראשון נבצע לבד על ידי פתיחת מופע של המחלקה והרצת המתודה Show.


עתה נחזור לקוד של אירוע הלחיצה על הכפתור שנמצא בטופס הראשי שלנו (קובץ Form1.cs) ונוכל לערוך את המתודה button1_Click של הטופס הראשון כך שהטופס הראשון יסגר.

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

Executing WinForm Application Without forms

אני מקווה שהמאמר היה מועיל :-)
יש עוד הרבה מה להוסיף, ואני מתכנן להוסיף בימים הקרובים עוד כמה מילים כאן, אם כי הבסיס מספיק כבר כדי להתחיל לרוץ קדימה תוך הבנה של הנושא.

קריאה מהנה