מדריך WPF

מדריך WPF – שימוש ב Resources

‏ • Sela

לפני שנתחיל נעיר כי ה Resources שאנו מדברים עליהם בפרק זה אינם קשורים בכלל ל Project Resources שאנו מכירים מעידן הקודם לWPF. השימוש בProject Resources מיועד בעיקר להוספת תמונות וטבלת מחרוזות (strings) שניתן לבצע להם לוקליזציה (תרגום לשפה מקומית). ה Resources שאנחנו נדבר עליהם בפרק זה הם תכונה ייחודית לטכנולוגיות מבוססות XAML.

 

מה זה Resources?

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

נראה דוגמא שבה נרצה להשתמש ב Resources. בקוד הבא אנו מגדירים טופס לקבלת נתוני User ו Password של משתמש, וקבענו רקע צבעוני במיוחד לשני ה TextBox בתוכנית. השימוש במחלקה LinearGradientBrush ליצירת רקע מתחלף ילמד בהמשך המדריך. כרגע מה שחשוב לשים לב הוא שאותה הגדרה מורכבת של האוביקט LinearGradientBrush מופיעה פעמיים בקוד הXAML:

<Window x:Class="ResourcesDemo.MainWindow"
       >="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       >:x="http://schemas.microsoft.com/winfx/2006/xaml"
       Title="MainWindow"
       Height="350"
       Width="525">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="User:"
              Grid.Row="0"
              Grid.Column="0" />
    <TextBox Grid.Row="0"
            Grid.Column="1">
      <TextBox.Background>
        <LinearGradientBrush>
          <GradientStop Color="Blue"
                       Offset="0" />
          <GradientStop Color="Yellow"
                       Offset="0.5" />
          <GradientStop Color="Red"
                       Offset="1" />
        </LinearGradientBrush>
      </TextBox.Background>
    </TextBox>
    <TextBlock Text="Password:"
              Grid.Row="1"
              Grid.Column="0" />
    <TextBox Grid.Row="1"
            Grid.Column="1">
      <TextBox.Background>
        <LinearGradientBrush>
          <GradientStop Color="Blue"
                       Offset="0" />
          <GradientStop Color="Yellow"
                       Offset="0.5" />
          <GradientStop Color="Red"
                       Offset="1" />
        </LinearGradientBrush>
      </TextBox.Background>
    </TextBox>
  </Grid
>
</
Window
>

 

מדריך WPF – שימוש ב Resources

 

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

כיצד נגדיר Resource?

כמעט לכל פקד בWPF יש Property בשם Resources. הטיפוס של תכונה זו הוא Dictionary. כלומר ניתן להכניס לתכונה זו זוגות של מפתח (Key) וערך (Value).

כדי להוסיף אובייקט לResources של פקד נשתמש בקוד הבא:

  <Window.Resources>
    <LinearGradientBrush x:Key="myLinearGradientBrush">
      <GradientStop Color="Blue"
                   Offset="0" />
      <GradientStop Color="Yellow"
                   Offset="0.5" />
      <GradientStop Color="Red"
                   Offset="1" />
    </LinearGradientBrush>
  </Window.Resources
>

 

הוספנו את האובייקט LinearGradientBrush שלנו תחת תכונת Resources של החלון ובנוסף הוספנו לו תגית x:Key כדי לתת לו מפתח שיזהה אותו. יותר מאוחר אנחנו נשתמש במפתח הזה כדי לפנות לאובייקט.

נעיר כי ניתן לשים אובייקטים בתכונת Resources של כל פקד שיש לו את התכונה הזו, אבל לרוב אנו נשים את האובייקטים במקום מרכזי כמו באובייקט החלון הראשי (Window) או באובייקט האפליקציה (App).

 

כיצד נשתמש ב Resource שהגדרנו?

ישנם שני דרכים להשתמש בResource שהגדרנו:

  1. ע"י שימוש ב StaticResource – דרך זו יותר יעילה מבחינת ביצועים, אך טובה רק כאשר ה Resource שאליו אנו פונים אינו משתנה.
  2. ע"י שימוש ב DynamicResource – בדרך זו ניתן לפנות גם ל Resource שמשתנה, אך נשלם על כך בביצועים.

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

כדי להשתמש בResource שהגדרנו נכתוב את הקוד הבא:

<TextBox Grid.Row="0"
         Grid.Column="1"
         Background="{StaticResource myLinearGradientBrush}"
>

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

להלן הקוד המלא של הדוגמא המתוקנת:

<Window x:Class="ResourcesDemo.MainWindow"
       >="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       >:x="http://schemas.microsoft.com/winfx/2006/xaml"
       Title="MainWindow"
       Height="350"
       Width="525">
  <Window.Resources>
    <LinearGradientBrush x:Key="myLinearGradientBrush">
      <GradientStop Color="Blue"
                   Offset="0" />
      <GradientStop Color="Yellow"
                   Offset="0.5" />
      <GradientStop Color="Red"
                   Offset="1" />
    </LinearGradientBrush>
  </Window.Resources>

  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="User:"
              Grid.Row="0"
              Grid.Column="0" />
    <TextBox Grid.Row="0"
            Grid.Column="1"
            Background="{StaticResource myLinearGradientBrush}">
    </TextBox>
    <TextBlock Text="Password:"
              Grid.Row="1"
              Grid.Column="0" />
    <TextBox Grid.Row="1"
            Grid.Column="1"
            Background="{StaticResource myLinearGradientBrush}">
    </TextBox>
  </Grid
>
</
Window
>

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

כיצד מתבצע תהליך החיפוש?

תהליך החיפוש של StaticResource וגם DynamicResource עובד באופן הבא:

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

ראשית מחפשים את האובייקט המבוקש (בדוגמא LinearGradientBrush ) בתכונה Resources של הפקד הנוכחי (בדוגמא TextBox), אם מצאנו את האוביקט ב Dictionary שלו התהליך מסתיים, אחרת החיפוש עולה רמה אחת בעץ הפקדים (בדוגמא Grid) ושוב מחפש בתכונה Resources, וכך התהליך ממשיך. בדוגמא שלנו, לאחר שהאובייקט לא נמצא ב Grid אנו מחפשים ב Resources של הWindow ושם האובייקט נמצא ומוחזר בהצלחה.

אם האובייקט לא נמצא בשורש העץ שהוא לרוב מטיפוס Window אזי מתבצע חיפוש נוסף בקובץ הXAML של האפליקציה: App.xaml.

 

מתי מתבצע תהליך החיפוש?

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

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

נעיר כי כדי להשתמש ב DynamicResource על התכונה שאנו קובעים להיות  מסוג Dependency Property. מגבלה זו אינה חלה כאשר אנו עובדים עם StaticResource.

תגיות: , , , ,

arikp

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

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