Зачем нужен валидный код и как устранить ошибки валидации

Ресурсы локализованных приложений

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

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

Создание файлов ресурсов

В файлах ресурсов могут храниться элементы, подобные изображениям и таблицам строк. В качестве ресурсного файла может быть обычный текстовый файл или файл с расширением.resX, в котором используется XML. В этой статье сначала рассматривается вариант простого текстового файла.

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

Ниже показан пример создания простой таблицы строк:

Title = Professional C# Site = сайт Author = Alex Erohin

Утилита Resgen.exe

Для создания из ***.txt файла ресурсов можно воспользоваться специальной утилитой генерации файлов ресурсов Resgen.exe. Например, ввод следующей команды:

Resgen MyResources.txt

приведет к созданию файла MyResources.resources. Сгенерированный этой утилитой файл ресурсов далее можно либо добавить в сборку как внешний файл, либо вставить в сборку DLL или ЕХЕ. Утилита Resgen также поддерживает возможность создания файлов ресурсов в формате XML с расширением.resX. Применяется она очень просто:

Resgen MyResources.txt MyResources.resX

Выполнение этой команды приведет к созданию XML-файла ресурсов по имени MyResources.resX.

Утилита Resgen поддерживает строго типизированные ресурсы. Строго типизированный ресурс представляется в виде класса, который получает доступ к ресурсам. Для создания такого класса в утилите Resgen предусмотрена опция /str:

Resgen /str:C#,MyResources.cs MyResources.resX

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

Возможность добавления изображений утилита Resgen не поддерживает. Среди примеров.NET Framework SDK есть и пример ResXGen с обучающими руководствами. В ResXGen можно ссылаться на изображения в файле.resX. Добавлять изображения можно также программно с применением классов ResourceWriter или ResXResourceWriter, как будет показано далее.

Класс ResourceWriter

Вместо использования для создания файлов ресурсов утилиты Resgen можно написать специальную, позволяющую это делать программу. Класс ResourceWriter из пространства имен System.Resources служит для создания бинарных файлов ресурсов, а класс ResXResourceWriter - для создания файлов ресурсов на базе XML. Оба эти класса поддерживают возможность добавления изображений и любых других сериализуемых объектов. В случае применения класса ResXResourceWriter потребуется сослаться на сборку System.Windows.Forms.

В следующем примере кода демонстрируется создание объекта ResXResourceWriter по имени rw в файле Demo.resx. После создания экземпляра с помощью метода AddResource() класса ResXResourceWriter можно приступать к добавлению набора ресурсов общим объемом до 2 Гбайт. Первый аргумент в AddResource() позволяет указывать имя ресурса, а второй - значение. Ресурс изображения можно добавлять за счет применения экземпляра класса Image. Чтобы можно было использовать класс Image, необходимо сослаться на сборку System.Drawing, а также добавить директиву using для открытия пространства имен System.Drawing.

Здесь объект Image создается за счет открытия файла logo.gif, поэтому потребуется либо скопировать этот файл изображения в каталог исполняемой программы, либо указать полный путь к нему в аргументе метода ImageToFile(). Оператор using указывает, что ресурс изображения должен автоматически уничтожаться в конце блока using.

Далее в объект ResXResourceWriter добавляются простые строковые ресурсы. В конце метод Close() класса ResXResourceWriter автоматически вызывает ResXResourceWriter.Generate() для осуществления записи ресурсов в файл Demo.resx:

Using System.Drawing; using System.Resources; namespace ProCSharp.Localization { class Program { static void Main() { var rw = new ResXResourceWriter("Demo.resx"); using (Image image = Image.FromFile("logo.gif")) { rw.AddResource("MyLogo", image); rw.AddResource("Title", "Professional C#"); rw..AddResource("Author", "Alex Erohin"); rw.Close(); } } } }

Запуск этой небольшой программы приведет к созданию файла ресурсов Demo.resx с изображением logo.gif внутри.

Использование файлов ресурсов

Добавлять файлы ресурсов в сборки можно либо с помощью работающего из командной строки C#-компилятора csc.exe с опцией /resource, либо прямо в Visual Studio 2010. Чтобы посмотреть, как работать с файлами ресурсов в Visual Studio 2010, создайте проект консольного приложения по имени ResourceDemo.

Добавьте в этот проект созданный ранее файл ресурсов Demo.resx, открыв в окне Solution Explorer контекстное меню и выбрав в нем пункт Add --> Add Existing Item (Добавить --> Добавить существующий элемент). По умолчанию для свойства Build Action (Действие при компоновке) этого ресурса будет установлено значение Embedded Resource (Встраиваемый ресурс), указывающее, что этот ресурс должен встраиваться в выходную сборку.

Далее в параметрах проекта (за счет выбора Application --> Assembly information (Приложение --> Информация о сборке)) следует установить в качестве значения параметра Neutral Language (Нейтральный язык) основной язык:

Изменение значения этого параметра приведет к добавлению в файл assemblyinfо.cs атрибута , как показано ниже:

Установка значения для данного атрибута улучшит производительность ResourceManager, поскольку позволит ему быстрее отыскивать ресурсы для en-US, а также использовать их в качестве варианта по умолчанию. В этом атрибуте можно также указать место размещения используемого по умолчанию ресурса за счет применения второго параметра в конструкторе. С помощью перечисления UltimateResourceFallbackLocation можно указать, что он должен размещаться в главной сборке (значение MainAssembly) или же в подчиненной (значение Satellite).

После компоновки проекта можно просмотреть сгенерированную сборку утилитой ildasm и увидеть в манифесте атрибут, .mresource. Атрибут.mresource объявляет имя для ресурса в сборке. Если.mresource объявлен public (как в данном примере), это означает, что ресурс может экспортироваться из сборки и использоваться в классах других сборок. Если же.mresource объявлен private, это значит, что ресурс экспортироваться не может и доступен только в пределах данной сборки.

Для получения доступа к встроенному ресурсу используется класс ResourceManager, который находится в пространстве имен System.Resources. Конструктору этого класса в качестве аргумента можно передать имя сборки, в которой содержатся ресурсы.

В рассматриваемом примере ресурсы встроены в исполняемую сборку, поэтому во втором аргументе конструктору должен быть передан результат выполнения метода Assembly.GetExecutingAssembly() . В первом аргументе передается корневое имя ресурсов, состоящее из названия пространства имен и имени файла ресурсов, но без расширения resources. Как было показано ранее, это имя можно отобразить с помощью утилиты ildasm и просто удалить из него расширение resources. Имя можно также получить и программно с применением метода GetManifestResourceNames() класса System.Reflection.Assembly:

Using System; using System.Drawing; using System.Reflection; using System.Resources; namespace Wrox.ProCSharp.Localization { class Program { static void Main() { var rm = new ResourceManager("Wrox.ProCSharp.Localization.Demo", Assembly.GetExecutingAssembly()); Console.WriteLine(rm.GetString("Title")); Console.WriteLine(rm.GetString("Site")); Console.WriteLine(rm.GetString("Author")); using (Image logo = (Image)rm.GetObject("MyLogo")) { logo.Save("logo.bmp"); } StronglyTypedResources(); } private static void StronglyTypedResources() { Console.WriteLine(Demo.Title); Console.WriteLine(Demo.Site); Console.WriteLine(Demo.Author); using (Bitmap logo = Demo.MyLogo) { logo.Save("logo.bmp"); } } } }

Для создания строго типизированного ресурса в редакторе управляемых ресурсов (Managed Resources Editor) можно изменить значение параметра Access Modifier (Модификатор доступа) с No Code Generation (Не генерировать никакой код) на Public (Общедоступный) или Internal (Внутренний). В случае установки значения Public генерируемый класс снабжается модификатором доступа public и тогда к нему возможен доступ из других сборок. При установке значения Internal генерируемый класс получает модификатор доступа internal и доступ к нему может осуществляться только изнутри сборки, в которой он находится.

Пространство имен System.Resources

Давайте кратко пройдемся по всем классам, которые содержатся в пространстве имен System.Resources и позволяют работать с ресурсами.

Класс ResourceManager

Может использоваться для получения ресурсов, относящихся к текущей культуре, из сборок или файлов ресурсов. С помощью ResourceManager можно получать сразу целый набор ресурсов для определенной культуры в виде экземпляра ResourceSet.

Класс ResourceSet

Позволяет представлять набор ресурсов для определенной культуры. При создании экземпляр ResourceSet он производит перечисление по классу, реализуя интерфейс IResourceReader, и сохраняет все ресурсы в HashTable.

Интерфейс IResourceReader

Используется в ResourceSet для перечисления ресурсов. Класс ResourceReader реализует этот интерфейс.

Класс ResourceWriter

Применяется для создания файла ресурсов и реализует интерфейс IResourceWriter.

Классы ResXResourceSet, ResXResourceReader и ResXResourceWriter

Похожи на классы ResourceSet, ResourceReader и ResourceWriter, но служат для создания не бинарного файла ресурсов, а не XML-файла.resx. Вместо того чтобы встраивать ресурс в XML-файл, они позволяют добавлять на него ссылку с помощью ResXFileRef.

Пространство имен System.Resources.Tools

Содержит класс StronglyTypedResourceBuilder, который можно использовать для создания класса из ресурса.

к.э.н. Лавлинский Н. Е., технический директор ООО «Метод Лаб»

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

Предыдущие стандарты

Идея об управлении загрузкой не нова. Ранее были разработаны несколько вариантов тегов link с атрибутами subresource , prerender и prefetch . Однако, они работали несколько иначе: с их помощью можно загружать элементы страниц или целые страницы, которые могут потребоваться при дальнейшей навигации по сайту. То есть, браузер отправлял такие запросы с низким приоритетом и в последнюю очередь. Если же нужно повысить приоритет, то решений не было.

Загрузка ресурсов с preload

Что же даёт новая спецификация? Во-первых, теперь загрузка происходит с уточнением, что загружается. Исходя из указанного типа ресурса браузером выставляется приоритет загрузки. Например:

link rel = "preload" href = "/js/script.js" as= "script" >
link rel = "preload" href = "/fonts/1.woff2" as= "font" type = "font/woff2" crossorigin>

Во-вторых, тип ресурса (as ) позволяет браузеру послать правильные заголовки, чтобы сервер мог отправить контент с лучшим вариантом сжатия (например, послать WebP картинки, если браузер их поддерживает).

Во втором примере мы загружаем файл шрифта, при этом указан конкретный формат (WOFF2), который поддерживается не всеми браузерами. Однако, пока поддержка механизма preload совпадает с поддержкой такого формата, проблем не возникает. Текущую поддержку механизма можно посмотреть .

Ускоренная загрузка шрифтов

В качестве примера ускорения сайта с использованием preload можно назвать загрузку глубоко закопанных ресурсов, например, шрифтов. В обычном процессе загрузки браузер должен сначала загрузить CSS-файл с указанием на шрифт, провести парсинг этого файла и только потом поставить в очередь запрос на скачивание файла шрифта.

Если мы укажем preload этого шрифта в коде HTML-страницы, браузер отправит запрос сразу же после разбора HTML-документа, что может быть на несколько секунд раньше, чем в обычном случае. А мы знаем, что подключаемые шрифты являются блокирующими элементами и задерживают отрисовку шрифта на странице, поэтому загрузить их нужно как можно быстрее. Особенно остро эта проблема стоит при использовании HTTP/2, когда браузер отправляет сразу множество запросов к серверу, в результате чего какие-нибудь картинки могут заполнить полосу клиента и загрузка важных ресурсов будет отложена.

Асинхронная загрузка CSS

CSS-файлы всегда блокируют рендеринг страницы, поэтому все CSS-ресурсы, загрузку которых можно отложить, можно загружать как обычные файлы и динамически подключать к странице.

Делается это следующим образом:

link rel = "preload" as= "style" href = "async_style.css" onload = "this.rel="stylesheet"" >

Загрузка JS-кода без исполнения

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

Это можно сделать с помощью следующего кода:

link rel = "preload" as= "script" href = "async_script.js" onload = "var script = document.createElement("script"); script.src = this.href; document.body.appendChild(script);" >

Мы рассмотрели основные способы использования механизма preload, но возможности на этом не ограничиваются, проводите собственные эксперименты!

Последнее обновление: 27.01.2016

В WPF важное место занимают ресурсы . В данном случае под ресурсами подразумеваются не дополнительные файлы (или физические ресурсы ), как, например, аудиофайлы, файлы с изображениями, которые добавляются в проект. Здесь речь идет о логических ресурсах , которые могут представлять различные объекты - элементы управления, кисти, коллекции объектов и т.д. Логические ресурсы можно установить в коде XAML или в коде C# с помощью свойства Resources . Данное свойство опредлено в базовом классе FrameworkElement , поэтому его имеют большинство классов WPF.

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

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

Определение ресурсов

Определим ресурс окна и ресурс кнопки:

Здесь у окна определяются два ресурса: redStyle, который представляет объект SolidColorBrush, и gradientStyle, который представляет кисть с линейным градиентом. У кнопки определен один ресурс darkStyle, представляющий кисть SolidColorBrush. Причем каждый ресурс обязательно имеет свойство x:Key , которое и определяе ключ в словаре.

А в свойствах Background соответственно у грида и кнопки мы можем применить эти ресурсы: Background="{StaticResource gradientStyle}" - здесь после выражения StaticResource идет ключ применяемого ресурса.

Управление ресурсами в коде C#

Добавим в словарь ресурсов окна градиентную кисть и установим ее для кнопки:

// определение объекта-ресурса LinearGradientBrush gradientBrush = new LinearGradientBrush(); gradientBrush.GradientStops.Add(new GradientStop(Colors.LightGray, 0)); gradientBrush.GradientStops.Add(new GradientStop(Colors.White, 1)); // добавление ресурса в словарь ресурсов окна this.Resources.Add("buttonGradientBrush", gradientBrush); // установка ресурса у кнопки button1.Background = (Brush)this.TryFindResource("buttonGradientBrush"); // или так //button1.Background = (Brush)this.Resources["buttonGradientBrush"];

С помощью свойства Add() объект кисти и его произвольный ключ добавляются в словарь. Далее с помощью метода TryFindResource() мы пытаемся найти ресурс в словаре и установить его в качестве фона. Причем, так как этот метод возвращает object, необходимо выполнить приведение типов.

Всего у ResourceDictionary можно выделить следующие методы и свойства:

    Метод Add(string key, object resource) добавляет объект с ключом key в словарь, причем в словарь можно добавить любой объект, главное ему сопоставить ключ

    Метод Remove(string key) удаляет из словаря ресурс с ключом key

    Свойство Uri устанавливает источник словаря

    Свойство Keys возвращает все имеющиеся в словаре ключи

    Свойство Values возвращает все имеющиеся в словаре объекты

Для поиска нужного ресурса в коллекции ресурсов у каждого элемента определены методы FindResource() и TryFindResource() . Она оба возвращают ресурс, соответствующий определенному ключу. Единственное различие между ними состоит в том, что FindResource() генерирует исключение, если ресурс с нужным ключом не был найден. А метод TryFindResource() в этом случае просто возвращает null.

Разделяемые ресурсы

Когда один и тот же ресурс используется в разных местах, то фактически мы используем один и тот же объект. Однако это не всегда желательно. Иногда необходимо, чтобы примение ресурса к разным объектам различалось. То есть нам необходимо, чтобы при каждом применении создавался отдельный объект ресурса. В этом случае мы можем изспользовать выражение x:Shared="False" :

Примеры использования ресурсов

Рассмотрим еще пару примеров применения ресурсов. К примеру, если мы хотим, чтобы ряд кнопок обладал одинаковыми свойствами, то мы можем определить одну общую кнопку в качестве ресурса:

Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: