This article has been localized into Vietnamese by the community.
Resources
WPF giới thiệu một khái niệm rất tiện dụng: Đó là chúng ta có thể lưu trữ dữ liệu như là một tài nguyên, bao gồm lưu trữ cục bộ để điều khiển, sử dụng cho cửa sổ hiện tại, hoặc lưu trữ toàn cục sử dụng cho toàn bộ ứng dụng. Dữ liệu rất đa dạng, có thể từ những thông tin thực thi cho đến thông tin các mục của WPF controls. Điều này cho phép bạn đặt dữ liệu ở một nơi, và có thể sử dụng ở những nơi khác, rất hữu dụng
Khái niệm này được sử dụng trong rất nhiều cho các style và template, chúng ta sẽ thảo luận trong phần sau của tutorial này. Tuy nhiên, trong chapter này, chúng ta cũng sẽ tìm hiểu một cách khái quát nó, và bạn có thể sử dụng cho rất nhiều việc khác nữa. Hãy cùng tôi xem xét một ví dụ đơn giản như sau:
<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>
Những tài nguyên được cấp cho một khoá riêng biệt, có thể dùng như x:Key attribute, cho phép bạn tham chiếu đến các phần khác của ứng dụng bằng cách sử dụng khoá đó, kết hợp với phần mở rộng StaticResource. Trong ví dụ này, tôi thực hiện lưu một string đơn giản, sử dụng 2 TextBlock controls.
Tài nguyên "tĩnh" vs. tài nguyên "động"
Các ví dụ sau này, tôi sẽ dùng StaticResource markup extension để tham chiếu đến một resource, tuy nhiên đã có giải pháp thay thế là dùng form của DynamicResource
Sự khác biệt chính ở chỗ 1 tài nguyên tĩnh chỉ được xử lý 1 lần, đó là tại thời điểểm XAML được tải. Sau đó nếu tài nguyên đó được thay đổi, thì sự thay đổi này không ảnh hưởng ngược lại những nơi bạn đã sử dụng staticResource
DynamicResource mặt khác sẽ đc xử lý ngay khi cần(khi resource bị thay đổi nó sẽ xử lý ngay lập tức). Nghĩ đơn giản như là biến static vs biến động. tuy k hoàn toàn đúng, nhưng cho ta cái nhìn tốt hơn khi sử dụng. DynamicResource cũng cho phép bạn dùng các resource mà ko đc thiết kế sẵn (ví dụ như các resource đc gen ra trong lúc chạy code-behind)
Các kiểu resource khác
Sharing một string đơn giản thì dễ, nhưng chúng ta có thể làm hơn thế, Chương sau, chúng ta sẽ lưu một chuỗi string đi kèm 1 gradient brush để làm background, điều này cho chúng ta một ý tưởng về cách chúng ta sẽ làm với resource
<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>
Lần này, chúng ta vừa thêm 1 số các resource, để ứng dụng giờ có 1 vái string đơn giản, 1 chuỗi string, và một LinearGradientBrush (background có màu gradient). string dùng để làm label, chuỗi string để làm combobox, và gradient brush để làm background. và mọi thứ giờ đây tuyệt hơn, và đc lưu trong resource
Tài nguyên cục bộ và toàn bộ ứng dụng
Bây giờ, chúng ta hãy lưu tài nguyên ở mức độ Window, điều này có nghĩa là bạn có thể truy cập chúng ở mọi nơi trong ứng dụng.
Trong trường hợp bạn muốn cung cấp resource cho riêng một control, bạn có thể thêm nó vào bên trong control thay vì phải đặt trong Window. Nó hoạt động một cách tương tự nhưng phạm vi hẹp hơn, chỉ có thể truy cập bên trong control mà bạn khai báo nó:
<StackPanel Margin="10">
<StackPanel.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</StackPanel.Resources>
<Label Content="{StaticResource ComboBoxTitle}" />
</StackPanel>
Trong trường hợp này, khi resource được thêm vào StackPannel và sau đó được sử dụng bởi control Label, là một control chứa bên trong StackPannel. Các control khác chứa trong StackPannel cũng có thể sử dụng resouce, các controls bên trong nhiều lớp so với StackPannel cũng có thể truy cập vào resource đó. Tuy nhiên những control nằm bên ngoài StackPannel không thể truy cập được.
Nếu bạn cần truy cập tới tài nguyên từ nhiều window thì điều này vẫn có thể làm được. File App.xaml chứa các tài nguyên cũng giống như window và các loại control khác của WPF, và khi bạn lưu chúng trong file App.xaml thì chúng có thể được truy cập toàn cục trong tất cả các window và user control của dự án. Nó làm việc thực sự cùng một cách với việc lưu trữ và sử dụng trên 1 window:
<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>
Việc sử dụng nó cũng tương tự - WPF sẽ tự động xem xét phạm vi, từ cục bộ cho tới window và tới App.xaml để tìm ra tài nguyên mong muốn:
<Label Content="{StaticResource ComboBoxTitle}" />
Resources từ Code-behind
Tới đây, bạn đã truy cập trực tiếp tất cả tài nguyên từ XAML bằng cách sử dụng 1 markup mở rộng. Tuy nhiên bạn cũng có thể truy cập tài nguyên của bạn từ Code-behind, điều này có thể hữu ích trong một vài trường hợp. Vì vậy trong ví này, chúng ta sẽ truy cập đến 3 loại tài nguyên khác nhau từ Code-behind, mỗi loại được lưu trong 1 phạm vi khác nhau:
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());
}
}
}
Như bạn thấy, chúng ta lưu 3 thông điệp "Hello, world!" khác nhau: một trong App.xaml file, một trong window, và một cục bộ trong panel chính. Giao diện bao gồm 1 button và 1 listbox.
Trong Code-behind, chúng ta có thực thi sự kiện click của button, trong đó chúng ta thêm mỗi một chuỗi vào Listbox giống như màn hình chụp. Chúng ta sử dụng phương thức FindResource() để trả về tài nguyên dạng object (nếu có), và chúng chuyển nó sang dạng chuỗi bằng cách sử dụng phương thức ToString().
Hãy lưu ý cách chúng ta sử dụng phương thức FindReource() trên các phạm vị khác nhau - đầu tiên là trên panel, tiếp theo là window và tiếp theo trên đối tượng hiện hành Application. Rất dễ để tìm tài nguyên khi chúng ta biết nó. Nhưng như đã đề cập, nếu 1 tài nguyên không được tìm thấy, tiến trình tìm kiếm sẽ được thực thi ngược lên cấp cao hơn. Vì thế, về nguyên tắc, chúng ta có thể sử dụng phương thức FindResource() trên panel trong cả 3 trường hợp, bởi vì nó sẽ tiếp tục tìm trên window và sau đó là mức toàn ứng dụng nếu không tìm thấy.
Điều tương tự này thì không đúng đối với trường hợp khác - việc tìm kiếm sẽ không được thực thi xuôi theo cấu trúc cây, vi thế bạn không thể bắt đầu tìm một tài nguyên ở mức toàn ứng dụng nếu nó được định nghĩa cục bộ ở control hoặc window.