This article is currently in the process of being translated into Persian (~99% done).
Resources
WPF مفهوم بسیار مفیدی را ارایه میدهد: توانایی ذخیره سازی دادهها به عنوان یک منبع، که به صورت محلی، برای یک کنترل یا کل پنجره و یا در سطح عمومی، برای کل برنامه قابل استفاده است. دادهها اغلب اوقات میتوانند آنچه میخواهید باشند، از اطلاعات واقعی گرفته تا سلسله مراتب کنترلهای WPF. این قابلیت به شما امکان میدهد دادهها را در یک مکان قرار داده و در چند مکان دیگر استفاده کنید، که بسیار مفید است.
این مفهوم برای سبکها و قالبهای زیادی استفاده میشود، که در ادامه این آموزش مطرح خواهیم کرد، اما همانطور که در این فصل نشان داده خواهد شد، میتوانید برای بسیاری از موارد دیگر نیز استفاده کنید. اجازه دهید آن را با یک مثال ساده شرح دهم:
<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 یک بار زمانی که مورد نیاز است خوانده میشود، سپس هنگام اعمال تغییر در منبع، دوباره خوانده میشود. تصور کنید چیزی دارید که به یک مقدار ثابت متصل است و در مقابل چیزی که به یک تابع متصل بوده و هر تغییری را مانیتور کرده، برای شما ارسال میکند - موضوع چگونگی عملکرد نیست هدف این ست که به شما ایده بهتری دهد که از کدام یک استفاده کنید. منابع دینامیکی همچنین به شما امکان میدهند از منابعی استفاده کنید که در زمان طراحی وجود ندارد. مشروط بر این که هنگام راه اندازی برنامه، آنها را از طریق code-behind اضافه کنید.
انواع بیشتر منابع
اشتراک یک رشته سادهی متن کار آسانی بود، اما شما میتوانید کارهای دیگری نیز انجام دهید. در نمونه بعدی، مجموعه کاملی از رشتهها را به همراه یک برس شیب دار (gradient brush) برای پس زمینه استفاده میکنم. این فقط ایده خوبی در مورد اینکه چه کارهایی میتوانید با منابع انجام دهید را ارایه میدهد:
<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>
این بار، ما چندین منبع را اضافه کردهایم، به گونه ای که Window ما اکنون شامل یک رشته ساده، آرایه ای از رشتهها و یک LinearGradientBrush است. رشته برای برچسب، آرایه رشتهها به عنوان آیتمهایی برای ComboBox و از برس شیب دار خطی (LinearGradientBrush) برای رنگ پس زمینه پنجره استفاده میشود. بنابراین، همانطور که مشاهده میکنید، هر چیزی میتواند به عنوان یک منبع ذخیره شود.
منابع گستردهی محلی و کاربردی
تا کنون، ما منابع را در سطح پنجره ذخیره کردهایم، به این معنی که میتوان در همه جای پنجره به آنها دسترسی داشت.
اگر فقط برای یک کنترل خاص به یک منبع مشخص نیاز دارید، میتوانید به جای اضافه کردن منبع به پنجره، آن را به کنترل مورد نظر اضافه کرده، منبع را محلیتر کنید. این نیز به همان روش کار میکند، تنها با این تفاوت اکنون فقط میتوانید از داخل محدوده آن کنترلی که در آن قرارش دادهاید، به منبع دسترسی داشته باشید:
<StackPanel Margin="10">
<StackPanel.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</StackPanel.Resources>
<Label Content="{StaticResource ComboBoxTitle}" />
</StackPanel>
در این مورد، ما منبع را به StackPanel اضافه میکنیم و سپس در کنترل درون آن، یعنی Label استفاده میکنیم. سایر کنترلهای داخل StackPanel نیز میتوانند از آن استفاده کنند، درست مانند کنترلهای StackPanel، کنترلهای درونی هر کنترل هم میتوانند به آن دسترسی داشته باشند. کنترلهای خارج از این StackPanel خاص به این منبع دسترسی ندارند.
در صورت نیاز، دسترسی به یک منبع از چندین پنجره نیز امکان پذیر است. فایل App.xaml میتواند مانند هر پنجره و هر نوع کنترل WPF دارای منابعی باشد. هنگامی که منابع را در 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/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 دسترسی پیدا کردیم. در عین حال، شما میتوانید از طریق code-behind نیز به منابع خود دسترسی داشته باشید، که این در چندین وضعیت میتواند مفید باشد. در مثال قبلی، ما دیدیم که چگونه میشود منابع را در چندین مکان مختلف ذخیره کرد، بنابراین در این مثال، ما به سه منبع مختلف در code-behind دسترسی خواهیم داشت که هر یک در حوزهای متفاوت ذخیره شدهاند:
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>
Code-behind:
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());
}
}
}
بنابراین، همانطور که مشاهده میکنید، ما سه نوع مختلف پیام 'سلام، دنیا!' را ذخیره کردیم: یکی در App.xaml، دیگری داخل پنجره و یکی دیگر به صورت محلی در پانل اصلی. رابط کاربری شامل یک دکمه و یک لیست است.
در code-behind، ما از رویداد کلیک دکمه استفاده میکنیم تا هر یک از رشتههای متن را همانطور که در تصویر مشاهده میکنید، به ListBox اضافه کنیم. متد ()FindResource که منبع را به عنوان یک شی باز میگرداند (در صورت یافتن) فراخوانی کرده، سپس با استفاده از متد ()ToString خروجی آن را به رشتهای که میشناسیم تبدیل میکنیم.
توجه کنید که چگونه از متد ()FindResource در حوزههای مختلف استفاده میکنیم - ابتدا در پانل، سپس در پنجره و دست آخر در برنامه جاری. منطقی است در جایی که میشناسیم، به دنبال منابعی باشیم که وجود دارند، اما همانطور که پیش از این نیز ذکر شد، اگر منبعی یافت نشود، جستجو به صورت سلسله مراتبی پیشرفت میکند، بنابراین در اصل، میتوانستیم در هر سه مورد، از متد ()FindResource در پانل استفاده کنیم. در صورت عدم یافتن، جستجو تا پنجره و بعد تا سطح برنامه ادامه پیدا میکرد.
این مورد به صورت برعکس صادق نیست - جستجو، رو به پایین درخت حرکت نمیکند، یعنی اگر منبعی در محلی برای کنترل یا پنجره تعریف شده باشد، نمیتوانید در جستجو در سطح برنامه آن را بیابید.