样式通用WPF控件

时间:2009-12-18 22:09:10

标签: c# .net wpf silverlight xaml

我正在研究创建类型安全的通用控件。这是针对WPF 4和未来Silverlight中的(reduced) generics support,并将包含通用控件的层次结构。

我有两个问题:

  1. 您是否可以为通用控件上定义的非泛型属性使用样式设置器和模板绑定?
  2. 在Silverlight中,是否有一个值可以用于基类中的默认样式键,它允许在(临时)特定类型派生类中使用相同的样式? (ComponentResourceKey在Silverlight中不存在,因此下面描述的设置不起作用。)

  3. 下面的测试通用控件定义了两个测试属性:非通用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'中是意外的。无效的XA​​ML类型名称。”,所以我将其更改为使用代理类型。

2 个答案:

答案 0 :(得分:2)

我将同样的问题发布到WPFSilverlight论坛。 Silverlight没有响应,但这里是WPF答案的摘要:

  • 引用Shreedhar,“XAML目前不支持指定开放泛型类型”。
  • 使用封闭的泛型类型(例如TargetType="{x:Type local:GenericControl(x:Int32)}")将适用于单个样式,但需要复制并粘贴以针对其他类型参数定位相同的控件。
  • 可以使用XamlReader和一些字符串替换为动态创建任何给定类型参数的默认模板,但这会在控件外创建新样式或模板时留下一些需要。

答案 1 :(得分:1)

  1. 是的,你可以。但是你的xaml应该只基于非泛型类型。例如,我们制作了这样的滑块控件......
  2. SliderInt32 -> BaseRange<T> -> BaseSliderControl
    

    我们只能在BaseSliderControl上定义样式,或者只能在SliderInt32上定义样式,但不能在BaseRange上定义样式。我们可以在BaseRange类中指定泛型属性和非泛型属性,它们在SliderInt32中运行良好。

    1. 即使在silverlight中,您也可以引入一个泛型类的父类,它可以作为您的类型键,如上例所示,“BaseSliderControl”控件样式始终有效,只要子项不覆盖它。
    2. GenericControl`1等名称不是合适的完全限定名称,因此它永远不会起作用。

相关问题