Руководство для начинающих (часть 2)
Судя по реакции на мою статью WPF. Руководство для начинающих. Часть 1 из 6, набралось достаточно заинтересованных людей, ради которых стоит продолжать работу над этой серией. В этой статье я решил затронуть следующие темы:
Просто потому, что мне кажется разумным объединить эти вопросы в одной статье. А вот предполагаемое содержание этой серии:
О чем же конкретно будет эта статья? Может быть, мой ответ прозвучит немного расплывчато, но я собираюсь кратко рассказать вот о чем:
Короче говоря, именно об этом и будет статья. Я подготовил простой проект, который на самом деле просто демонстрирует синтаксис. Я умышленно решил сделать пустое демонстрационное приложение, поскольку на этом этапе не хочу запутывать вас событиями, командами, привязками данных, шаблонами и стилями. Итак, это приложение намеренно сделано пустым. Если вас такой вариант не устраивает, подавайте на меня в суд. У меня отличный адвокат. Теперь можно без лишних слов переходить к делу. Что делать в XAML Оговорка. Это только рекомендация. Вы вправе делать по-своему, а мне нравится делать именно так. В общем смысле платформу WPF можно отнести к шаблону типа квази-MVC (Model-View-Controller, модель — представление — поведение). Хотя модель и поведение объединены, разделение кода XAML и кода программной части отлично отделяет представление от логики кода программной части так же, как в платформе ASP.NET. Это не классический шаблон MVC, но благодаря XAML представление действительно отделено. Рекомендую делать в XAML следующие, на мой взгляд, полезные вещи:
Да уж, список оказался коротким. По-моему, он и должен остаться таким. Очевидно, что иногда из этих правил следует делать исключения, но я думаю, что, следуя этой рекомендации, вы получите очень четкое и разделенное приложение. Что делать в коде Оговорка. Это только рекомендация. Вы вправе делать по-своему, а мне нравится делать именно так. Я считаю, что файл кода программной части (C# или VB.NET) должен выполнять всю обработку событий и пользовательскую логику для управления интерфейсом пользователя. Сам интерфейс пользователя должен быть в некоторой степени пустым. Есть пара исключений, когда необходимые действия, которые трудно реализовать в коде программной части, стоит выполнять в XAML. Естественно, в таких ситуациях следует использовать XAML. Здесь нет однозначного ответа, вы должны решить сами. Тем не менее я бы рекомендовал следующие действия:
В сущности, интерфейс пользователя (XAML) должен только представлять данные, все остальное должно делаться в коде программной части. Недавно Джош Смит (Josh Smith) опубликовал на сайте codeproject подлинный шаблон MVC для платформы WPF. С ним можно ознакомиться здесь, если вы действительно не можете дождаться и хотите во всем разобраться. Статья, правда, довольно сложная, но я возлагаю на вас большие надежды — к концу моей серии у вас должно быть достаточно знаний, чтобы посмотреть на код Джоша и, по крайней мере, понять основы (найти привязки, определить ресурсы и т. д.). Мне понадобилось полчаса, чтобы сделать то же, что и он: пришлось немного с этим повозиться. Как ссылаться на классы и сборки в XAML Если вы планируете разрабатывать что-то сложнее приложения Window, вам однозначно придется ссылаться в XAML на собственные классы и элементы управления или даже классы и элементы управления из другой сборки .NET. Сделать это в коде программной части очень просто, нужно только написать: using System.Windows.Controls //И на VB.NET imports System.Windows.Controls И вот мы можем ссылаться на все объекты в пространстве имен System.Windows.Controls. Все очень легко и удобно в коде программной части, но как это все работает в XAML? Да точно так же! Если мы хотим использовать элемент управления из текущего пространства имен, нужно указать это в коде XAML. Для этого используется директива пространства имен в верхней части файла XAML, где требуется использовать элемент управления или класс. И экземпляры таких классов можно создавать прямо в коде XAML при условии, что ссылки на них используют подходящее пространство имен. Рассмотрим четыре примера.
Код, прилагаемый к этой статье, снабжен комментариями в файле Window1.xaml, которыми можно воспользоваться, чтобы посмотреть ссылки на эти элементы управления сборки. Я пытался сделать все максимально удобным. Теперь можно смело продолжить. Ссылка на сборку 1: использование локального (из этого же пространства имен) элемента управления в элементе Window Это самый простой из четырех способов ссылки на классы в XAML. Поскольку класс, на который требуется ссылаться, находится в том же пространстве имен, достаточно добавить объявление пространства имен xmlns: в открывающий тег элемента Window (обратите внимание, я использовал элемент Window, но это мог быть элемент Frame или любой другой, где можно размещать другие элементы). Допустим, что в текущем приложении, над которым я работаю, пространство имен WPF_Tour_Beginners_Part_2, а у сгенерированной сборки то же имя. В этом же пространстве имен есть элемент типа UserControl с именем UserConrtrol2. Можно использовать следующее объявление пространства имен xmlns: в открывающем теге элемента Window: xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly=" Это позволит использовать в разметке XAML элемент UserControl с именем UserConrtrol2 следующим образом: <!-- Ссылка на сборку 1. Это объявление класса, который не входит в стандартное пространство имен Microsoft. Объявление xmlns: в верхней части файла можно использовать для ссылок на локальную (имя local: означает это же пространство имен) или другую библиотеку Dll. Ниже описывается, как определять xmlns:. Если класс находится в другой сборке, требуется указать сборку в объявлении xmlns:, в противном случае требуется только пространство имен. Обратите внимание, что строка clr-namespace/assembly вводится С УЧЕТОМ РЕГИСТРА. Этот элемент управления входит в текущее пространство имен или текущую сборку. --> <local:UserControl2/> У вас может возникнуть вопрос, почему я использовал слово local в объявлении xmlns: в элементе Window. Конечно, можно использовать любое имя, но имя local: используется во многих книгах и де-факто стало обозначением для объектов из этого же пространства имен. Я полагаю, что это объясняется тем, что они и правда локальны для текущего пространства имен или текущей сборки. Ссылка на сборку 2: использование локального (из этого же пространства имен) класса в элементе Window В этом случае необходимы практически те же действия, что и для локального элемента UserControl. Единственное отличие в том, что элемент управления UserControl — визуальный объект WPF (как правило, он наследуется от элементов Visual или Visual3D), то есть он может использоваться в интерфейсе пользователя. Классы могут наследоваться от элементов Visual или Visual3D, а могут и не наследоваться, поэтому они могут быть, а могут и не быть объектами интерфейса пользователя. Если они не наследуются от Visual или Visual3D, в разметке интерфейса их использовать нельзя, но в качестве ресурсов — можно. Мы еще не обсуждали ресурсы, поэтому пока вам просто придется принять на веру тот факт, что мы можем создать экземпляр класса, разместив ссылку на него в разделе ресурсов или в файле ресурсов файла XAML. Допустим, что в текущем приложении, над которым я работаю, пространство имен WPF_Tour_Beginners_Part_2, а у сгенерированной сборки то же имя. А также что в этом пространстве имен есть пользовательский класс LocalClass. Как и раньше, можно использовать следующее объявление пространства имен xmlns: в открывающем теге элемента Window: xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly=" Это позволит использовать пользовательский класс LocalClass в разметке XAML в виде ресурса. <StackPanel x:Name="sp1" Orientation="Vertical"> <!--Я добавил эту строку только для того, чтобы продемонстрировать возможность добавления классов в разметку XAML. Использованные мною классы не могут входить в интерфейс пользователя, поскольку они не являются элементами управления интерфейса (не наследуются от класса Visual). Поэтому я поместил их в словарь ресурсов. --> <StackPanel.Resources> <!-- Ссылка на сборку 2. В разметке XAML можно использовать локальные классы (из этого же пространства имен) при условии, что объявление xmlns: вверху этого файла является корректным --> <local:LocalClass x:Key="localClass1" AnIntProp="5"/> </StackPanel.Resources> </StackPanel> Что можно сказать об этом? Мы создали экземпляр класса LocalClass в XAML и присвоили его свойству AnIntProp значение 5. Обратите внимание, что для создания экземпляра класса в XAML в исходном классе должен быть конструктор без параметров. При необходимости мы могли бы использовать этот экземпляр класса LocalClass в коде программной части. Можно просто захватить ресурс и использовать созданный объект точно так же, как если бы он был создан и добавлен в кучу с помощью ключевого слова new. Рассмотрим эту ситуацию снова с использованием ресурса (извините, что забегаю вперед, но это очень важно). Ссылка на сборку 3: использование внешнего (из другого пространства имен или другой сборки) элемента управления в элементе Window В этом случае необходимы практически те же действия, что и для локального элемента UserControl. Единственное отличие в том, что элемент управления UserControl, который мы пытаемся использовать в текущем объекте Window, фактически находится в другой сборке (в демонстрационном приложении используется сборка SeperateWPFUserControl_Dll). Итак, очевидно, необходимо ссылаться на эту сборку SeperateWPFUserControl_Dll. Но как и ссылаться на нее, и использовать ее в XAML? Что ж, тут нет ничего отличного от того, что мы уже видели. Единственное изменение объявления пространства имен xmlns: заключается в добавлении имени Assembly. Предположим, что в текущем приложении, над которым я работаю, пространство имен WPF_Tour_Beginners_Part_2, и мы пытаемся ссылаться на сборку сторонних разработчиков (SeperateWPFUserControl_Dll в демонстрационном приложении), а эта сборка содержит элемент управления UserControl с именем UserControl1. Как и раньше, можно использовать следующее объявление пространства имен xmlns: в открывающем теге элемента Window, но в этот раз необходимо включить также имя сборки: xmlns:SeperateWPFUserControlDll="clr-namespace:SeperateWPFUserControl_Dll;assembly=SeperateWPFUserControl_Dll" Это позволит использовать в разметке XAML элемент UserControl с именем UserConrtrol1 следующим образом: <!-- Ссылка на сборку 3. Это объявление класса, который не входит в стандартное пространство имен Microsoft. Объявление xmlns: в верхней части файла можно использовать для ссылок на локальную (имя local: означает это же пространство имен) или другую библиотеку Dll. Ниже описывается, как определять xmlns:. Если класс находится в другой сборке, требуется указать сборку в объявлении xmlns:, в противном случае требуется только пространство имен. Обратите внимание, что строка clr-namespace/assembly вводится С УЧЕТОМ РЕГИСТРА. Этот элемент управления входит в пространство имен SeperateWPFUserControl_Dll и сборку SeperateWPFUserControl_Dll.dll. --> <SeperateWPFUserControlDll:UserControl1/> Как и раньше, вы можете выбрать для пространства имен любое имя, однако желательно присвоить ему имя исходной сборки, в которую входит используемый элемент. Это упростит поиск неполадок, если что-то пойдет не так. Ссылка на сборку 4: использование внешнего (из другого пространства имен или другой сборки) класса в элементе Window В этом случае необходимы практически те же действия, что и для локального класса. Единственное отличие в том, что будут использоваться классы из некоторых основных сборок .NET, но принцип остается таким же. Предположим, что в XAML требуется использовать следующие классы:
Как и раньше, можно использовать следующее объявление пространства имен xmlns: в открывающем теге элемента Window: xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib" Это позволит использовать эти классы в разметке XAML в виде ресурсов. <StackPanel x:Name="sp1" Orientation="Vertical"> <!--Я добавил эту строку только для того, чтобы продемонстрировать возможность добавления классов в разметку XAML. Использованные мною классы не могут входить в интерфейс пользователя, поскольку они не являются элементами управления интерфейса (не наследуются от класса Visual). Поэтому я поместил их в словарь ресурсов. --> <StackPanel.Resources> <!-- Ссылка на сборку 4. В разметке XAML можно использовать даже стандартные классы из пространства имен System при условии, что объявление xmlns: вверху этого файла является корректным --> <collections:Hashtable x:Key="ht1"> <sys:Int32 x:Key="key1">1</sys:Int32> <sys:Int32 x:Key="key2">2</sys:Int32> </collections:Hashtable> </StackPanel.Resources> </StackPanel> Это очень похоже на то, что мы видели в примере с локальным классом. Расширения разметки Расширения разметки увеличивают возможности выражений XAML. Строка расширения разметки может обрабатываться во время выполнения. На основании этой строки создается соответствующий объект. Расширения разметки реализуются в XAML явно и единообразно. Если значение атрибута заключено в скобки {}, компилятор-анализатор XAMLобрабатывает его как расширение разметки, а не как строковый литерал. Например, рассмотрим следующий элемент Rectangle, у которого свойству Background присвоено значение null. <Rectangle Fill="{x:Null}" Stroke="Black" StrokeThickness="2" Height="20"/> Поскольку компилятор-анализатор видит строку, заключенную в скобки {}, он знает, что это расширение разметки. Это всего лишь простенький трюк, хорошо известный анализатору XAML. Расширения разметки в XAML объявляются так же, как и классы. Для этого используется синтаксис элемента свойства. Синтаксис элемента свойства Синтаксис элемента свойства позволяет превратить любое свойство в полноценный элемент дерева XAML, в который можно добавлять дочерние элементы. Это станет очевиднее из примера. Представим, что расширение разметки можно объявлять следующим образом: <Binding RelativeSource="{RelativeSource modeEnumValue}" .../> Или, возможно, таким: <object mode="{Binding RelativeSource={RelativeSource modeEnumValue} ...}" .../> Затем с помощью синтаксиса элемента свойства мы можем обращаться со свойством Mode как с элементом дерева, у которого могут быть собственные дочерние элементы, такие как здесь: <Binding> <Binding.RelativeSource> <RelativeSource Mode="modeEnumValue"/> </Binding.RelativeSource> </Binding> //и более сложный пример <Binding> <Binding.RelativeSource> <RelativeSource Mode="FindAncestor" AncestorType="{x:Type typeName}" AncestorLevel="2" /> </Binding.RelativeSource> </Binding> Видите, сколько всего можно делать в XAML! Не забывайте, объявлять все эти объекты таким образом можно потому, что они всего лишь классы. Скобки {}, по сути, служат синтаксической оболочкой того, что скрыто за кулисами, то есть за кулисами создается вся масса классов (как только что было продемонстрировано). Такой синтаксис элемента свойства можно применять ко всему. В примере ниже другое свойство используется в качестве элемента для свойства Background типа Button. <Button Width="auto" Content="Button"> <Button.Background> <SolidColorBrush Color="Blue"/> </Button.Background> </Button> Поскольку класс System.Windows.Markup.MarkupExtension базовый, от него наследуются все расширения разметки. Вот список стандартных классов расширений разметки:
Чаще всего при программировании на платформе WPF используются расширения разметки, которые поддерживают ссылки на ресурсы (StaticResourceи DynamicResource) и привязки данных (Binding). Обратите внимание на отсутствие суффикса Extension. Он неявно подразумевается компилятором, поскольку скобки {} указывают на то, что суффикс Extension в расширении разметки НЕ требуется. Расширение разметки StaticResource предоставляет значение свойства XAML путем замены на значение уже определенного ресурса. Дополнительные сведения см. в разделе «Расширение разметки StaticResource». Расширение разметки DynamicResource предоставляет значение свойства XAML во время выполнения по ссылке на ресурс. Динамическая ссылка на ресурс приводит к новому поиску значения при каждом обращении к ресурсу. Дополнительные сведения см. в разделе «Расширение разметки DynamicResource». Расширение разметки Binding предоставляет значение свойства, привязанное к данным, в соответствии с контекстом данных, применяемым для данного элемента. Это расширение разметки относительно сложное, в нем используется встроенный синтаксис для задания привязки данных. Дополнительные сведения см. в разделе «Расширение разметки Binding». Расширение разметки RelativeSource предоставляет исходную информацию для привязки, которая может использоваться для перехода к нескольким возможным отношениям в дереве элементов во время выполнения. Это расширение предоставляет специальную исходную информацию для привязок, создаваемых в многократно используемых шаблонах и в коде в отсутствие полного знания о дереве элементов. Дополнительные сведения см. в разделе «Расширение разметки RelativeSource». Существует еще несколько других расширений разметки (см. выше), которые не относятся конкретно к использованию XAML в приложении WPF, а, скорее, являются частью спецификации и пространства имен самого языка XAML. Обычно их можно опознать по префиксу x:. Для их реализации в WPF используется тот же самый базовый класс MarkupExtension. Расширение разметки x:Type предоставляет объект Typeдля именованного типа. Чаще всего оно используется в стилях и шаблонах. Дополнительные сведения см. в разделе «Расширение разметки x:Type». Расширение разметки x:Static создает статические значения из сущностей кода типа значения, которые непосредственно не принадлежат к типу значения свойства, но могут быть приведены к нему. Расширение разметки x:Null присваивает свойству XAMLзначение null. Расширение разметки x:Array поддерживает создание общих массивов в синтаксисе XAML, когда поддержка коллекций, предоставляемая базовыми элементами и моделями элементов управления, намеренно не используется. Не волнуйтесь, скоро расширения Binding и RelativeSource станут куда непонятнее. Мы поговорим о них в статье, посвященной привязкам. ПРИМЕЧАНИЕ. Можно создавать собственные расширения разметки. Вот, например, Томер Шамам (Tomer Shamam) в отличной статье Сохранение состояния элементов управления WPF описывает, как создать расширение разметки, чтобы сохранить состояние элементов WPF в файле. Я бы также рекомендовал ознакомиться со статьей MSDN Расширения разметки и XAML, где приводятся более подробные сведения о расширениях разметки. Что такое ресурсы и чем они полезны Ресурсы — это многократно используемые объекты, которые можно объявлять в различных местах.
Ресурс можно представить как один объект, сохраненный в словаре объектов ресурсов. И поэтому у каждого ресурса ДОЛЖЕН быть ключ для безопасного извлечения уникального ресурса из словаря ResourceDictionary. При определении ресурсов в разметке уникальный ключ присваивается с помощью атрибута x:Key. Как правило, ключ — это строка, но с помощью расширений разметки можно задавать и объекты других типов. Ключи ресурсов, отличные от строк, используются в некоторых функциональных областях WPF, таких как стили, ресурсы компонентов и стили данных. Эти вопросы будут рассмотрены в следующих статьях этой серии. Каждый элемент уровня платформы (FrameworkElement или FrameworkContentElement) обладает свойством Resources, содержащим ресурсы (в виде словаря ResourceDictionary). Определять ресурсы можно для любого элемента. Однако чаще всего ресурсы определяются для корневого элемента — обычно это элемент Window. После объявления ресурса его можно использовать в элементе. Например, в следующем коде XAML объявляется новый ресурс SolidColorBrush в элементе Window. Затем этот ресурс используется в качестве фона для двух объектов Button в элементе Window. <Window x:Class="WPF_Tour_Beginners_Part_2.Window1" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:SeperateWPFUserControlDll="clr-namespace:SeperateWPFUserControl_Dll;assembly=SeperateWPFUserControl_Dll" xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly=" xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib" Title="Window1" Height="300" Width="600" WindowStartupLocation="CenterScreen"> <Window.Resources> <SolidColorBrush x:Key="windowLevelResourceBlueBrush" Color="Blue"/> </Window.Resources> <StackPanel x:Name="sp1" Orientation="Vertical"> <!--Объявление двух объектов Button, использующих ресурсWindow.windowLevelResourceBlueBrush --> <Button Width="auto" Content="1st Button : I use the resourceBrushBlue Window Resource" Background="{StaticResource windowLevelResourceBlueBrush}"/> <Button Width="auto" Content="2nd Button : I use the resourceBrushBlue Window Resource" Background="{StaticResource windowLevelResourceBlueBrush}"/> </StackPanel> </Window> В этом простом примере использования ресурсов для двух объектов Button в качестве фона будут использоваться синие объекты SolidColorBrush. (Примечание. Мы ничего не сказали о том, как будет выглядеть кнопка при наведении на нее указателя мыши. Синий объект SolidColorBrush применяется в качестве фона, только когда указатель мыши не наведен на кнопку. Этот вопрос мы рассмотрим в статье, посвященной стилям и шаблонам.) Еще следует отметить, что фактически мы используем расширение разметки StaticResourceExtension (помните, что суффикс Extension можно пропустить). Но что такое расширение StaticResourceExtensionи какие преимущества оно дает? В действительности можно использовать два типа ресурсов. Обращаться к ресурсу можно как к статическому или динамическому. Для этого используется расширение разметки StaticResourceExtension или DynamicResourceExtension соответственно. Ниже приведены рекомендации по использованию статических и динамических ресурсов согласно разделу MSDN Ресурсы WPF. Статические ресурсы Ссылки на статические ресурсы рекомендуется использовать в следующих случаях:
Динамические ресурсы Ссылки на динамические ресурсы рекомендуется использовать в следующих случаях:
Варианты использования ресурсов Итак, это было краткое знакомство с ресурсами (конечно, есть масса других особенностей ресурсов, я не могу рассказать обо всех, так что вам придется почитать что-нибудь по этому поводу). Далее мы рассмотрим, как определять некоторые из этих типов ресурсов. Возьмем, например, следующие типы ресурсов:
Ресурс уровня приложения Чтобы создать глобальный ресурс уровня приложения, следует просто добавить раздел Resources в файл App.xaml (или как у вас называется файл приложения). Рассмотрим пример. <Application x:Class="WPF_Tour_Beginners_Part_2.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="appLevelResourceGreenBrush" Color="Green"/> </Application.Resources> </Application> Это означает, что ресурсом может воспользоваться любой объект в текущем приложении. В прилагаемом демонстрационном приложении я создал один элемент Button в файле Window1.xaml, который использует ресурс уровня приложения. <!--Объявление объекта Button, который использует ресурс уровня приложения appLevelResourceGreenBrush --> <Button Width="auto" Content="1st Button : I use the appLevelResourceGreenBrush Application level Resource" Background="{StaticResource appLevelResourceGreenBrush}"/> Ресурс уровня элемента Window <Window x:Class="WPF_Tour_Beginners_Part_2.Window1" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:SeperateWPFUserControlDll="clr-namespace:SeperateWPFUserControl_Dll;assembly=SeperateWPFUserControl_Dll" xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly=" xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib" Title="Window1" Height="300" Width="600" WindowStartupLocation="CenterScreen"> <Window.Resources> <SolidColorBrush x:Key="windowLevelResourceBlueBrush" Color="Blue"/> </Window.Resources> <StackPanel x:Name="sp1" Orientation="Vertical"> <!--Объявление двух объектов Button, использующих ресурс Window.windowLevelResourceBlueBrush --> <Button Width="auto" Content="1st Button : I use the resourceBrushBlue Window Resource" Background="{StaticResource windowLevelResourceBlueBrush}"/> <Button Width="auto" Content="2nd Button : I use the resourceBrushBlue Window Resource" Background="{StaticResource windowLevelResourceBlueBrush}"/> </StackPanel> </Window> Я просто использую ресурс windowLevelResourceBlueBrush в двух объектах Button. Ресурс уровня элемента платформы Как говорилось ранее, каждый такой элемент (FrameworkElement или FrameworkContentElement) обладает свойством Resources, что позволяет создать локальный (то есть доступный только текущему элементу и его дочерним элементам) ресурс. <Window x:Class="WPF_Tour_Beginners_Part_2.Window1" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:SeperateWPFUserControlDll="clr-namespace:SeperateWPFUserControl_Dll;assembly=SeperateWPFUserControl_Dll" xmlns:local="clr-namespace:WPF_Tour_Beginners_Part_2;assembly=" xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib" Title="Window1" Height="300" Width="600" WindowStartupLocation="CenterScreen"> ..... <StackPanel x:Name="sp1" Orientation="Vertical"> <!--Я добавил эту строку только для того, чтобы продемонстрировать возможность добавления классов в разметку XAML. Использованные мною классы не могут входить в интерфейс пользователя, поскольку они не являются элементами управления интерфейса (не наследуются от класса Visual). Поэтому я поместил их в словарь ресурсов. --> <StackPanel.Resources> <SolidColorBrush x:Key="parentLevelResourceOrangeBrush" Color="Orange"/> </StackPanel.Resources> ...... ...... <!--Объявление объекта Button, который использует ресурсparentLevelResourceOrangeBrush родительского элемента (StackPanel) --> <Button Width="auto" Content="Button : I use the parentLevelResourceOrangeBrush parent (the StackPanel) level Resource" Background="{StaticResource parentLevelResourceOrangeBrush}"/> </StackPanel> </Window> Это означает, что использовать ресурс может элемент FrameworkElement, который объявляет ресурс, и его дочерние элементы. Попытка использовать ресурс, объявленный таким способом, приведет к созданию исключения. Отдельные свободные ресурсы XAML Ресурсы Windows Presentation Foundation (WPF) поддерживают функцию объединенного словаря ресурсов. Эта функция позволяет определять ресурсы приложения WPF вне скомпилированного приложения XAML. Затем такие ресурсы можно использовать в различных приложениях. Это также обеспечивает более удобную изоляцию ресурсов для локализации. Обратите внимание, что элемент Resource Dictionary не имеет атрибута x:Key, который обычно требуется для всех элементов в коллекции ресурсов. При этом другая ссылка ResourceDictionary в коллекции Merged Dictionaries является особым случаем, зарезервированным для этого сценария объединенного словаря ресурсов. Словарь ResourceDictionary, представляющий объединенный словарь ресурсов, не может обладать атрибутом x:Key. Как правило, каждый объект Resource Dictionary в коллекции Merged Dictionaries задает атрибут Source. Значение атрибута Source должно быть в виде кода URI, который разрешает путь к объединяемому файлу ресурсов. Местом назначения этого кода URI должен быть другой файл XAML с объектом ResourceDictionary в качестве корневого элемента. Рассмотрим пример. Начнем с того, как определять свободный файл XAML. <ResourceDictionary xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"> <SolidColorBrush x:Key="seperateResourceFilePinkBrush" Color="Pink"/> </ResourceDictionary> Все. Осталось только присвоить свойству Build Action значение Resource или Page в Visual Studio. Теперь можно использовать этот файл. Допустим, нужно использовать этот свободный файл XAML в свойстве Resources элемента Button. Для этого можно написать следующий код: <!--Объявление объекта Button, который использует свободный ресурсseperateResourceFilePinkBrush уровня XAML --> <Button Width="auto" Content="Button : Uses a seperate loose XAML level Resource, namely the seperateResourceFilePinkBrush resource"> <Button.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="SeperateResourceDictionary1.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Button.Resources> <Button.Background> <StaticResourceExtension ResourceKey="seperateResourceFilePinkBrush"/> </Button.Background> </Button> Поскольку в этом примере элемент Button объявляет свойство MergedDictionaries, можно использовать ВСЕ ресурсы, объявленные в свободном файле XAML, как если бы эти ресурсы были объявлены локально (локально для элемента Button). На этом закончим Как я уже говорил, это еще не самая сложная статья. Не сомневайтесь: дальше будет потруднее. С уважением, Саша Барбер. |