Главная » Silverlight » Подключенные свойства которые применяться к нескольким элементам и определяться в разных классах

0

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

Каждый элемент управления имеет собственный набор внутренних свойств. Например, текстовое поле имеет свойства FontFamily (Семейство шрифтов), Foreground (Цвет текста) и Text (Текстовое содержимое элемента управления). При размещении элемента управления в контейнере желательно присвоить ему дополнительные сред­ства в зависимости от типа контейнера. Например, при размещении текстового поля в решетке возникает необходимость задать ячейку решетки, в которой он позициони­руется. Дополнительные средства добавляются в элемент управления с помощью под­ключенных свойств.

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

В приложении EightBall подключенные свойства позволяют отдельным элементам размещать себя в разных строках невидимой решетки.

<TextBox … Grid.Row="0">

[Здесь введите вопрос]

</TextBox>

<Button … Grid.Row="l">

Программа! Ответь на вопрос!

</Button>

CTextBox … Grid.Row="2">

[Здесь появится ответ]

</TextBox>

фактически не являются свойствами. В действительности они транслируются в вызовы методов. Синтаксический анализатор XAML вызывает статический метод, имя которого определено в форме определяющий_тип. Set имя_ свойства (). В предыдущем примере определяющим типом является класс Grid, а име­нем свойства — идентификатор Row (Строка), поэтому синтаксический анализатор вы­зывает метод Grid. SetRow ().

При вызове метода Set имя_свойства () синтаксический анализатор передает ему два параметра: изменяемый объект и задаваемое значение свойства. Например, при установке свойства Grid.Row элемента управления TextBox синтаксический анализа­тор XAML неявно выполняет следующий код.

Grid.SetRow(txtQuestion, 0);

Применяемый шаблон вызова статического метода определяющего типа скры­вает выполняемые операции. На первый взгляд кажется, что номер строки хранится в объекте Grid, однако на самом деле он хранится в объекте, к которому применяется, в данном случае — в объекте TextBox.

Этот прием работоспособен благодаря тому, что класс TextBox производится от базового класса DependencyObject, как и любой другой элемент Silverlight. В классе DependencyObject может храниться неограниченная коллекция зависимых свойств (dependency properties). — это один из типов зависимых свойств. Зависимые свойства рассматриваются в главе 4.

Имя метода Grid. SetRow () фактически является псевдонимом, вызывающим метод управляющий_объект. SetValue (). Ниже приведен пример фактического вызова в при­ложении EightBall.

txtQuestion.SetValue(Grid.RowProperty, 0) ;

— ключевой компонент технологии Silverlight. Они слу­жат для облегчения расширяемости системы. Например, определив свойство Row как подключенное, вы гарантируете возможность его использования с любым элементом управления. Другой вариант — определение свойства как части базового класса (на­пример, класса FrameworkElement) — существенно усложняет приложение. Открытый (public) интерфейс будет загроможден свойствами, имеющими смысл только в неко­торых ситуациях (в нашем примере — при использовании элемента в объекте Grid). Кроме того, будет невозможно добавить новый тип контейнера, требующий дополни­тельных свойств.

Вложение элементов

Документ XAML представляет собой древовидную структуру вложенных элементов. В рассматриваемом примере элемент окна содержит элемент Grid, который, в свою очередь, содержит элементы TextBox и Button.

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

•       Если родительский элемент реализует интерфейс IList<T>, синтаксический ана­лизатор вызывает метод IList<T>. Add () и переходит в дочерний элемент.

•       Если родительский элемент реализует интерфейс IDictionary<T>, синтаксиче­ский анализатор вызывает метод IDictionary<T>. Add () и переходит в дочерний элемент. При использовании коллекции типа словаря нужно также установить атрибут х: Key, чтобы присвоить имя ключа каждому элементу.

•       Если родительский элемент содержит атрибут ContentProperty, синтаксический анализатор использует дочерний элемент для установки свойства.

Например (см. приложение EightBall на рис. 2.1), элемент LinearGradientBrush может содержать коллекцию объектов GradientStop, заданных следующим образом.

<LinearGradientBrush> <LinearGradientBrush.GradientStops> <GradientStop 0ffset="0.00" Color="Red" /> <GradientStop Offset="0.50" Color="Indigo" /> <GradientStop Offset="l.00" Color="Violet" /> </LinearGradientBrush.GradientStops>

</LinearGradientBrush>

Синтаксический анализатор XAML распознает элемент LinearGradientBrush. GradientStops как составное свойство, поскольку его имя содержит точку. Однако вло­женные дескрипторы (три элемента GradientStop) должны быть обработаны несколько иначе. В данном случае синтаксический анализатор знает, что свойство GradientStops возвращает объект GradientStopCollection, который реализует интерфейс IList. Поэтому синтаксический анализатор считает (вполне правильно), что каждый объект GradientStop должен быть добавлен в коллекцию с помощью метода IList. Add ().

GradientStop gradientStopl = new GradientStop () ;

gradientStopl.Offset = 0;

gradientStopl.Color = Colors.Red;

IList list = brush.GradientStops;

list.Add(gradientStopl);

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

<LinearGradientBrush> <LinearGradientBrush.GradientStops> <GradientStopCollection>

<GradientStop 0ffset="0.00" Color="Red" /> <GradientStop Offset="0.50" Color="Indigo" /> <GradientStop Offset="l.00" Color="Violet" /> </GradientStopCollection> </LinearGradientBrush.GradientStops>

</LinearGradientBrush>

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

Вложенное содержимое не всегда указывает на коллекцию. Например, рассмотрим элемент Grid, содержащий несколько других элементов.

<Grid x:Name="gridl">

<TextBox x:Name="txtQuestion" . . . >

</TextBox>

<Button x:Name="cmdAnswer" . . . >

</Button>

<TextBox x:Name="txtAnswer" … >

</TextBox>

 </Grid>

Эти вложенные дескрипторы не являются составными свойствами, поскольку в их именах нет точек. Более того, элемент управления Grid не является коллекцией и не реализует интерфейс IList или IDictionary. Вместо этого элемент Grid поддерживает атрибут ContentProperty, указывающий на свойство, которое должно быть присвое­но любому вложенному содержимому. Атрибут ContentProperty принадлежит классу Panel, от которого производится класс Grid.

[ContentPropertyAttribute("Children")] public abstract class Panel : FrameworkElement

Приведенная выше запись сообщает о необходимости присвоения любому вложенно­му элементу свойства Children (Дочерний). Синтаксический анализатор XAML интер­претирует свойство содержимого по-разному, в зависимости от того, является ли оно коллекцией (если да, то оно должно реализовать интерфейс IList или IDictionary). Свойство Panel .Children возвращает объект UIElementCollection, реализующий ин­терфейс IList, поэтому синтаксический анализатор применяет метод IList. Add () для добавления вложенного содержимого в решетку.

Обрабатывая приведенную выше разметку, синтаксический анализатор XAML соз­дает экземпляр каждого вложенного элемента и передает его объекту Grid с помощью метода Grid.Children.Add().

txtQuestion = new TextBox(); gridl.Children.Add(txtQuestion); cmdAnswer = new Button(); gridl.Children.Add(cmdAnswer); txtAnswer = new TextBox(); gridl.Children.Add(txtAnswer) ;

Что происходит после этого, полностью определяется тем, как элемент управления реализует содержимое свойства. Элемент Grid выводит на экран все элементы, рас­положенные на невидимом слое строк и столбцов (см. главу 3).

Проход по вложенным элементам

Инфраструктура Silverlight предоставляет класс VisualTreeHelper, позволяющий проходить по иерархии эле­ментов. Класс VisualTreeHelper содержит три статических метода: GetParent (), возвращающий элемент, содержащий заданный элемент; GetChildrenCount (), возвращающий количество элементов, вложенных в за­данный; GetChild (), извлекающий один из вложенных элементов, заданный с помощью индекса позиции.

Преимущество класса VisualTreeHelper состоит в том, что он работает унифицированным способом, под­держивая все элементы Silverlight независимо от используемой модели содержимого. Предположим, известно, что список предоставляет несколько элементов посредством свойства items, контейнеры компоновки предоставляют дочерние элементы посредством свойства Children, а элементы содержимого предоставляют вложенные эле­менты посредством свойства Content (Содержимое). В этой ситуации только класс VisualTreeHelper может углубиться во все три уровня вложенности с помощью одного и того же бесшовного кода.

Недостаток класса VisualTreeHelper состоит в том, что он извлекает каждую деталь визуальной композиции элемента, включая все несущественные средства. Например, при использовании VisualTreeHelper для прохо­да по элементу управления ListBox вы пройдете по всем низкоуровневым деталям, которые вас не интересуют, та­ким как рамка Border, полоса прокрутки ScrollViewer, решетка Grid, служащая для компоновки элементов, и т.д. По этой причине класс VisualTreeHelper обычно используется только в рекурсивном коде для прохода вниз по дереву до тех пор, пока не будет найден элемент нужного типа, после чего найденный элемент обрабатыва­ется. Ниже приведен пример использования этого метода для очистки всех текстовых полей в иерархии элементов.

private void Clear (Dependency-Object element) {

// Если это текстовое поле, текст очищается

TextBox txt = element as TextBox;

if (txt != null) txt.Text = "";

// Проверка вложенных элементов

int children = VisualTreeHelper.GetChildrenCount(element);

for (int і = 0; і < children; i++)

{

DependencyObject child =

VisualTreeHelper.GetChild(element, i);

Clear(child);

}

)

/

Чтобы запустить проход, вызовите метод Clear () через проверяемый объект верхнего уровня. Например, для про­хода по всей текущей странице нужно выполнить оператор Clear (this);.

События

До сих пор мы рассматривали привязку атрибутов к свойствам. Однако атрибу­ты можно также использовать и для подключения обработчиков событий. Для этого используется синтаксис имя_события= "имя_обработчика ".

Например, элемент управления Button (Кнопка) предоставляет событие Click (Щелчок). Подключить к нему обработчик можно следующим образом.

<Button .. . Click="cmdAnswer_Click">

Предполагается, что в коде фонового класса определен метод cmdAnswer_Click. Обработчик события должен иметь правильную сигнатуру, т.е. такую же, как и делегат события Click. Ниже приведен пример обработчика.

private void cmdAnswer_Click(object sender, RoutedEventArgs e)

AnswerGenerator generator = new AnswerGenerator();

txtAnswer.Text =

generator.GetRandomAnswer(txtQuestion.Text);

}

Во многих ситуациях атрибуты используются для установки свойств и подключения обработчиков событий одного и того же элемента. Компилятор Silverlight всегда при­держивается одной и той же последовательности: сначала он устанавливает свойство Name (если нужно), затем подключает обработчик и устанавливает остальные свойства. Благодаря этому при установке свойства в первый раз будет запущен любой обработ­чик, реагирующий на изменение свойства.

Источник: Мак-Дональд, Мэтью. Silverlight 3 с примерами на С# для профессионалов. : Пер. с англ. —- М. : ООО «И.Д. Вильяме», 2010. — 656 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии