Главная » Разработка для Windows Phone 7 » Присоединенные свойства Windows Phone 7

0

Присоединенные свойства на первый взгляд кажутся очень загадочными. Как мы уже знаем из главы 9, так они могут выглядеть в XAML:

<Canvas>

<Ellipse Style="{StaticResource ellipseStyle}" Canvas.Left="116" Canvas.Top="92" />

</Canvas>

Это фрагмент приложения EllipseChain.

Canvas.Left и Canvas.Top – присоединенные свойства. Это свойства, описанные классом Canvas и задаваемые для дочерних элементов Canvas.

Как я уже говорил в главе 9, в классе Canvas нет ни одного члена с именем Left или Top. Для задания этих присоединенных свойств в коде мы используем два статических метода, определенных классом Canvas:

Canvas.SetLeft(ellipse, 116); Canvas.SetTop(ellipse, 92);

Или можно воспользоваться методом SetValue, унаследованным классом Ellipse от DependencyObject, и обратиться к свойствам-зависимостям, описанным классом Canvas:

ellipse.SetValue(Canvas.LeftProperty, 116.0); ellipse.SetValue(Canvas.TopProperty, 92.0);

Это те же самые методы SetValue, которые класс вызывает в CLR-свойстве для задания свойства-зависимости.

Это практически все, что необходимо знать для описания собственных присоединенных свойств. В проекте CanvasCloneDemo (Демонстрация клона холста) используется класс CanvasClone (Клон холста). Этот клон определяет два поля DependencyProperty: LeftProperty и TopProperty:

Проект: CanvasCloneDemo Файл: CanvasClone.cs (фрагмент)

public class CanvasClone : Panel {

public static readonly DependencyProperty LeftProperty = DependencyProperty.RegisterAttached("Left", typeof(double), typeof(CanvasClone),

new PropertyMetadata(0.0, 0nLeft0rTopPropertyChanged));

public static readonly DependencyProperty TopProperty = DependencyProperty.RegisterAttached("Top", typeof(double), typeof(CanvasClone),

new PropertyMetadata(0.0, 0nLeft0rTopPropertyChanged));

}

Обратите внимание, ранее в данной главе объекты DependencyProperty создавались с помощью статического метода DependencyProperty.Register. Поля DependencyObject в CanvasClone создаются единственно возможным альтернативным способом: с помощью метода DependencyProperty.RegisterAttached (Зарегистрировать присоединенное свойство). Это делает свойства присоединенными и позволяет задавать их для классов, в которых они не описаны.

Первый аргумент конструктора PropertyMetadata явно определен типа double, благодаря этому не будет возникать ошибки времени выполнения из-за того, что компилятор C# принимает передаваемое значение за int.

После того как поля DependencyProperty описаны, нам понадобятся статические методы для доступа к присоединенным свойствам. Имена этих методов начинаются с Set и Get, за которыми следуют имена присоединенных свойств, в данном случае это Left и Top.

Проект: CanvasCloneDemo Файл: CanvasClone.cs (фрагмент)

public static void SetLeft(Dependency0bject obj, double value) obj.SetValue(LeftProperty, value);

public static double GetLeft(Dependency0bject obj) return (double)obj.GetValue(LeftProperty);

public static void SetTop(Dependency0bject obj, double value) obj.SetValue(TopProperty, value);

public static double GetTop(Dependency0bject obj) return (double)obj.GetValue(TopProperty);

Данные методы вызываются либо явно из кода, либо неявно из синтаксического анализатора XAML. Первый аргумент – это объект, для которого задается присоединенное свойство, т.е. первым аргументом, по всей вероятности, будет дочерний элемент CanvasClone. Тело метода использует этот аргумент для вызова методов SetValue и GetValue дочернего элемента. Эти же методы определены классом DependencyObject для задания и возвращения значений свойств-зависимостей.

При изменении значений этих свойств вызывается обработчик события изменения значения свойства, определенный в конструкторе PropertyMetadata. Сигнатура этого метода аналогична стандартному методу-обработчику события изменения значения свойства для обычных свойств-зависимостей.

static void 0nLeft0rTopPropertyChanged(Dependency0bject obj,

DependencyPropertyChangedEventArgs args)

{ }

Опять же, это статический метод. Но первый аргумент не является объектом типа CanvasClone. Это дочерний элемент объекта CanvasClone. Или, скорее, это дочерний элемент CanvasClone. Метод CanvasClone.SetLeft может быть вызван для элемента, который не является дочерним элементом панели. Более того, методы CanvasClone.SetLeft и OnLeftOrTopPropertyChanged (При изменении свойства Слева или Сверху) могут быть вызваны в условиях, когда не существует никакого экземпляра CanvasClone!

По этой причине в теле метода должны быть предприняты определенные меры предосторожности. С помощью вызова такого удобного статического метода VisualTreeHelper.GetParent (Получить родителя) он получает аргумент DependencyObject и приводит его к CanvasClone:

Проект: CanvasCloneDemo Файл: CanvasClone.cs (фрагмент)

static void OnLeftOrTopPropertyChanged(DependencyObject obj,

DependencyPropertyChangedEventArgs args)

{

CanvasClone parent = VisualTreeHelper.GetParent(obj) as CanvasClone;

if (parent != null)

parent.InvalidateArrange();

}

Если родителем объекта, вызвавшего CanvasClone.SetLeft или CanvasClone.SetTop, на самом деле является CanvasClone, метод вызывает InvalidateArrange родителя, т.е. CanvasClone.

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

Проект: CanvasCloneDemo Файл: CanvasClone.cs (фрагмент)

protected override Size MeasureOverride(Size availableSize) {

foreach (UIElement child in Children)

child.Measure(new Size(Double.PositiveInfinity,

Double.PositiveInfinity));

return Size.Empty;

}

Canvas так построен, что MeasureOverride всегда возвращает нуль независимо от наличия дочерних элементов, поэтому CanvasClone делает то же самое. MeasureOverride по-прежнему должен вызвать Measure для всех дочерних элементов, в противном случае для них не будет задан размер, но Measure вызывается с неограниченными размерами, что заставляет дочерние элементы принимать минимально возможные размеры.

Когда панель вызывает собственный InvalidateArrange, начинается перекомпновка и вызывается ArrangeOverride. Этот метод заставляет панель расставить элементы на своей поверхности. По сути, он задает размер и местоположение каждого дочернего элемента.

Проект: CanvasCloneDemo Файл: CanvasClone.cs (фрагмент)

protected override Size Arrange0verride(Size finalSize) {

foreach (UIElement child in Children) child.Arrange(new Rect(

new Point(GetLeft(child), GetTop(child)), child.DesiredSize));

return base.Arrange0verride(finalSize);

}

ArrangeOverride вызывает собственные статические методы GetLeft и GetTop для каждого дочернего элемента, чтобы определить, куда должен быть перемещен дочерний элемент относительно самого себя. Размер каждого дочернего элемента – это просто DesiredSize, который был вычислен при пересчете размеров.

XAML-файл в CanvasCloneDemo полностью аналогичен XAML-файлу проекта EllipseChain, только Canvas заменен на CanvasClone:

Проект: CanvasCloneDemo Файл: MainPage.xaml (фрагмент)

<Grid x:Name="ContentPanel" Grid.Row="1"> <local:CanvasClone>

<local:CanvasClone.Resources> <Style x:Key="ellipseStyle" TargetType="Ellipse"> <Setter Property="Width" Value="100" /> <Setter Property="Height" Value="100" />

<Setter Property="Stroke" Value="{StaticResource PhoneAccentBrush}"

/>

<Setter Property="StrokeThickness" Value="10" /> </Style> </local:CanvasClone.Resources>

<Ellipse Style="{StaticResource ellipseStyle}"

local:CanvasClone.Left="0" local:CanvasClone.Top="0" />

<Ellipse Style="{StaticResource ellipseStyle}"

local:CanvasClone.Left="52" local:CanvasClone.Top="53" />

<Ellipse Style="{StaticResource ellipseStyle}"

local:CanvasClone.Left="116" local:CanvasClone.Top="92" />

<Ellipse Style="{StaticResource ellipseStyle}"

local:CanvasClone.Left="190" local:CanvasClone.Top="107" />

<Ellipse Style="{StaticResource ellipseStyle}"

local:CanvasClone.Left="2 63" local:CanvasClone.Top="92" />

<Ellipse Style="{StaticResource ellipseStyle}"

local:CanvasClone.Left="32 6" local:CanvasClone.Top="53" />

<Ellipse Style="{StaticResource ellipseStyle}"

local:CanvasClone.Left="3 8 0" local:CanvasClone.Top="0" /> </local:CanvasClone> </Grid>

С восторгом мы обнаруживаем на экране изображение, абсолютно аналогичное получаемому при выполнении первого приложения:

 

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

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