views:

57

answers:

1

I have a custom dependency property:

    public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("HeaderProperty", typeof(string), typeof(RadAdjustableSlider));
    public string Header
    {
        get
        {
            return (string)GetValue(HeaderProperty);
        }
        set
        {
            SetValue(HeaderProperty, value);
        }
    }

I then have a binding in my xaml:

<TextBlock Name="txtHeader" Text="{Binding ElementName=main, Path=Header, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />

Note that I also have this in the declaration at the top of the xaml file:

         x:Name="main"

Finally, I have this constructor:

    public RadAdjustableSlider()
    {
        InitializeComponent();
        this.Header = "Header";
    }

When I put this control inside of another parent control, the Header textblock is blank. Why?

Edit: This blog says that the correct way to do this is by providing a ValidateValueCallback in the DependencyProperty.Register call, but that seems like quite a bit of plumbing and doesn't explain the way dependency properties behave when interacting with external controls. Am I really going to have to write callback functions for all of my dependency properties?

+1  A: 

There is a HeaderedContentControl and HeaderedItemsControl in the framework already...

But if you really want to create your own then you should probably use a TemplateBinding. Try something like this instead:

class MyHeaderedControl : ContentControl
{
  public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
    "Header",
    typeof(object),
    typeof(MyHeaderedControl),
    new PropertyMetadata());

  public MyHeaderedControl()
  {
    this.DefaultStyleKey = typeof(MyHeaderedControl);
  }
}

Then in your project create a file at "\Themes\Generic.xaml". This is a specially named file and must be in the root of the project then in the Themes folder. It must contain a ResourceDictionary.

<ResourceDictionary
  xmlns="..."
  xmlns:x="..."
  xmlns:c="MyControlLibrary1"
  >
  <Style TargetType="{x:Type c:MyHeaderedControl>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type c:MyHeaderedControl}">
          <StackPanel>
            <ContentControl Content="{TemplateBinding Header}" />
            <ContentPresenter /> 
          </StackPanel>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

Also, in your AssemblyInfo.cs add this attribute if it's not there already:

[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, 
       ResourceDictionaryLocation.SourceAssembly)]

So for the overview. The general idea is to create some type of logical control where you have properties and events and logic etc. Then in the same assembly you provide default themes. That is how the controls will be displayed by default. At any place where the controls are used the default templates can be overriden and specific templates can be overridden as usual.

So this is the most pain free way you can add custom content like this to your custom controls! Try it once and it will make sense and not feel to cludgy. If you make more controls just keep adding them to the Generic.xaml file.

justin.m.chase
My question is more concerned with the general problem of binding children of a custom control to the control's custom property. E.g. if I have a `TextBox` and a `RadAdjustableSlider` in the same grid, the Header binding works the way I expect, but doesn't work if the `TextBox` is defined as part of the `RadAdjustableSlider`. So the "Header" aspect is irrelevant - I was simply providing a concrete example for the more general problem. I appreciate your effort, but in requiring ~20 lines of plumbing your solution defeats part of the purpose of modularizing w/ a custom control.
Jake
WPF isn't known for being terse... I still think this is the "proper" way.
justin.m.chase