我正在研究创建类型安全的通用控件。这是针对WPF 4和未来Silverlight中的(reduced) generics support,并将包含通用控件的层次结构。
我有两个问题:
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与类型不匹配element'GenericControl`1'。“exception)。这有两个主要后果,如上面问题1所述:
RelativeSource={RelativeSource TemplatedParent}
而不是TemplateBinding
的常规绑定来引用通用控件定义的属性。Description
属性创建样式设置器,即使它不依赖于控件的泛型类型。对于后者,WPF中有一种解决方法:只需将非泛型属性定义为代理类型的附加依赖项属性。然后,您可以使用AddOwner
“声明”通用控件上的属性,并且可以在样式设置器中使用“ProxyType.Property”语法。当然,Silverlight不支持AddOwner
,并且将应该是实例属性的内容转换为附加属性在任何情况下都不理想,因此这不是一个长期的解决方案。
旁白:看起来类型的xaml解析行为存在回归。使用VS2008,我可以使用{x:Type local:GenericControl`1}
来获取控件的开放类型,我在ComponentResourceKey
中将其用作示例类型。但是在VS2010中,这会导致以下错误:“字符'''在字符串'local:GenericControl`1'中是意外的。无效的XAML类型名称。”,所以我将其更改为使用代理类型。
答案 0 :(得分:2)
我将同样的问题发布到WPF和Silverlight论坛。 Silverlight没有响应,但这里是WPF答案的摘要:
TargetType="{x:Type local:GenericControl(x:Int32)}"
)将适用于单个样式,但需要复制并粘贴以针对其他类型参数定位相同的控件。XamlReader
和一些字符串替换为动态创建任何给定类型参数的默认模板,但这会在控件外创建新样式或模板时留下一些需要。答案 1 :(得分:1)
SliderInt32 -> BaseRange<T> -> BaseSliderControl
我们只能在BaseSliderControl上定义样式,或者只能在SliderInt32上定义样式,但不能在BaseRange上定义样式。我们可以在BaseRange类中指定泛型属性和非泛型属性,它们在SliderInt32中运行良好。
GenericControl`1等名称不是合适的完全限定名称,因此它永远不会起作用。