title: styling generic WPF controls
body: <p>I am looking into creating type-safe generic controls. This is targeting the <a href="http://blogs.windowsclient.net/rob_relyea/archive/2009/05/20/yes-xaml2009-isn-t-everywhere-yet.aspx" rel="nofollow noreferrer">(reduced) generics support</a> in WPF 4 and future Silverlight, and will include a hierarchy of generic controls.</p>
  
  <p>I have two questions:</p>
  
  <ol>
  <li>Can you use style setters and template bindings for non-generic properties defined on a generic control?</li>
  <li>In Silverlight, is there a value I can use for the default style key in the base class that will allow using the same style in the (temporary) specific-type derived classes? (<code>ComponentResourceKey</code> does not exist in Silverlight, so the setup described below does not work.)</li>
  </ol>
  
  <hr>
  
  <p>The test generic control below defines two test properties: a non-generic <code>Description</code> property, and a generic <code>Data</code> property. The control sets <a href="http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.defaultstylekey.aspx" rel="nofollow noreferrer"><code>DefaultStyleKey</code></a> to a <a href="http://msdn.microsoft.com/en-us/library/system.windows.componentresourcekey.aspx" rel="nofollow noreferrer"><code>ComponentResourceKey</code></a> for the control.</p>
  
  <p>Here is how the test control is defined:</p>
  
  <pre><code>public class GenericControl&lt;T&gt; : Control {
    static GenericControl( ) {
      DefaultStyleKeyProperty.OverrideMetadata(
        typeof(GenericControl&lt;T&gt;), new FrameworkPropertyMetadata(
          new ComponentResourceKey( typeof(Proxy), "GenericControl`1" )
        )
      );
    }
  
    public static readonly DependencyProperty DescriptionProperty =
      DependencyProperty.Register(
        "Description", typeof(string), typeof(GenericControl&lt;T&gt;),
        new PropertyMetadata( "Default Description" )
      );
    public static readonly DependencyProperty DataProperty =
      DependencyProperty.Register(
        "Data", typeof(T), typeof(GenericControl&lt;T&gt;),
        new PropertyMetadata( default(T) )
      );
  
    public string Description { get { ... } set { ... } }
    public T Data { get { ... } set { ... } }
  }
  </code></pre>
  
  <p>Here is the style for the test control in <code>generic.xaml</code>:</p>
  
  <pre><code>&lt;Style x:Key="{ComponentResourceKey {x:Type local:Proxy}, GenericControl`1}"&gt;
    &lt;Setter Property="Control.Template"&gt;
      &lt;Setter.Value&gt;
        &lt;ControlTemplate TargetType="{x:Type Control}"&gt;
          &lt;Border Background="{TemplateBinding Background}"
                  BorderBrush="{TemplateBinding BorderBrush}"
                  BorderThickness="{TemplateBinding BorderThickness}""&gt;
            &lt;StackPanel Orientation="Horizontal"&gt;
              &lt;TextBlock Text="{Binding Description, 
                               RelativeSource={RelativeSource TemplatedParent}}" /&gt;
              &lt;TextBlock Text="{Binding Data,
                               RelativeSource={RelativeSource TemplatedParent}}" /&gt;
            &lt;/StackPanel&gt;
          &lt;/Border&gt;
        &lt;/ControlTemplate&gt;
      &lt;/Setter.Value&gt;
    &lt;/Setter&gt;
  &lt;/Style&gt;
  </code></pre>
  
  <p>Here are some examples of how this test control would be declared in xaml:</p>
  
  <pre><code>&lt;ListBox Name="list" ... /&gt;
  &lt;GenericControl x:TypeArguments="sys:Int32" Description="Count: "
                 Data="{Binding Items.Count, ElementName=list}" /&gt;
  
  &lt;Slider Name="slider" ... /&gt;
  &lt;GenericControl x:TypeArguments="sys:Double" Description="Slider Value: "
                 Data="{Binding Value, ElementName=slider}" /&gt;
  </code></pre>
  
  <hr>
  
  <p>With the current generics support in WPF 4, you cannot use an open generic type as the <code>TargetType</code> of a style or control template (doing so results in a "'GenericControl&#x60;1' TargetType does not match type of element 'GenericControl&#x60;1'." exception). This has two main consequences, as mentioned in question 1 above:</p>
  
  <ul>
  <li>You must use a normal binding with <code>RelativeSource={RelativeSource TemplatedParent}</code> instead of a <code>TemplateBinding</code> in the control template to reference properties defined by the generic control.</li>
  <li>You cannot create a style setter for the <code>Description</code> property, even though it does not depend on the generic type of the control.</li>
  </ul>
  
  <p>For the latter, there is a workaround in WPF: just define the non-generic properties as attached dependency properties on a proxy type. Then you can use <a href="http://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty.addowner.aspx" rel="nofollow noreferrer"><code>AddOwner</code></a> to "declare" the properties on the generic control, and you can use "ProxyType.Property" syntax in a style setter. Of course, Silverlight does not support <code>AddOwner</code>, and turning what is supposed to be an instance property into an attached property is not ideal in any case, so this is not really a long-term solution.</p>
  
  <p><em>Aside:</em> It looks like there is a regression in the xaml parsing behavior for types. Using VS2008, I can use <code>{x:Type local:GenericControl&#x60;1}</code> to get the open type of the control, which I used as the example type in the <code>ComponentResourceKey</code>. In VS2010 though, this results in the following error: "Character '&#x60;' was unexpected in string 'local:GenericControl&#x60;1'. Invalid XAML type name.", so I changed it to use the proxy type instead.</p>