מדריך Windows Phone

מדריך Windows Phone – שימוש ב Data Binding: שימוש ב INotifyPropertyChange

‏ • Sela

בואו ננסה לחשוב לרגע על הסוגיה הבאה: כיצד מנגנון ה Binding יודע על כל שינוי באובייקט המקור?

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

אז מי מספק את אותם אירועים בכל פעם תכונת מקור משתנה? זה תלוי.

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

אם תכונת המקור היא תכונה רגילה אזי אין דרך למנגנון ה Binding לדעת על השינוי!

דוגמא שלא עובדת

בדוגמא הבאה יש לנו מחלקת נתונים בשם MyData הממומשת באופן הבא:


namespace NotifyTargetBad
{
public class MyData
{
public string User { get; set
; }
public string Password { get; set; }
}
}

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

<phone:PhoneApplicationPage
  x:Class="PhoneDemo.MainPage"
  >="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  >:d="http://schemas.microsoft.com/expression/blend/2008"
  >:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  >:local="clr-namespace:PhoneDemo"
  mc:Ignorable="d"
  d:DesignWidth="480"
  d:DesignHeight="800"
  FontFamily="{StaticResource PhoneFontFamilyNormal}"
  FontSize="{StaticResource PhoneFontSizeNormal}"
  Foreground="{StaticResource PhoneForegroundBrush}"
  Orientation="Portrait"
  SupportedOrientations="Portrait">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition
       Height="Auto" />
      <RowDefinition
       Height="Auto" />
    </Grid.RowDefinitions>
    <StackPanel
     x:Name="stackPanel"
     Orientation="Horizontal"
     Grid.Row="0">
      <TextBlock
       Text="User: " />
      <TextBox
       Text="{Binding Path=User}" />
      <TextBlock
       Text="Password: " />
      <TextBox
       Text="{Binding Path=Password}" />
    </StackPanel>
    <Button
     Grid.Row="1"
     Content="ChangeData"
     Click="Button_Click" />
  </Grid
>
</
phone:PhoneApplicationPage
>

מדריך Windows Phone – שימוש ב Data Binding: שימוש ב INotifyPropertyChange

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

using Microsoft.Phone.Controls;

namespace
PhoneDemo
{
 
public partial class MainPage : PhoneApplicationPage
  {
   
private MyData
_myData;

   
// Constructor
    public
MainPage()
    {
      InitializeComponent();
      _myData =
new MyData
()
      {
        User =
"Arik", Password = "123456"
 
      };
      stackPanel.DataContext = _myData;
    }

   
private void Button_Click(object sender, System.Windows.RoutedEventArgs
e)
    {
      _myData.Password =
"555555";
    }
  }
}

הרצת הדוגמא הזו תראה לכם שלחיצה על הכפתור לא משנה שום דבר בפקדי ה UI למרות שהנתון של הסיסמא השתנה. הסיבה היא שהתכונות שיצרנו באובייקט MyData הן תכונות פשוטות (לא Dependency Properties) ולכן מנגנון ה Binding אינו יכול לדעת על השינוי!

דוגמא עובדת באמצעות שימוש ב INotifyPropertyChanged

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

להלן הקוד שמראה את הדרך הנכונה לכתוב את המחלקה MyData:

using System.ComponentModel;

namespace
NotifyTargetUsingInterface
{
public class MyData : INotifyPropertyChanged
{
private string
_user;
public string
User
{
get
{
return
_user;
}
set
{
_user =
value
;
if (PropertyChanged != null
)
{
PropertyChanged(
this, new PropertyChangedEventArgs("User"
));
}
}
}

private string
_password;
public string
Password
{
get
{
return
_password;
}
set
{
_password =
value
;
if (PropertyChanged != null
)
{
PropertyChanged(
this, new PropertyChangedEventArgs("Password"
));
}
}
}

public event PropertyChangedEventHandler PropertyChanged;
}
}

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

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

מדריך Windows Phone – שימוש ב Data Binding: שימוש ב INotifyPropertyChange

דוגמא עובדת באמצעות שימוש ב Dependency Properties

כמובן שיכולנו לעשות גם שימוש בתכונות מסוג Dependency Properties ואז ליהנות מדיווח אוטומטי על שינויים בתכונות. לשם כך יש לשנות את המחלקה MyData כך שתירש מ DependencyObject ותהיה בעלת שתי תכונות מתאימות מסוג Dependency Property:

using System.Windows;

namespace
PhoneDemo
{
 
public class MyData : DependencyObject
  {
   
public string
User
    {
     
get { return (string
)GetValue(UserProperty); }
     
set { SetValue(UserProperty, value
); }
    }

   
public static readonly DependencyProperty
UserProperty =
     
DependencyProperty.Register("User"
,
     
typeof(string), typeof(MyData), new PropertyMetadata(""
));

   
public string
Password
    {
     
get { return (string
)GetValue(PasswordProperty); }
     
set { SetValue(PasswordProperty, value
); }
    }

   
public static readonly DependencyProperty
PasswordProperty =
     
DependencyProperty.Register("Password"
,
     
typeof(string), typeof(MyData), new PropertyMetadata(""));
  }
}

אמנם בשיטה זו מקבלים בחינם את הדיווח על שינויים (ועוד תכונות טובות של Dependency Properties) אבל אנו משלמים על כך שאנו חייבים לרשת מאובייקט DependencyObject ישירות או בעקיפין. ישנם הרבה מקרים שבהם זה בלתי אפשרי (בגלל שניתן לרשת רק ממחלקה אחת) ואז הפתרון הוא להשתמש ב INotifyPropertyChanged.

תגיות: , , , ,

arikp

אריק פוזננסקי הוא יועץ בכיר ומרצה בסלע. הוא השלים שני תארי B.Sc. במתמטיקה ומדעי המחשב בהצטיינות יתרה בטכניון. לאריק ידע נרחב בטכנולוגיות מיקרוסופט, כולל .NET עם C#, WPF, Silverlight, WinForms, Interop, COM/ATL, C++ Win32 ו reverse engineering.

תגובות בפייסבוק