我正在研究创建类型安全的通用控件。这是针对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
(这样做会导致“'GenericControl`1' TargetType does not match type of element 'GenericControl`1'.”异常)。如上面的问题1所述,这有两个主要后果:
- 您必须在控件模板中使用普通绑定,并使用
RelativeSource={RelativeSource TemplatedParent}
引用定义在泛型控件中的属性,而不是使用TemplateBinding
。 - 即使
Description
属性不依赖于控件的泛型类型,您也无法为其创建样式设置器。
AddOwner
在泛型控件上“声明”属性,并且您可以在样式设置器中使用“ProxyType.Property”语法。当然,Silverlight不支持AddOwner
,并且将本应是实例属性的属性转换为附加属性在任何情况下都不是理想的,因此这不是真正的长期解决方案。另外:看起来在类型的xaml解析行为中存在回归。使用VS2008,我可以使用
{x:Type local:GenericControl`1}
来获取控件的开放类型,我将其用作ComponentResourceKey
中示例类型的示例。但是在VS2010中,这会导致以下错误:“字符串'local:GenericControl`1'中的字符'`'是意外的。无效的XAML类型名称。”,所以我改用代理类型。
GenericControl\
1是开放类型
GenericControl<>的名称(即
typeof(GenericControl<>).Name == "GenericControl`1"`)。 - Emperor XLII