Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Размещение ресурсов в коллекции окна






  • Создайте новое решение с именем Resources командой File/New/Project и новый проект с именем UseResource


увеличить изображение

  • Добавьте к корню проекта UseResource новую папку Images и скопируйте в нее контекстной командой Add/Existing Item из прилагаемого к работе каталога Source три рисунка, как показано на снимке

 

  • Выделяя последовательно эти файлы рисунков, установите (или проверьте) в панели Properties для них следующие свойства
    • FACE02.ICO, FACE04.ICO:
      • Build Action=Resource
      • Copy to Output Directory=Do not copy
    • 10440.jpg:
      • Build Action=None
      • Copy to Output Directory=Copy if newer

Эти растровые изображения мы в дальнейшем используем для создания ресурсов, обеспечивающих фон кнопок интерфейса окна.

  • Откройте файл App.xaml и добавьте в коллекцию ресурсов приложения объект SolidColorBrush со следующими настройками
< Application x: Class=" UseResource.App" xmlns=" https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns: x=" https://schemas.microsoft.com/winfx/2006/xaml" StartupUri=" Window1.xaml" > <! -- Определение ресурсов в коллекции приложения --> < Application.Resources> < SolidColorBrush x: Key=" ControlColorBrush" Color=" {x: Static SystemColors.ControlColor}" /> < /Application.Resources> < /Application>

Цвет для настройки помещаемого в ресурсы объекта SolidColorBrush извлекается из принадлежащего WPF статического свойства ControlColor статического класса SystemColors. Этот ресурс мы используем в параметрах окна для задания его цвета фона. В дальнейшем мы более подробно рассмотрим механизм использования статических свойств и полей в объектных ресурсах, а пока заметим, что псевдоним x в строке

xmlns: x=" https://schemas.microsoft.com/winfx/2006/xaml"

отображает пространство имен WPF на разметку для синтаксического анализатора XAML.

  • Заполните файл Window1.xaml следующим содержимым (разметка файла приводится полностью)
< Window x: Class=" UseResource.Window1" xmlns=" https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns: x=" https://schemas.microsoft.com/winfx/2006/xaml" Title=" UseResource" MinHeight=" 300" MinWidth=" 300" Height=" 300" Width=" 300" WindowStartupLocation=" CenterScreen" Background=" {StaticResource ControlColorBrush}" > <! -- Определение ресурсов в коллекции окна --> < Window.Resources> < TextBlock x: Key=" Title1" TextAlignment=" Center" Text=" 1. Модификация объекта ресурса" FontSize=" 14" FontWeight=" Bold" FontFamily=" Arial" FontStyle=" Italic" TextDecorations=" Underline" Foreground=" DarkViolet" /> < SolidColorBrush x: Key=" ForegroundBrush1" Color=" Blue" /> < ImageBrush x: Key=" BackgroundBrush1" TileMode=" Tile" ViewportUnits=" Absolute" Viewport=" 0 0 10 10" ImageSource=" Images/FACE02.ICO" Opacity=" 0.5" /> < TextBlock x: Key=" Title2" TextAlignment=" Center" Text=" 2. Полная замена объекта ресурса" FontSize=" 14" FontWeight=" Bold" FontFamily=" Arial" FontStyle=" Italic" TextDecorations=" Underline" Foreground=" DarkViolet" /> < SolidColorBrush x: Key=" ForegroundBrush2" Color=" Red" /> < ImageBrush x: Key=" BackgroundBrush2" TileMode=" Tile" ViewportUnits=" Absolute" Viewport=" 0 0 24 24" ImageSource=" Images/FACE04.ICO" Opacity=" 0.3" /> < /Window.Resources> <! -- Содержимое окна --> < StackPanel> <! -- Это не ресурс, а элемент визуального дерева --> < TextBlock HorizontalAlignment=" Center" FontSize=" 18" FontWeight=" Bold" Margin=" 0, 0, 0, 5" > Применение ресурсов < /TextBlock> <! -- Синтаксис подключения ресурсов 'как элемента свойства' --> < Label HorizontalAlignment=" Center" > < Label.Content> < StaticResource ResourceKey=" Title1" /> < /Label.Content> < /Label> < Button Name=" btn1" Padding=" 5" Margin=" 5" FontWeight=" Bold" FontSize=" 16" > < Button.Foreground> < DynamicResource ResourceKey=" ForegroundBrush1" /> < /Button.Foreground> < Button.Background> < DynamicResource ResourceKey=" BackgroundBrush1" /> < /Button.Background> Использует DynamicResource < /Button> < Button Name=" btn2" Padding=" 5" Margin=" 5" FontWeight=" Bold" FontSize=" 16" > < Button.Foreground> < StaticResource ResourceKey=" ForegroundBrush1" /> < /Button.Foreground> < Button.Background> < StaticResource ResourceKey=" BackgroundBrush1" /> < /Button.Background> Использует StaticResource < /Button> <! -- Синтаксис подключения ресурсов 'как расширения разметки' --> < Label HorizontalAlignment=" Center" Content=" {StaticResource ResourceKey=Title2}" /> < Button Click=" btn3_Click" Padding=" 5" Margin=" 5" FontWeight=" Bold" FontSize=" 16" Foreground=" {DynamicResource ResourceKey=ForegroundBrush2}" Background=" {DynamicResource ResourceKey=BackgroundBrush2}" > Использует DynamicResource < /Button> <! -- Упрощенный синтаксис без указания ResourceKey --> < Button Click=" btn4_Click" Padding=" 5" Margin=" 5" FontWeight=" Bold" FontSize=" 16" Foreground=" {StaticResource ForegroundBrush2}" Background=" {StaticResource BackgroundBrush2}" > Использует StaticResource < /Button> < /StackPanel> < /Window>

Представление разметки в режиме Design на данном этапе должно выглядеть так

 

Вначале мы создали 6 объектов с настройками и разместили их в коллекции Window.Resources ресурсов окна. Два объекта TextBlock будут отображать заголовки. Два объекта SolidColorBrush будут использованы для определения цвета надписей на кнопках. Два объекта ImageBrush будут задавать фон кнопок. В настройках объектов, помещенных в ресурсы, в качестве обязательного параметра должен присутствовать ключ x: Key, по которому эти ресурсы будут извлечены элементами интерфейса окна.

Затем мы разместили элементы пользовательского интерфейса - надписи и кнопки. В первой половине элементов мы подключили ресурсы по синтаксису " элемента свойства ", а в остальных - по синтаксису " расширения разметки ". В обоих случаях главным атрибутом в подключении является параметр ResourceKey. Обратите внимание, что при использовании синтаксиса расширения разметки этот параметр можно не указывать и запрещается ключевое слово размещать в кавычках. Но оба синтаксиса подключения являются совершенно равнозначными.

Обязательным при подключении ресурсов к элементу является указание способа их применения с помощью ключевого слова DynamicResource или StaticResource. Отсюда и название - динамические или статические ресурсы. Если ресурс в процедурном коде полностью не подменяется новым объектом, а остается постоянным или только редактируется (адрес объекта не меняется), то каким бы способом он не был подключен к использующим его элементам, внешне эти элементы ведут себя одинаково.

Но если помещенный в ресурс объект в процедурном коде заменить на новый (адрес объекта изменится), то элемент со статическим способом подключения этого не заметит и будет использовать старую копию ресурса. Элемент же с динамическим способом подключения это заметит и сразу использует новый ресурс. В WPF операция смены ресурса преобразуется в вызов метода InvalidateVisual(), означающий, что элемент должен быть немедленно перерисован вызовом виртуального метода OnRender().

Итак, если в коллекции ресурсов полностью не менять адрес объекта ресурса на новый, то элементы с разными способами подключения ведут себя одинаково: замечают все изменения в существующем ресурсе. А объекты ресурсов, соответственно, исправно посылают элементам уведомления о своих изменениях. Обычно необходимость в полной смене ресурса встречается редко, поэтому лучше использовать статический способ подключения. Тем более, что динамический ресурс требует повышенных накладных расходов при обработке.

Для разнообразия, заголовки на форме окна мы тоже определили как ресурсы TextBlock. Но чтобы их присоединить к интерфейсу и отобразить, пришлось использовать элементы Label с универсальным свойством Content типа Object.

Следующим шагом мы создадим процедурный код, который будет управлять ресурсами и наглядно продемонстрирует разницу между способами подключения DynamicResource и StaticResource.

  • Заполните файл Window1.xaml.cs следующим процедурным кодом
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; namespace UseResource{ public partial class Window1: Window { public Window1() { // Инициализация интерфейсных элементов InitializeComponent(); // Создаем и добавляем программно объекты ресурсов // для будущей замены декларативных ресурсов целиком // //... alternateColor SolidColorBrush solidColorBrush = new SolidColorBrush(); solidColorBrush.Color = Colors.Blue; this.Resources.Add(" alternateColor", solidColorBrush); // //... alternateImage ImageBrush imageBrush = new ImageBrush(); imageBrush.ImageSource = new BitmapImage(new Uri(@" Images\10440.jpg", UriKind.Relative)); imageBrush.TileMode = TileMode.Tile; imageBrush.ViewportUnits = BrushMappingMode.Absolute; // Не путать с ViewboxUnits imageBrush.Viewport = new Rect(0, 0, 32, 32); imageBrush.Opacity = 0.5D; // Можно и без D, преобразует неявно из float в double this.Resources.Add(" alternateImage", imageBrush); // Регистрируем часть обработчиков программно btn1.Click += btn1_Click; btn2.Click += btn2_Click; /* // Эти ресурсы не будем сохранять, потому что мы их будем только модифицировать resourceDictionary.Add(" ForegroundBrush1", this.Resources[" ForegroundBrush1" ]); resourceDictionary.Add(" BackgroundBrush1", this.Resources[" BackgroundBrush1" ]); //*/ // Заполняем словарь ресурсов: дублируем ссылки на ресурсы resourceDictionary.Add(" originColor", this.Resources[" ForegroundBrush2" ]); resourceDictionary.Add(" originImage", this.Resources[" BackgroundBrush2" ]); } // Поле-ссылка на объект, может объявляться в любом месте как член класса ResourceDictionary resourceDictionary = new ResourceDictionary(); // Просто модифицируем объект ресурса bool changeResourceFlag1 = true; void btn1_Click(object sender, RoutedEventArgs e) { // Извлекаем ресурсы из словаря окна и приводим к типу двумя вариантами SolidColorBrush colorRes = (SolidColorBrush)this.Resources[" ForegroundBrush1" ]; ImageBrush imageRes = this.Resources[" BackgroundBrush1" ] as ImageBrush; if (changeResourceFlag1)// Редактируем существующий объект ресурса { colorRes.Color = Colors.Red; imageRes.Viewport = new Rect(0, 0, 16, 16); } else // Возвращаем к прежнему { colorRes.Color = Colors.Blue; imageRes.Viewport = new Rect(0, 0, 10, 10); /* // Восстанавливать из словаря нельзя - ссылаются на один и тот же объект colorRes = resourceDictionary[" ForegroundBrush1" ]; imageRes = resourceDictionary[" BackgroundBrush1" ]; //*/ } changeResourceFlag1 =! changeResourceFlag1; // Готовим другой вариант } // Просто модифицируем объект ресурса void btn2_Click(object sender, RoutedEventArgs e) { // Ищем ресурс по ключу, начиная с текущего элемента к корню дерева // Метод TryFindResource защищать не нужно, потому что при неудаче он // не генерирует исключение, а просто возвращает нулевую ссылку SolidColorBrush colorRes = ((FrameworkElement)sender). TryFindResource(" ForegroundBrush1") as SolidColorBrush; ImageBrush imageRes = (ImageBrush)((FrameworkElement)sender). TryFindResource(" BackgroundBrush1"); if (colorRes == null || imageRes == null)// Ресурс не найден return; if (changeResourceFlag1)// Редактируем существующий объект ресурса { colorRes.Color = Colors.Red; imageRes.Viewport = new Rect(0, 0, 16, 16); } else // Возвращаем к прежнему { colorRes.Color = Colors.Blue; imageRes.Viewport = new Rect(0, 0, 10, 10); } changeResourceFlag1 =! changeResourceFlag1; // Готовим другой вариант } // Заменяем ресурс новым объектом bool changeResourceFlag2 = true; private void btn3_Click(object sender, RoutedEventArgs e) { if (changeResourceFlag2)// Полностью заменяем объекты ресурсов на новые { this.Resources[" ForegroundBrush2" ] = this.Resources[" alternateColor" ]; this.Resources[" BackgroundBrush2" ] = this.Resources[" alternateImage" ]; } else // Восстанавливаем объекты из словаря ресурсов { this.Resources[" ForegroundBrush2" ] = resourceDictionary[" originColor" ]; this.Resources[" BackgroundBrush2" ] = resourceDictionary[" originImage" ]; } changeResourceFlag2 =! changeResourceFlag2; // Готовим другой вариант } // Заменяем ресурс новым объектом private void btn4_Click(object sender, RoutedEventArgs e) { // Повышаем полномочия ссылки, чтобы применить метод FindResource. Можно повысить // только до FrameworkElement, там уже есть методы FindResource и TryFindResource Button btn = (Button)sender; // Объявляем локальные ссылки Object colorRes, imageRes; if (changeResourceFlag2)// Полностью заменяем объекты ресурсов на новые { // Защищенно ищем ресурс методом FindResource, который // при отсутствии ресурса генерирует исключение try { colorRes = btn.FindResource(" alternateColor"); imageRes = btn.FindResource(" alternateImage"); } catch { return; } // Если не нашли, все оставляем как есть this.Resources[" ForegroundBrush2" ] = colorRes; this.Resources[" BackgroundBrush2" ] = imageRes; } else // Восстанавливаем объекты из словаря ресурсов { this.Resources[" ForegroundBrush2" ] = resourceDictionary[" originColor" ]; this.Resources[" BackgroundBrush2" ] = resourceDictionary[" originImage" ]; } changeResourceFlag2 =! changeResourceFlag2; // Готовим другой вариант } }}
  • Запустите приложение и испытайте работу ресурсов

Снимки окна до изменения и после изменения объектов ресурсов в коде будут такими

   

Кнопки в нашем приложении мы использовали только для того, чтобы совместить элементы, отображающие ресурсы, с элементами, инициирующими код их изменения. Первые две кнопки (сверху) реализуют обработчики, которые модифицируют существующие ресурсы, не меняя адреса подключенных объектов. Они решают одну и ту же задачу, но разными способами. Последние две кнопки в своих обработчиках полностью меняют объекты ресурсов, но тоже делают это по разному.

Последняя кнопка, к которой ресурс подключен как статический, не замечает полной замены объектов ресурсов и продолжает отображать прежние ресурсы. Вторая сверху кнопка, которая тоже использует статический способ подключения ресурсов, реагирует на изменения, поскольку адресует прежний объект, хотя и с измененными настройками.

Мы точно знаем, что наши используемые элементами ресурсы размещены в коллекции ресурсов окна. Поэтому мы жестко адресуемся в коде к этим ресурсам по ссылке this, указывающей на объект окна. Такой способ не совсем гибкий - при переносе разметки ресурсов в коллекцию приложения придется переделывать процедурный код. Использование методов TryFindResource() и FindResource(), наследуемых объектами от класса FrameworkElement, делает код более универсальным. Но метод FindResource() генерирует исключение, если ресурс не будет найден, поэтому его разумнее поместить в контейнер обработки исключений.

Для разнообразия, мы использовали класс-словарь ResourceDictionary, но он сохраняет только ссылки на существующие ресурсы. Поэтому для восстановления модифицированных ресурсов мы его на применяли, поскольку он ссылается на те же уже модифицированные объекты ресурсов. Зато при полной смене объектов ресурсов, когда ссылки коллекции окна получат адреса новых объектов, ссылки словаря продолжат адресовать прежние ресурсы и словарь правомерно использовать для восстановления адресации.

В конструкторе класса окна мы разместили код программного создания ресурсов. Поскольку при срабатывании конструктора сам объект окна уже существует, то этот код можно было бы разместить и до вызова метода InitializeComponent(). Там же в конструкторе мы динамически выполнили регистрацию части обработчиков в дополнение к регистрации другой их части в разметке.

  • Разберитесь с кодом, который снабжен подробными комментариями





© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.