DCSIMG
[Tapuz .Net] WPF vs. Winforms - Justin myJustin = new Justin( Expriences.Current );

[Tapuz .Net] WPF vs. Winforms

שאלה:

אין לי ידע רחב ב C# אך לעומת זאת יש ידע ב MFC ו C++.
אני עומד להתחיל לבנות אפליקציה לחבר בעל חברת השמה.
אני יכול לבנות לו את זה ב MFC או ב WINDOWS FORMS אבל קראתי הרבה על WPF ואני שוקל אם כבר, להתמקצע בזה למטרת בניית אפליקציות מהסוג הנ"ל. אין לי בעיה להעמיק את הידע ב C#(לא מעוניין ב VB).
ראיתי ש WPF מספק UI עשיר מאוד וזה נראה באמת הדור הבא של UI.
מה דעתכם ? האם מישהו כבר בנה אפליקצייה עם הטכנולוגיה הזאת ?
אשמח לשמוע התרשמויות מבעלי נסיון.

 

תשובה:

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

היתרונות הגדולים שלו: (לפי דעתי ובלי סדר ספציפי)

1.  אפליקציות WPF מתאימות את עצמן לרזולוצית המשתמש, ה-DPI של המסך שלו ולשטח המסך הזמין.
שמעצבים תמיד מעצבים ב-1 יחידות WPF סטנדרטיות ולא בפיסקלים או משהו כזה.
ככה שהאפליקציות נראות אותו דבר בלי קשר לרזולוציה והן יודעות לסדר את עצמן מחדש מצויין לפי כמה שטח מסך יש להן.

למשל נביט על יכולות ה-Resizing של WPF.
נגיד וציירנו כפתור שלוקח את כל השטח בתא שגדולה 100 (יחסי) על 100 (יחסי).

image

זה ה-XAML שנקבל:

<Window x:Class="WPFTesting.Window2"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Title="Window2" Height="300" Width="300">

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="100*" />

            <RowDefinition Height="162*" />

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="100*" />

            <ColumnDefinition Width="178*" />

        </Grid.ColumnDefinitions>

        <Button Name="button1">Button</Button>

    </Grid>

</Window>

 

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

image  

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

image

או להקטין את החלון והכפתור עדיין יתפוס גודל יחסי.

image

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

בכלל WPF יודע לתת התייחסות מצויינת לגודל הסופי של אלמנט על המסך דרך פרדיגמת: Measure Twice, Cut Once. למעשה, WPF שואל את האלמנטים שלו "מה הגודל שאתם חושבים שתצטרכו?" ובנפרד מודיע לכל אלמנט "זה הגודל הסופי שקיבלתם לפי מערכת ה-Layout המאוד מתוחכמת שלי".

יש עוד המון אפשרויות Layout כאלו שיודעות להתאים את התוכן לגודל המסך הזמין, הרזולוציה וה-DPI של המסך.

 

2. מודל ה-Content. בכל מקום שהיינו רגילים ב-Winforms שמבקשים מאתנו String לתצוגה, עכשיו מבקשים מאתנו UIElement.
כלומר, אם פעם היה Button.Text ב-Winforms, אז ב-WPF יש לנו Button.Content.
ככה שלמעשה אפשר להכניס מחדש על אלמנט גרפי לתוך אלמנטים גרפיים אחרים. מה שב-Winforms ובטכנולוגיות אחרות מהדור שלו, בלתי אפשרי או דורש המון המון המון עבודה עם ציור ידני.

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

<Window x:Class="WPFTesting.Window2"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Title="Window2" Height="300" Width="300">

    <Grid>

        <Button Name="button1" Margin="35,30,43,40">

            <Button.Content>

                <MediaElement Source="http://silverlight.net/quickstarts/silverlight10/thebutterflyandthebear.wmv" />

            </Button.Content>

        </Button>

    </Grid>

</Window>

שימו לב שיש בתוך Button.Content את אלמנט ה-MediaElement שמצביע לקובץ WMV באינטרנט. (אומנם עדיף במציאות לעבוד רק מול קבצים לוקאליים, אבל זה רק הדגמה ליכולות של WPF).

אם נריץ את האפליקציה נקבל את הכפתור הבא:

image

image

 

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

 

3. ההפרדה בין "עיצוב" ל-"פונקציונליות" הרבה יותר ברורה באמצעות קבצי XAML.
אפשר אפילו באמת להגיע למצב שמעצב עובד על איך מסך נראה ואיפה הוא מציג מידע, שהתוכניתן רק דואג לחשוף למעצב את המידע והאירועים המתאימים.

המעצב שהוא יושב בתוכנה היעודית שלו Expression Blend יכול לקשר כפתור ל-Command שהמתכנת חשף לו מראש ולבצע DataBinding בצורה גרפית על Net Classes.

בואו נציג תרחיש שבו אנחנו בונים מערכת CRUD בסיסית לפרות ובעזרתו נדגים גם את Commands וגם את Databinding ואת ההפרדה המצויינת שהם עוזרים להשיג בין תכנות לבין עיצוב מסך.

נתחיל מדוגמה על Commands.
המתכנת יצור Commands, המעצב ישתמש בהן, והמתכנת בכלל לא יודע מה הולך ב-GUI.

המתכנת בא ויצר פקודה חדשה.

    public partial class Window2 : Window

    {

        public static RoutedCommand InsertNewCowCommand =

            new RoutedCommand("InsertNewCowCommand", typeof(Window2));

 

        public Window2()

        {

            InitializeComponent();

        }

    }

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

    public partial class Window2 : Window

    {

        public static RoutedCommand InsertNewCowCommand =

            new RoutedCommand("InsertNewCowCommand", typeof(Window2));

 

        public Window2()

        {

            InitializeComponent();

 

            CommandManager.RegisterClassCommandBinding(this.GetType(),

                    new CommandBinding(InsertNewCowCommand, InsertNewCowCommandExcuted));

        }

 

        private void InsertNewCowCommandExcuted(object sender, ExecutedRoutedEventArgs e)

        {

            // Insert Cow Into Database, designer doesn't know this

            MessageBox.Show("Cow has been inserted to DB");

        }

    }

הגיע המצב שלנו, פתח את Expression Blend ואפילו צייר כפתור.

image

עכשיו המעצב מפרט איזה פקודה מעלה האירוע ברירת מחדל של הכפתור (שהוא Button.Click).

image

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

image

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

 

 

הטיפול ב-DataBinding עובד בצורה דומה.
המתכנת יבנה את המחלקה שלו - Cow.
המעצב ידאג לחבר את המאפיינים של המחלקה לחלקים שונים בטופס.

המתכנת בא וכתב את המחלקה שלו.

namespace BL

{

    public class Cow

    {

        public Cow()

        {

        }

 

        public Cow(string Name)

        {

            this.Name = Name;

        }

 

        private string _name;

 

        public string Name

        {

            get { return _name; }

            set { _name = value; }

        }

    }

}

סה"כ אין פה הרבה - יש מחלקה עם מאפיין (גם באנגלית: Property) בשם Name שמחזיק את השם של הפרה שלנו ושני קונסטרקטורים.

המעצב יגיע ל-Expression Blend ויבחר שהוא רוצה לעשות DataBinding ל-Cow.Name ישר לתוך TextBox שהוא שם על הטופס.

אז דבר ראשון הוא יוסיף TextBox.

image

ילחץ על הכפתור הלבן הקטן מצד ימין למאפיין Text ויבחר DataBinding.

image

יוסיף Data Source חדש לטופס שמצביע על Cow.

image

 image

ויעשה Drag & Drop ל-Name ישר לתוך ה-TextBox.

image

יבחר שהוא רוצה לבצע DataBinding ל-Text.

image

image

ועכשיו כל מה שנותר זה לדאוג שהתכנת דואג לתת לטופס Data Source כלשהו.

    public partial class Window2 : Window

    {

        public Window2()

        {

              this.DataContext = new Cow("עדנה");

        }

    }

ומולנו נקבל את הטופס הבא:

image

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

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

 

מודל ה-DataBinding ומודל ה-Commands של WPF יוצר הפרדה מוחלטת בשתי הנקודות הכואבות של תכנות חלונאי עד כה ובאמת מאפשר למעצב לעבוד עצמאית מתוכניתן אחרי שהכינו לו את התשתית.

 

4. העובדה שעכשיו יש לנו אפשרות לתכנות "דמוי CSS" זה אחד מהפיצ'רים הבאמת חדשניים של WPF? 
ככה אפשר להכיל סגנון גרפי על כל האפליקציה בשניות.
(כמובן שיש גם אפשרות לירושה בין ה-Styleים אז זה בכלל יוצא מצויין)
מה-Styleים של WPF גם ניתן לקבוע תוכן ברירת מחדל וציור ברירת מחדל לחלוטין.

למשל, ה-Style ה-WPF הבא ידאג לפרמט סוג וגודל פונט לכפתורים כל האפליקציה. (כאשר הוא יושב בקובץ ה-App.xaml של האפליקציה)

        <Style TargetType="{x:Type Button}">

            <Setter Property="FontSize" Value="22"/>

            <Setter Property="FontFamily" Value="Arial"/>

        </Style>

בדוגמה ל-CSS יש לנו שלוש רמות של Style:
1. שמתאים לכל אלמנט גרפי מסוג מסויים ברחבי האפליקציה. זאת הדוגמה שראינו למעלה.
2. שמתאים לכל אלמנט גרפי מסוג מסויים בתוך חלון או Container כלשהו. (ככה אפשר לפרמט רק כפתורים בתוך Panel או Window מסויים)
3. שמתאים רק לאלמנט גרפי ספציפי או שביקש ספציפית שה-Style יפעל עליו.

 

בניגוד ל-CSS קיבלנו גם פיצ'ר מדהים של ירושה בין Styleים. 

למשל בדוגמה הבאה נירש את BaseStyle ונוסיף לו.

        <Style TargetType="{x:Type Button}" x:Key="BaseStyle">

            <Setter Property="FontSize" Value="22"/>

            <Setter Property="FontFamily" Value="Arial"/>

        </Style>

 

        <Style TargetType="{x:Type Button}" BasedOn="{StaticResource BaseStyle}">

            <Setter Property="FontWeight" Value="Bold" />

        </Style>

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

image

 

כמובן שכל העניין הזה עם ה-Styleים הוא אוטומטי 99% דרך Expression Blend ומעצב רק גורר דברים ממקום למקום וקובע מאפיינים כרגיל.

 

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

 

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

למשל, אם הייתי רוצה שב-Button Click הכפתור יעשה סיבוב של 360 מעלות תוך 2 שניות ויזוז ימינה, פשוט אי-אפשר לעשות את זה בצורה הגיונית ב-Winforms. זה ייקח לפחות שבוע עבודה ב-Winforms ויראה נורא.
אבל ב-WPF אנימציות הן פיצ'ר משמעותי ודרך Expression Blend ניתן לצייר אותן.

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

נבחר להוסיף Trigger חדש לטופס שיעבוד על button Click.

image

image

 image

נבחר להוסיף Storyboard חדש באירוע הזה.

image

 

דרך ה-Designer הגרפי גררתי את הכפתור 100 פיסקלים ימינה וסובבתי אותו, והכל בסימן ה-2 שניות באנימציה.

image

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

ואם נריץ את האפליקציה נראה במהלך ה-2 שניות את האנימציה הבאה את האנימציה מאוד חלקה הבאה.

image

image 

image

image

image

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

 

סה"כ אלו היתרונות הגדולים של WPF לפי דעתי.

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

 

קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=112351636

Published Sunday, February 17, 2008 4:11 PM by Justin-Josef Angel [MVP]
תגים:, ,

Comments

No Comments