Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF: Cannot set properties on property elements weirdness

private TextBlock _caption = new TextBlock();

public TextBlock Caption  
{  
    get { return _caption; }  
    set { _caption = value; }  
}

<l:CustomPanel>  
    <l:CustomPanel.Caption Text="Caption text" FontSize="18" Foreground="White" />  
</l:CustomPanel>

Gives me the following error:
Cannot set properties on property elements.

If I use:

<l:CustomPanel>  
    <l:CustomPanel.Caption>
        <TextBlock Text="Caption text" FontSize="18" Foreground="White" /> 
    </l:CustomPanel.Caption>
</l:CustomPanel>

My TextBlock shows up fine but it's nested inside another TextBlock like so, it even seems to add itself outside of the Caption property:

<l:CustomPanel>  
    <l:CustomPanel.Caption>
        <TextBlock>
             <InlineUIContainer>
                 <TextBlock Text="Caption text" FontSize="18" Foreground="White" /> 
             </InlineUIContainer>
        </TextBlock>
    </l:CustomPanel.Caption>

    <TextBlock>
         <InlineUIContainer>
             <TextBlock Text="Caption text" FontSize="18" Foreground="White" /> 
         </InlineUIContainer>
    </TextBlock>
</l:CustomPanel>

As you might have already guessed, what i'd like my code to do is to set my Caption property from XAML on a custom panel, if this is possible.

I've also tried the same code with a DependencyProperty to no avail.

So, anyone that can help me with this problem?

like image 625
Willy Avatar asked Oct 12 '09 10:10

Willy


2 Answers

I can explain what is going wrong and how to fix it.

First,

<l:CustomPanel>
  <l:CustomPanel.Caption Text="Caption text" FontSize="18" Foreground="White" />

is a simple syntax error. The <l:CustomPanel.Caption> syntax does not accept XML attributes - the property value must be within the element.

This is proper property element syntax:

<l:CustomPanel>    
  <l:CustomPanel.Caption>  
    <TextBlock Text="Caption text" FontSize="18" Foreground="White" />   
  </l:CustomPanel.Caption>  
</l:CustomPanel>

but:

  1. Property element syntax works only with DependencyProperties (so it didn't work with your CLR property) and
  2. Property element syntax always honors the ContentPropertyAttribute of the property type

Since TextBlock has a [ContentPropertyAttribute("Inlines")], the property element syntax is trying to add the TextBlock to the Inlines collection.

The solution is simple: Declare your property as a DependencyProperty of type UIElement instead of type TextBlock. This has the additional advantage of not restricting the display of content to just a TextBlock. If you really do want to restrict it to just a TextBlock, you can use a validation callback.

public UIElement Content { get { ...
public static readonly DependencyProperty ContentProperty = ...
like image 129
Ray Burns Avatar answered Nov 15 '22 14:11

Ray Burns


Just got a non-ideal workaround from a colleague of mine. It involves declaring the Caption property as a resource like:

<Page.Resources>
    <TextBlock x:Key="test" Text="Caption text" FontSize="18" Foreground="White" />
</Page.Resources>

<l:CustomPanel Caption="{StaticResource test}" />

I'd still like to know why I can't use the two previous options, so if anyone knows please answer. :)

like image 40
Willy Avatar answered Nov 15 '22 13:11

Willy