Содержание
Под термином «Ресурсы» в WPF может пониматься практически всё, что используется в нашем приложении — картинки, статические файлы, строки и так далее. Здесь, под ресурсами мы будем рассматривать логические ресурсы, то есть такие ресурсы, которые мы можем определить внутри приложения и использовать их в нескольких элементах приложение, например, это могут быть ракие ресурсы, как строки, значения цветов, объекты .NET и так далее.
Класс ResourceDictionary и виды ресурсов приложения
Класс ResourceDictionary в WPF выступает в качестве репозитория ресурсов приложения. Свойство этого типа определено в базовом классе FrameworkElement и, поэтому, мы можем использовать ресурсы практически в любом элементе управления в приложении WPF. К типичным ресурсам, хранящимся в ResourceDictionaryможно отнести такие элементы приложения, как стили, шаблоны элементов управления, шаблоны данных, конвертеры свойств и цвета. Так как ресурсы хранятся, по сути, в словаре, то для каждого ресурса с помощью атрибута x:Keyзадается свой уникальный ключ по которому в дальнейшем мы можем к этому ресурсу обратиться.
В приложении WPF каждый элемент управления, который является наследником FrameworkElement включая страницы приложения содержит свойство Resources в котором могут размещаться словари ресурсов. Условно, все ресурсы в WPF можно разделить на следующие группы:
- Ресурсы элементов управления, например мы можем использовать ресурсы для конкретной кнопки:
<Button>
<Button.Resources>
тут размещаем ресурсы, которые могут использоваться для этой кнопки
</Button.Resources>
</Button>
- Ресурсы макета (контейнера компоновки), например
StackPanelилиGrid, могут применяться как к макету, так и ко всем дочерним элементам. - Ресурсы, определенные на уровне страницы (
Page), можно применять к странице и ко всем его дочерним элементам. - Ресурсы, определенные на уровне приложения, могут применяться во всем приложении.
Например, мы уже изучили виды кистей в WPF. Воспользуемся этими знаниями и определим в нашем приложении новый ресурс — градиентную кисть, которая будет использоваться в приложении для заливки кнопок и других элементов управления, поддерживающих свойство Background:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<LinearGradientBrush x:Key="MyBrush">
<GradientStopCollection>
<GradientStop Color="DarkRed" Offset="0"></GradientStop>
<GradientStop Color="Red" Offset="0.5"></GradientStop>
<GradientStop Color="LightPink" Offset="1"></GradientStop>
</GradientStopCollection>
</LinearGradientBrush>
</ResourceDictionary>
</Window.Resources>
<Grid>
</Grid>
</Window>
Здесь мы определили словарь ресурсов на уровне окна приложения:
<Window.Resources>
<ResourceDictionary>
...
</ResourceDictionary>
</Window.Resources>
В этом словаре мы определили ресурс (градиентную кисть) с именем MyBrush:
<LinearGradientBrush x:Key="MyBrush">
<GradientStopCollection>
<GradientStop Color="DarkRed" Offset="0"></GradientStop>
<GradientStop Color="Red" Offset="0.5"></GradientStop>
<GradientStop Color="LightPink" Offset="1"></GradientStop>
</GradientStopCollection>
</LinearGradientBrush>
Теперь мы можем использовать этот ресурс в нашем приложении. Чтобы использовать ресурс в разметке XAML мы должны использовать расширение StaticResource или DynamicResource. Рассмотрим как мы можем использовать эти расширения и в чем их отличие.
Расширение XAML StaticResource
StaticResource — это расширение XAML, благодаря которому мы можем получать значения ресурсов. Это расширение наиболее часто используется в WPF. Сам поиск ресурса производится в следующей последовательности:
- Для
StaticResourceуказывается ключ ресурса, значение которого необходимо получить из словаря ресурсов. - Запрошенный ключ проверяется в словаре ресурсов на уровне элемента в котором производится запрос ресурса (например, на уровне
Button), если ресурс существует, то он возвращается, а процесс подстановки на этом завершается. - Если совпадение не найдено, процесс подстановки выполняет поиск по визуальному дереву вверх, проверяя словарь ресурсов каждого родительского элемента.
- Если совпадение не найдено в корневом элементе, проверяется словарь ресурсов уровня приложения.
- Если совпадение по-прежнему не найдено, то вызывается исключение типа
XamlParseException.
Используем расширение StaticResource в нашем приложении. Добавим новый элемент Button в наше приложение:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<LinearGradientBrush x:Key="MyBrush">
<GradientStopCollection>
<GradientStop Color="DarkRed" Offset="0"></GradientStop>
<GradientStop Color="Red" Offset="0.5"></GradientStop>
<GradientStop Color="LightPink" Offset="1"></GradientStop>
</GradientStopCollection>
</LinearGradientBrush>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Button Content="Кнопка"
Width="200"
Height="100"
Background="{StaticResource MyBrush}"/>
</Grid>
</Window>
Обратите внимание на то, как мы воспользовались нашим ресурсом:
<Button Content="Кнопка"
Width="200"
Height="100"
Background="{StaticResource MyBrush}"/>
Так как свойство Background у кнопки имеет тип Brush, то мы можем присвоить этому свойству значение ресурса MyBrush. В данном случае, мы воспользовались расширением XAML StaticResourceЗапустим приложение и проверим его работу:
Как видите, ресурс был успешно использован для заливки кнопки. Ничего нам не мешает аналогичным образом использовать наш ресурс в различных элементах управления WPF.
Расширение XAML DynamicResource
Расширение DynamicResource во многом схоже со StaticResource и используется аналогично. Различие заключается в том, что DynamicResource сохраняет ссылку на ресурс в словаре, что позволяет по ходу выполнение приложения изменять ресурс и использовать измененное значение, например, в визуальных элементах. Рассмотрим использование DynamicResource на примере. Добавим к нашей кнопке обработчик события Click:
private void Button_Click(object sender, RoutedEventArgs e)
{
System.Drawing.Color[] GreenColors = [System.Drawing.Color.DarkGreen, System.Drawing.Color.Green, System.Drawing.Color.LightGreen];
LinearGradientBrush resource = new();
double offset = 0;
foreach (var color in GreenColors)
{
resource.GradientStops.Add(new GradientStop()
{
Color = System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B),
Offset = offset,
});
offset += 0.5;
}
Resources["MyBrush"] = resource;
}
<Button Content="Кнопка"
Width="200"
Height="100"
Background="{DynamicResource MyBrush}"
Click="Button_Click"/>
В обработчике мы создаем новую градиентную кисть и пытаемся присвоить её ресурсу:
Resources["MyBrush"] = resource;
Теперь клик по кнопке должен привести к тому, что кисть заменится и мы увидим градиент зеленого, а не красного цвета. Чтобы это произошло, мы использовали расширение DynamicResource при определении значения Background кнопки. Запустите приложение и убедитесь, что цвет кнопки действительно меняется:
Теперь поменяйте обратно расширение на StaticResource и убедитесь, что в этом случае, кнопка не меняет своего цвета сколько бы раз мы не кликали по ней. При этом, стоит особо отметить, что даже при использовании StaticResource мы можем поменять отдельные свойства ресурса. Например, изменим код XAML кнопки следующим образом:
<Button Content="Кнопка"
Width="200"
Height="100"
Background="{StaticResource MyBrush}"
Click="Button_Click"/>
а обработчик Click кнопки сделаем таким:
private void Button_Click(object sender, RoutedEventArgs e)
{
System.Drawing.Color[] GreenColors = [System.Drawing.Color.DarkGreen, System.Drawing.Color.Green, System.Drawing.Color.LightGreen];
var resource = Resources["MyBrush"] as LinearGradientBrush;
if (resource != null)
{
resource.GradientStops.Clear();
double offset = 0;
foreach (var color in GreenColors)
{
resource.GradientStops.Add(new GradientStop()
{
Color = System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B),
Offset = offset,
});
offset += 0.5;
}
}
}
Здесь мы не создаем новый объект ресурса, а просто меняем свойства у уже существующего. Поэтому при клике по кнопке она изменит свой цвет — объект ресурса остается тем же, но с обновленными свойствами.
Итого
Ресурсы приложения WPF хранятся в словаре ResourceDictionaryи могут быть уровня элемента управления, макета, страницы или приложения в целом. Для доступа к ресурсам приложения используются расширения XAML StaticResource или DynamicResource. Использование DynamicResource позволяет изменять ресурсы приложение по ходу выполнения приложения и использовать измененные значения.
