Я занимаюсь созданием безопасных для типов универсальных элементов управления. Он нацелен на поддержку (уменьшенная) универсальных шаблонов в WPF 4 и будущем Silverlight и будет включать иерархию общих элементов управления.
У меня два вопроса:
- Можно ли использовать установщики стилей и привязки шаблонов для неуниверсальных свойств, определенных в универсальном элементе управления?
- В Silverlight есть ли значение, которое я могу использовать для ключа стиля по умолчанию в базовом классе, который позволит использовать тот же стиль в (временных) производных классах определенного типа? (
ComponentResourceKey
не существует в Silverlight, поэтому описанная ниже настройка не работает.)
Тестовый общий элемент управления ниже определяет два тестовых свойства: неуниверсальное свойство Description
и универсальное свойство Data
. Управление устанавливает DefaultStyleKey
на ComponentResourceKey
для управления.
Вот как определяется контрольный контроль:
public class GenericControl<T> : Control {
static GenericControl( ) {
DefaultStyleKeyProperty.OverrideMetadata(
typeof(GenericControl<T>), new FrameworkPropertyMetadata(
new ComponentResourceKey( typeof(Proxy), "GenericControl`1" )
)
);
}
public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register(
"Description", typeof(string), typeof(GenericControl<T>),
new PropertyMetadata( "Default Description" )
);
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register(
"Data", typeof(T), typeof(GenericControl<T>),
new PropertyMetadata( default(T) )
);
public string Description { get { ... } set { ... } }
public T Data { get { ... } set { ... } }
}
Вот стиль для контрольного контроля в generic.xaml
:
<Style x:Key="{ComponentResourceKey {x:Type local:Proxy}, GenericControl`1}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Description,
RelativeSource={RelativeSource TemplatedParent}}" />
<TextBlock Text="{Binding Data,
RelativeSource={RelativeSource TemplatedParent}}" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Вот несколько примеров того, как этот тестовый элемент управления будет объявлен в xaml:
<ListBox Name="list" ... />
<GenericControl x:TypeArguments="sys:Int32" Description="Count: "
Data="{Binding Items.Count, ElementName=list}" />
<Slider Name="slider" ... />
<GenericControl x:TypeArguments="sys:Double" Description="Slider Value: "
Data="{Binding Value, ElementName=slider}" />
С текущей поддержкой универсальных шаблонов в WPF 4 вы не можете использовать открытый универсальный тип в качестве TargetType
шаблона стиля или элемента управления (в результате получается, что TargetType GenericControl & # x60; 1 не соответствует типу исключение элемента 'GenericControl & # x60; 1'. "). Это имеет два основных последствия, как указано в вопросе 1 выше:
- Вы должны использовать обычную привязку с
RelativeSource={RelativeSource TemplatedParent}
вместо TemplateBinding
в шаблоне элемента управления для ссылки на свойства, определенные общим элементом управления.
- Нельзя создать установщик стиля для свойства
Description
, даже если он не зависит от универсального типа элемента управления.
Для последнего есть обходной путь в WPF: просто определите неуниверсальные свойства как присоединенные свойства зависимостей для типа прокси. Затем вы можете использовать AddOwner
, чтобы «объявить» свойства общего элемента управления, и вы можете использовать синтаксис «ProxyType.Property» в установщике стилей. Конечно, Silverlight не поддерживает AddOwner
, и превращение того, что должно быть свойством экземпляра, в присоединенное свойство, в любом случае не является идеальным, поэтому это не является долгосрочным решением.
В сторону: Похоже, что существует регрессия в поведении синтаксического анализа xaml для типов. Используя VS2008, я могу использовать {x:Type local:GenericControl`1}
, чтобы получить открытый тип элемента управления, который я использовал в качестве примера в ComponentResourceKey
. Однако в VS2010 это приводит к следующей ошибке: "Символ '& # x60;' было неожиданным в строке 'local: GenericControl & # x60; 1'. Неверное имя типа XAML. ", поэтому я изменил его, чтобы использовать вместо него тип прокси.