首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >设计通用WPF控件的样式

设计通用WPF控件的样式
EN

Stack Overflow用户
提问于 2009-12-18 22:09:10
回答 2查看 5.2K关注 0票数 3

我正在研究创建类型安全的泛型控件。这是针对WPF 4和未来Silverlight中的(减少)泛型支持,并将包括一个泛型控件的层次结构。

我有两个问题:

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

下面的测试泛型控件定义了两个测试属性:一个非泛型Description属性和一个泛型Data属性。该控件将DefaultStyleKey设置为控件的ComponentResourceKey

下面是如何定义测试控件:

代码语言:javascript
复制
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中测试控件的样式

代码语言:javascript
复制
<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中如何声明此测试控件的一些示例:

代码语言:javascript
复制
<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 (这样做会产生“GenericControl1' TargetType does not match type of element 'GenericControl1”。例外情况)。如上文问题1所述,这有两个主要后果:

  • 要引用由泛型控件定义的属性,必须使用与RelativeSource={RelativeSource TemplatedParent}的正常绑定,而不是控件模板中的TemplateBinding
  • 您不能为Description属性创建样式设置器,即使它不依赖于控件的泛型类型。

对于后者,WPF有一个解决办法:只需将非泛型属性定义为代理类型上附加的依赖项属性。然后可以使用AddOwner“声明”泛型控件上的属性,也可以在样式设置器中使用"ProxyType.Property“语法。当然,Silverlight不支持AddOwner,在任何情况下,将本应是实例属性的内容转换为附加属性并不理想,因此这并不是一个长期的解决方案。

旁白:看起来在类型的xaml解析行为中存在一个回归。使用VS2008,我可以使用{x:Type local:GenericControl`1}获取控件的开放类型,这是我在ComponentResourceKey中使用的示例类型。但是,在VS2010中,这会导致以下错误:“字符'' was unexpected in string 'local:GenericControl1'.无效的XAML类型名称”,因此我将其改为使用代理类型。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-04-12 13:56:57

我在WPF银光论坛上发布了同样的问题。Silverlight没有收到任何答复,但以下是WPF的答复摘要:

  • 引用什里达尔的话,"XAML目前不支持指定开放的泛型类型“。
  • 使用封闭的泛型类型(如TargetType="{x:Type local:GenericControl(x:Int32)}" )将适用于单个样式,但需要复制和粘贴才能针对其他类型参数的相同控件。
  • 可以使用XamlReader和一些字符串替换为任何给定类型参数动态创建默认模板,但在控件之外创建新样式或模板时仍有一些不理想之处。
票数 2
EN

Stack Overflow用户

发布于 2009-12-19 06:46:20

  1. 可以,停那儿吧。但是您的xaml应该只基于非泛型类型。例如,我们做了这样的滑块控制。

SliderInt32 -> BaseRange -> BaseSliderControl

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

  1. 即使在silverlight中,您也可以引入一个泛型类的父类,它可以作为您的类型键,如上面的示例"BaseSliderControl“控件样式所示,只要子控件不覆盖它,就可以工作。

GenericControl`1 `1 etc名称不是正确的完全限定名,因此它将永远无法工作。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1930960

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档