This article is currently in the process of being translated into Hebrew (~97% done).
משאבים - Resources
WPF מציגה תפיסה שימושית מאוד: היכולת לאחסן נתונים כמשאבים, מקומית לפקד, לחלון או גלובלית לכל היישום. "נתונים" יכולים להיות מה שאתה רוצה, החל במידע וכלה בהיררכית פקדי WPF. זה מאפשר לך לכתוב נתונים במקום אחד ולעשות בהם שימוש חוזר בכל מקום שתרצה.
דבר זה משמש בעיקר עבור סגנונות (Styles) ותבניות (Templates), שנתאר בהרחבה בהמשך המדריך. אבל כדי להדגים תפיסה זאת בפרק זה, נשתמש בה עבור דברים אחרים. נתחיל עם דוגמה פשוטה:
<Window x:Class="WpfTutorialSamples.WPF_Application.ResourceSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ResourceSample" Height="150" Width="350">
<Window.Resources>
<sys:String x:Key="strHelloWorld">Hello, world!</sys:String>
</Window.Resources>
<StackPanel Margin="10">
<TextBlock Text="{StaticResource strHelloWorld}" FontSize="56" />
<TextBlock>Just another "<TextBlock Text="{StaticResource strHelloWorld}" />" example, but with resources!</TextBlock>
</StackPanel>
</Window>
משאבים ניתנים כמפתחות על ידי התכונה x:Key, אשר מאפשרת להפנות לחלקים אחרים של היישום ע"י המפתח, בשילוב עם StaticResource. בדוגמה זו איחסנו מחרוזת פשוטה, והשתמשנו בה בשני פקדי TextBlock שונים.
StaticResource מול DynamicResource
בדוגמה זו, השתמשנו ב-StaticResource כדי להפנות למשאבים. אולם, קיימת חלופה, הנקראת DynamicResource.
ההבדל העיקרי הוא שמשאבים סטטיים נלקחים רק פעם אחת - כשקובץ ה-XAML נטען. אם המשאב ישתנה מאוחר יותר, השינוי לא ישתקף במקום שהשתמשנו ב-StaticResource.
לעומת זאת, DynamicResource נלקח כשהמשאב נצרך, ולקח מחדש כל פעם שהוא משתנה. חשוב על זה כקשירה (binding) לערך סטטי לעומת קשירה לפונקציה שמבוצעת שוב כל פעם שהערך משתנה - זה לא עובד בדיוק כך, אבל זה מספיק כדי לתת לך תמונה טובה יותר של הרעיון. משאבים דינמיים מאפשרים לך להשתמש במשאבים שאינם ידועים בזמן עיצוב (Design time), לדוגמה אם אתה מוסיף אותם מהקוד מאחור כאשר היישום מתחיל לפעול.
סוגי משאבים נוספים
שיתוף מחרוזת הינו פשוט, אבל אתה יכול לעשות הרבה יותר. בדוגמה הבאה, נאחסן מערך של מחרוזות לצד מברשת gradient שתשמש כצבע רקע. זה ייתן לך תמונה טובה על כמה אתה יכול לעשות עם משאבים:
<Window x:Class="WpfTutorialSamples.WPF_Application.ExtendedResourceSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ExtendedResourceSample" Height="160" Width="300"
Background="{DynamicResource WindowBackgroundBrush}">
<Window.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
<x:Array x:Key="ComboBoxItems" Type="sys:String">
<sys:String>Item #1</sys:String>
<sys:String>Item #2</sys:String>
<sys:String>Item #3</sys:String>
</x:Array>
<LinearGradientBrush x:Key="WindowBackgroundBrush">
<GradientStop Offset="0" Color="Silver"/>
<GradientStop Offset="1" Color="Gray"/>
</LinearGradientBrush>
</Window.Resources>
<StackPanel Margin="10">
<Label Content="{StaticResource ComboBoxTitle}" />
<ComboBox ItemsSource="{StaticResource ComboBoxItems}" />
</StackPanel>
</Window>
בפעם זו, הוספנו כמה משאבים נוספים, כל שהחלון שלנו מכיל מחרוזת פשוטה, מערך של מחרוזות ו-LinearGradientBrush. במחרוזת משתמשת התווית, המערך של המחרוזות נצרך לפקד ComboBox ומברשת ה-gradient שימושית לחלון עצמו. כמו שאתה יכול לראות, הרבה דברים יכולים להיות מאוחסנים כמשאבים.
משאבים מקומיים ומשאבים ברמת היישום
עד עכשיו אחסנו משאבים ברמת החלון, אליהם אפשר לגשת מכל מקום בחלון.
אם אתה צריך משאבים לפקד ספציפי, אתה יכול לאחסן אותם כמקומיים לאותו פקד, במקום כמקומיים לחלון. זה עובד בדיוק אותו דבר, ההבדל היחיד הוא שכעת המשאב נגיש רק בתוך תחום ההכרה (scope) של הפקד:
<StackPanel Margin="10">
<StackPanel.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</StackPanel.Resources>
<Label Content="{StaticResource ComboBoxTitle}" />
</StackPanel>
במקרה זה, הוספנו משאב ל-StackPanel והשתמשנו בו בפקד בן, ה-Label. פקדים אחרים בתוך ה-StackPanel יכולים גם להשתמש בו, כמו שילדים שלהם יוכלו לגשת אל המשאב. אך לפקדים מחוץ ל-StackPanel זה לא תהיה גישה למשאב.
אם אתה צריך גישה למשאב מחלונות שונים, זה אפשרי. הקובץ App.xaml יכול להכיל משאבים כמו כל חלון או פקד WPF אחר, וכאשר אתה מאחסן בו משאבים, הם יהיו נגישים מכל מקום ביישום. זה עובד בדיוק באותה הדרך כמו משאבים מקומיים לחלון:
<Application x:Class="WpfTutorialSamples.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="WPF application/ExtendedResourceSample.xaml">
<Application.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</Application.Resources>
</Application>
שימוש בהם זהה - WPF תצעד אוטומטית למעלה ולמעלה בטווח ההכרה, מהפקד המקומי עד ל-App.xaml, על מנת למצוא את המשאב המבוקש:
<Label Content="{StaticResource ComboBoxTitle}" />
משאבים מהקוד-מאחור (code-behind)
בינתיים, ניגשנו למשאבים ישירות מה-XAML. אולם, ניתן לגשת למשאבים מהקוד, דבר שימושי במספר מקרים. בדוגמה הקודמת, ראינו שאנחנו יכולים לאחסן משאבים במקומות שונים, לכן בדוגמה זו, ניגש לשלושה משאבים שונים מהקוד-מאחור, כל אחד מאוחסן במקום אחר:
App.xaml:
<Application x:Class="WpfTutorialSamples.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="WPF application/ResourcesFromCodeBehindSample.xaml">
<Application.Resources>
<sys:String x:Key="strApp">Hello, Application world!</sys:String>
</Application.Resources>
</Application>
Window:
<Window x:Class="WpfTutorialSamples.WPF_Application.ResourcesFromCodeBehindSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ResourcesFromCodeBehindSample" Height="175" Width="250">
<Window.Resources>
<sys:String x:Key="strWindow">Hello, Window world!</sys:String>
</Window.Resources>
<DockPanel Margin="10" Name="pnlMain">
<DockPanel.Resources>
<sys:String x:Key="strPanel">Hello, Panel world!</sys:String>
</DockPanel.Resources>
<WrapPanel DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10">
<Button Name="btnClickMe" Click="btnClickMe_Click">Click me!</Button>
</WrapPanel>
<ListBox Name="lbResult" />
</DockPanel>
</Window>
הקוד מאחור:
using System;
using System.Windows;
namespace WpfTutorialSamples.WPF_Application
{
public partial class ResourcesFromCodeBehindSample : Window
{
public ResourcesFromCodeBehindSample()
{
InitializeComponent();
}
private void btnClickMe_Click(object sender, RoutedEventArgs e)
{
lbResult.Items.Add(pnlMain.FindResource("strPanel").ToString());
lbResult.Items.Add(this.FindResource("strWindow").ToString());
lbResult.Items.Add(Application.Current.FindResource("strApp").ToString());
}
}
}
כמו שאתה יכול לראות, איחסנו שלושה הודעות "Hello, world!" שונות: אחת בקובץ App.xaml, אחת בתוך החלון ואחת מקומית לפאנל הראשי. ממשק המשתמש מכיל לחצן ו-LitsBox.
בקוד מאחור, טיפלנו בלחיצה על הלחצן בהוספה של כל אחת מהמחרוזות ל-ListBox. השתמשנו בשיטה ()FindResource, שמחזירה את המשאב כאובייקט (אם נמצא), וכך הכנסנו כל מחרוזת ע"י השיטה ()ToString שלה.
שים לב שהשתמשנו בשיטה ()FindResource על אובייקטים שונים - בתחילה על הפאנל, בהמשך על החלון ולבסוף על האובייקט Application. זו הדרך לקבל משאב שאנחנו מכירים, אך כפי שצויין, אם המשאב לא נמצא, החיפוש יעלה במעלה ההיררכיה, ולכן עקרונית, יכלנו להשתמש בשיטה ()FindResource על הפאנל בשלושת המקרים, היות שהיא הייתה ממשיכה למעלה לחלון ומאוחר יותר לאובייקט Application, אם המשאב עדיין לא נמצא.
ההפך לא נכון - החיפוש לא יורד בהיררכיה אלא עולה, לכן אינך יכול לחפש משאב ברמת היישום אם הוא מוגדר מקומית לפקד או לחלון.