Опубликован: 05.08.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 13:

Ресурсы в WPF

< Самостоятельная работа 12 || Самостоятельная работа 13: 123456 || Лекция 1 >

Упражнение 2. Использование ресурсов как статических свойств и полей классов

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

В WPF заготовлены 3 специальных библиотечных класса, хранящие наиболее востребованные ресурсы. Это классы SystemColors, SystemFonts и SystemParameters - все они находятся в пространстве имен System.Windows сборки PresentationFramework.dll и являются частью WPF. В пространстве имен System.Drawing тоже имеются одноименные классы SystemColors и SystemFonts, но они относятся к технологии Windows Forms и никакого отношения к рассматриваемым ресурсам не имеют.

Тот механизм хранения и извлечения ресурсов, который мы здесь будем применять, основан на использовании статических свойств и статических полей классов. Существует и другой подход к решению подобной задачи - хранение и извлечение свойств и полей экземпляров классов. Но такой способ относится к теме привязки данных и в этом разделе рассматриваться не будет.

Ключевым компонентом в месте извлечения статического свойства или поля является служебное слово x:Static, настраивающее синтаксический анализатор на поиск такого ресурса. В предыдущем упражнении мы уже подобным синтакисом пользовались для извлечения системного цвета при определении кисти в ресурсе файла App.xaml так

<!-- Определение ресурсов в коллекции приложения -->
    <Application.Resources>
        <SolidColorBrush x:Key="ControlColorBrush" 
                         Color="{x:Static SystemColors.ControlColor}" />
    </Application.Resources>

Присоединение ресурса кисти к целевому элементу в файле Window1.xaml мы задали в синтаксисе расширения разметки так

<Window x:Class="UseResource.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="UseResource" 
    MinHeight="300" 
    MinWidth="300"
    Height="300" 
    Width="300"
    Background="{StaticResource ControlColorBrush}">
    ...............................................
</Window>

Если посмотреть свойства любого из классов SystemColors, SystemFonts или SystemParameters, то они существуют парами: одно свойство представляет объект, а парное свойство представляет ключ с добавкой Key. Например, MenuBarBrush и MenuBarBrushKey. Все свойства с окончанием Key возвращают объект ключа типа ComponentResourceKey. Соответственно, существуют и два синтаксиса: извлечение самого объекта и извлечение объекта по ключу.

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

<Window
    ....................................................
    Background="{x:Static SystemColors.MenuBarBrush}"
    >
    		
    ....................................................
</Window>
<Window
    ....................................................
    Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}"
    >
    		
    ....................................................
</Window>
<Window
    ....................................................
    >
    
    <Window.Background>
        <x:Static Member="SystemColors.MenuBarBrush" />
    </Window.Background>
		
    ....................................................
</Window>
<Window
    ....................................................
    >
    
    <Window.Background>
        <DynamicResourceExtension>
            <DynamicResourceExtension.ResourceKey>
                <x:Static Member="SystemColors.MenuBarBrushKey" />
            </DynamicResourceExtension.ResourceKey>
        </DynamicResourceExtension>
    </Window.Background>
		
    ....................................................
</Window>

Извлекать можно не только статические поля и свойства упомянутых классов WPF, но и любых библиотечных классов, подходящих по типу целевому элементу. Но если применяются классы, не принадлежащие WPF, то соответствующее пространство имен и сборку нужно отобразить на разметку, использовав произвольный псевдоним (префикс). Долее этот префикс будет ассоциировать с пространством имен класса, в котором находятся статические поля или свойства. При использовании нескольких пространств имен и сборок, не принадлежащих WPF, все их нужно указать, чтобы у синтаксического анализатора не было проблем с видимостью классов. Например

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom1="clr-namespace:System;assembly=mscorlib"
    xmlns:custom2="clr-namespace:MyNamespace"
    .................................................
    >
		
    .................................................
</Window>

Продемонстрируем сказанное на простом приложении.

  • Добавьте к решению командой File/Add/New Project новый проект типа WPF Application с именем UseStaticMember и назначьте его стартовым
  • Заполните файл Window1.xaml следующим исходным кодом разметки
<Window x:Class="UseStaticMember.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Использование статических членов классов как ресурсы" 
    SizeToContent="WidthAndHeight"
    WindowStartupLocation="CenterScreen"
    ResizeMode="CanResizeWithGrip"
    >
    <StackPanel>
        
    </StackPanel>
</Window>
  • Добавьте в открывающий дескриптор окна библиотечный ресурс (объект) определения параметра цвета фона по синтаксису расширения разметки
<Window x:Class="UseStaticMember.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Использование статических членов классов как ресурсы" 
    SizeToContent="WidthAndHeight"
    WindowStartupLocation="CenterScreen"
    ResizeMode="CanResizeWithGrip"
    Background="{x:Static SystemColors.MenuBarBrush}"
    >
    <StackPanel>
        
    </StackPanel>
</Window>
  • Добавьте в открывающий дескриптор компоновочной панели код определения префиксов пространств имен системной сборки и сборки текущего проекта (для текущего проекта сборку можно не указывать, если ее имя совпадает с пространством имен)
<StackPanel
        xmlns:s="clr-namespace:System;assembly=System"
        xmlns:my="clr-namespace:UseStaticMember"
    >
        
    </StackPanel>
  • Поместите в компоновочную панель файла Window1.xaml следующий код разметки (файл приводится полностью)
<Window x:Class="UseStaticMember.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Использование статических членов классов как ресурсы" 
    SizeToContent="WidthAndHeight"
    WindowStartupLocation="CenterScreen"
    ResizeMode="CanResizeWithGrip"
    Background="{x:Static SystemColors.MenuBarBrush}"
    >
    <StackPanel
        xmlns:s="clr-namespace:System;assembly=mscorlib"
        xmlns:my="clr-namespace:UseStaticMember"
        >
        <TextBlock 
            TextAlignment="Center"
            FontFamily="Arial"
            Foreground="Red"
            FontWeight="Bold"
            FontSize="14">
            <LineBreak />
            Ресурсы из статических свойств системных классов
        </TextBlock>
        <TextBlock>
            <Label Content="OSVersion:" />
            <Label Content="{x:Static my:UserClass.OSVersion}" />
            <LineBreak />
            <Label Content="Version:" />
            <Label Content="{x:Static my:UserClass.Version}" />
            <LineBreak />
            <Label Content="DateTime:" />
            <Label Content="{x:Static my:UserClass.DateTime}" />
            <LineBreak />
            <LineBreak />
            <Label Content="MachineName:" />
            <Label Content="{x:Static s:Environment.MachineName}" />
            <LineBreak />
            <Label Content="UserName:" />
            <Label Content="{x:Static s:Environment.UserName}" />
            <LineBreak />
            <Label Content="UserDomainName:" />
            <Label Content="{x:Static s:Environment.UserDomainName}" />
            <LineBreak />
            <Label Content="SystemDirectory:" />
            <Label Content="{x:Static s:Environment.SystemDirectory}" />
            <LineBreak />
            <Label Content="CurrentDirectory:" />
            <Label Content="{x:Static s:Environment.CurrentDirectory}" />
            <LineBreak />
            <Label Content="CommandLine:" />
            <Label Content="{x:Static s:Environment.CommandLine}" />
            <LineBreak /> 
            <Label Content="ExitCode:" />
            <Label Content="{x:Static s:Environment.ExitCode}" />
            <LineBreak />
            <Label Content="HasShutdownStarted:" />
            <Label Content="{x:Static s:Environment.HasShutdownStarted}" />
            <LineBreak />
            <TextBlock Padding="10,0,10,0"
                Background="{x:Static my:UserClass.backgroundBrush}"
                Foreground="{x:Static my:UserClass.foregroundBrush}"
                FontSize="{x:Static my:UserClass.FontSize}"
                >
                <TextBlock.FontFamily>
                    <x:Static Member="my:UserClass.FontFamily" />
                </TextBlock.FontFamily>
                Ресурсы из статических полей нашего класса
            </TextBlock>
        </TextBlock>
    </StackPanel>
</Window>
  • В застраничном файле процедурного кода Window1.xaml.cs создайте класс с именем UserClass и определите в нем статические члены (static members) следующим образом (файл приводится полностью)
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
    
namespace UseStaticMember
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
    }
    
    public static class UserClass
    {
        // Статические поля только для чтения
        public static readonly LinearGradientBrush backgroundBrush = // Имя любое
            new LinearGradientBrush(Colors.Blue,
                Colors.White, new Point(0.5, 0.5), new Point(1, 1));
        public static readonly LinearGradientBrush foregroundBrush = // Имя любое
            new LinearGradientBrush(Colors.Red,
                Colors.White, new Point(1, 1), new Point(0, 0));
        public static readonly Double FontSize = 24;    // Имя любое
        public static readonly FontFamily FontFamily =  // Имя любое
            new FontFamily("Times New Roman Italic");
    
        // Статические свойства доступа
        public static String OSVersion  // Имя любое      
        {
            get { return Environment.OSVersion.ToString(); }
        }
    
        public static String Version    // Имя любое
        {
            get { return Environment.Version.ToString(); }
        }
    
        public static String DateTime   // Имя любое
        {
            get { return System.DateTime.Now.ToString(); }
        }
    }
}
  • Запустите приложение - результат будет следующим

К приведенному коду следует сделать несколько замечаний.

  1. Когда синтаксический анализатор компилирует разметку, то он способен автоматически приводить только простые типы, такие как string, int, bool, double. Более сложные типы он конвертировать не может и мы три типа: OSVersion, Version, DateTime.Now преобразуем в коде статических свойств класса
  2. Статические поля не обязательно объявлять только для чтения и ключевое слово readonly можно из кода убрать
  3. Ключевое слово static из объявления заголовка класса тоже можно убрать. Компилятор сам знает, что по классу со статическими членами нельзя создавать экземпляры и, в случае необходимости, присечет такие попытки программиста еще на этапе компиляции
  4. При объявлении свойств и полей можно, в том числе, применять и имена, совпадающие с системными. В разметке они будут адресоваться как принадлежащие пользовательскому классу. Но если в теле свойства, как в DateTime, применяется системное имя, то его нужно прописывать полностью с префиксом библиотечного пространства имен - System.DateTime
  • Разберитесь с кодом упражнения
< Самостоятельная работа 12 || Самостоятельная работа 13: 123456 || Лекция 1 >
Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Xna.Framework.Graphics;

   

namespace Application1

{

    public partial class MainForm : Form

    {

        // Объявим поле графического устройства для видимости в методах

        GraphicsDevice device;

   

        public MainForm()

        {

            InitializeComponent();

   

            // Подпишемся на событие Load формы

            this.Load += new EventHandler(MainForm_Load);

   

            // Попишемся на событие FormClosed формы

            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);

        }

   

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)

        {

            //  Удаляем (освобождаем) устройство

            device.Dispose();

            // На всякий случай присваиваем ссылке на устройство значение null

            device = null;       

        }

   

        void MainForm_Load(object sender, EventArgs e)

        {

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

            PresentationParameters presentParams = new PresentationParameters();

            // Настраиваем объект представления через его свойства

            presentParams.IsFullScreen = false; // Включаем оконный режим

            presentParams.BackBufferCount = 1;  // Включаем задний буфер

                                                // для двойной буферизации

            // Переключение переднего и заднего буферов

            // должно осуществляться с максимальной эффективностью

            presentParams.SwapEffect = SwapEffect.Discard;

            // Устанавливаем размеры заднего буфера по клиентской области окна формы

            presentParams.BackBufferWidth = this.ClientSize.Width;

            presentParams.BackBufferHeight = this.ClientSize.Height;

   

            // Создадим графическое устройство с заданными настройками

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,

                this.Handle, presentParams);

        }

   

        protected override void OnPaint(PaintEventArgs e)

        {

            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

   

            base.OnPaint(e);

        }

    }

}

Выбрасывается исключение:

Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?

Dmitriy Ivanchenko
Dmitriy Ivanchenko
Украина, Кировоград, Виктория-П, 2011
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974