phonetagger
phonetagger

Reputation: 7873

DependencyProperty compiles and works but Intellisense doesn't think so

I have two custom controls/visuals and I need an Orientation property on both. In both cases, the default should be "Horizontal" but the user of the control/visual should be able to specify Orientation="Vertical" to arrange the components of the control/visual vertically. What I have works great on my ImageButton control, but not so well on my HeaderedLabel visual. Although both of them compile fine, Intellisense doesn't like one of them. Here's an example of their use...

<Visuals:ImageButton Image="Icons/ok.png" Content="Normal Content"/>
<Visuals:ImageButton Image="Icons/ok.png" Content="Vertical Content" Orientation="Vertical"/>
<Visuals:HeaderedLabel Header="Normal Header" Content="Normal Content"/>
<Visuals:HeaderedLabel Header="Vertical Header" Content="Vertical Content" Orientation="Vertical"/>

...which produces the following when rendered inside a vertical StackPanel:

Rendered controls/visuals

So it does what I want, but the problem is this: While Intellisense recognizes the possible options for Orientation for the ImageButton, it does not recognize the possible options for Orientation for the HeaderedLabel. And while the code compiles & runs fine, there's a persistent error in the Visual Studio "Error List" pane: "'Vertical' is not a valid value for property 'Orientation'.", and there's a blue squiggly line under the text Orientation="Vertical" for the second HeaderedLabel in my xaml example above.

Here are the relevant files:

// File 'ImageButton.cs'
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Visuals
{
   public class ImageButton : Button
   {
      static ImageButton()
      {
         DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton),
            new FrameworkPropertyMetadata(typeof(ImageButton)));
      }

      public ImageSource Image
      {
         get { return (ImageSource)GetValue(ImageProperty); }
         set { SetValue(ImageProperty, value); }
      }

      public Orientation Orientation
      {
         get { return (Orientation)GetValue(OrientationProperty); }
         set { SetValue(OrientationProperty, value); }
      }

      // Note that for ImageButton, I can just say 'Orientation.Horizontal' and
      // the compiler resolves that to System.Windows.Controls.Orientation...
      public static readonly DependencyProperty OrientationProperty =
         DependencyProperty.Register("Orientation", typeof(Orientation),
            typeof(ImageButton), new UIPropertyMetadata(Orientation.Horizontal));

      public static readonly DependencyProperty ImageProperty =
         DependencyProperty.Register("Image", typeof(ImageSource),
            typeof(ImageButton), new UIPropertyMetadata(null));
   }
}

.

// File 'HeaderedLabel.cs'
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Visuals
{
   public class HeaderedLabel : Control
   {
      static HeaderedLabel()
      {
         DefaultStyleKeyProperty.OverrideMetadata(typeof(HeaderedLabel),
            new FrameworkPropertyMetadata(typeof(HeaderedLabel)));
      }

      public object Header
      {
         get { return (object)GetValue(HeaderProperty); }
         set { SetValue(HeaderProperty, value); }
      }

      public object Content
      {
         get { return (object)GetValue(ContentProperty); }
         set { SetValue(ContentProperty, value); }
      }

      public object Orientation
      {
         get { return (object)GetValue(OrientationProperty); }
         set { SetValue(OrientationProperty, value); }
      }

      public static readonly DependencyProperty HeaderProperty =
         DependencyProperty.Register("Header", typeof(object), typeof(HeaderedLabel),
            new UIPropertyMetadata(null));

      public static readonly DependencyProperty ContentProperty =
         DependencyProperty.Register("Content", typeof(object), typeof(HeaderedLabel),
            new UIPropertyMetadata(null));

      // Note that for HeaderedLabel, unlike ImageButton, I have to specify the fully-
      // qualified name of 'Orientation.Horizontal', otherwise the compiler resolves it
      // to Visuals.HeaderedLabel.Orientation and gives a compiler error...
      public static readonly DependencyProperty OrientationProperty =
         DependencyProperty.Register("Orientation", typeof(System.Windows.Controls.Orientation),
            typeof(HeaderedLabel), new UIPropertyMetadata(System.Windows.Controls.Orientation.Horizontal));
   }
}

.

   <!-- File 'Generic.xaml' -->
   <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:local="clr-namespace:Visuals"
                        xmlns:bind="clr-namespace:Visuals.BindingConverters">

       <Style TargetType="{x:Type local:ImageButton}">
          <Setter Property="Template">
             <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ImageButton}">
                   <Button>
                      <StackPanel Orientation="{TemplateBinding Orientation}">
                         <Image Source="{TemplateBinding Image}"/>
                         <ContentPresenter/>
                      </StackPanel>
                   </Button>
                </ControlTemplate>
             </Setter.Value>
          </Setter>
       </Style>

       <Style TargetType="{x:Type local:HeaderedLabel}">
          <Setter Property="Template">
             <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:HeaderedLabel}">
                   <StackPanel Orientation="{TemplateBinding Orientation}">
                      <StackPanel Orientation="Horizontal">
                         <ContentControl Content="{TemplateBinding Header}" />
                         <TextBlock Text=":" />
                      </StackPanel>
                      <ContentControl Content="{TemplateBinding Content}"/>
                   </StackPanel>
                </ControlTemplate>
             </Setter.Value>
          </Setter>
       </Style>

    </ResourceDictionary>

Any ideas why the compiler would resolve Orientation.Horizontal to System.Windows.Controls.Orientation.Horizontal for the ImageButton, but not for the HeaderedLabel? And more importantly, any ideas why Intellisense can't figure out the options for HeaderedLabel.Orientation?

BTW, I'm using VisualStudio 2012 and .NET Framework 4.0.

Upvotes: 2

Views: 461

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70652

All of your properties, including the Orientation property, are declared as having the type object.

You should have this instead:

public Orientation Orientation
{
    get { return (Orientation)GetValue(OrientationProperty); }
    set { SetValue(OrientationProperty, value); }
}

The XAML editor should be able to correctly accept values of type Orientation if you declare the property correctly. Otherwise, it will attempt to assign a string value of "Vertical" to the property, which when passed to the SetValue() method will fail, because the DependencyProperty object itself was initialized with Orientation as the valid type and it has no way to convert from the string value to an Orientation value.

If you declare the property correctly, then WPF will understand automatically that it needs to convert the string value shown in the XAML to an Orientation value for the property (i.e. parse the string value as the appropriate enum type), and in that case the initialization should work.

Upvotes: 3

Related Questions