tags:

views:

165

answers:

3

If I have an object derived from System.Windows.DispatcherObject but defines a ControlTemplate.

public class A : System.Windows.DependencyObject
{
   public ControlTemplate ControlTemplate {get; set;}
}

which is a member of

public class B 
{
   public A NonUIElement {get; set;}
}

Is it possible to render this object via a Binding such as

<Border Name="Border">
<ContentPresenter Margin="5,0" Content="{Binding NonUIElement }"/>
</Border>

assuming the Datacontext of border is set to an instance of B?

TIA.

+2  A: 

The object will render, but not in the way I think you're hoping for. The Content of the ContentPresenter is set to the instance of A. WPF then tries to figure out how to render this instance of A. It first asks, is this object a UIElement? In this case, the answer is no. So it next looks for a DataTemplate for the type. In this case, there's no DataTemplate for the A class. So it falls back on calling ToString(). So your ContentPresenter will display a TextBlock containing the text "YourNamespace.A".

The fact that A happens to have a member of type ControlTemplate does not affect this logic. To WPF, that's just a chunk of data that A happens to be carrying around. WPF only uses ControlTemplate when there is a Control involved and the ControlTemplate is assigned to the Template property.

So you need either to supply a DataTemplate for A (which of course can access the ControlTemplate and use it to render the instance), or create a named DataTemplate and apply that via ContentPresenter.ContentTemplate, or derive from UIElement instead.

itowlson
Excellent explanation, thank you. However, when you say I need to supply a DataTemplate for A where do I assign this within my HierarchicalDataTemplate, which is what I am working with?
e28Makaveli
Probably the easiest way to do this initially is to assign it on the ContentPresenter.ContentTemplate property: `<ContentPresenter Content="{Binding NonUIElement}" ContentTemplate="{StaticResource MyNonUIElementTemplate}" />`.
itowlson
If WPF uses will look for the ControlTemplate for NonUIElement before it tries to render it within the ContentPresenter specified in your suggestion, why do I still have to supply a DataTemplate?Should this <ContentPresenter Content="{Binding NonUIElement}" /> not implicitley mean "Use ControlTemplate of NonUIElement to to render content?"
e28Makaveli
Also, what would the composition of MyNonUIElementTemplate look like?Something like this simply throws a StackOverflow exception. <DataTemplate x:Key="AnyControlTemplate"> <Border> <ContentPresenter /> </Border> </DataTemplate>I cannot seem to wrap my head around this.
e28Makaveli
WPF will *NOT* "look for the ControlTemplate for NonUIElement before it tries to render it." As far as the content model is concerned, NonUIElement is just a chunk of data. It does not know that you want the ControlTemplate property to have significance for rendering. Per my answer, "WPF only uses ControlTemplate when there is a Control involved and the ControlTemplate is assigned to the Template property." That is, the significance of ControlTemplate is implemented by the Control class, not by DependencyObject or the WPF rendering infrastructure.
itowlson
I think that throws a StackOverflowException because you're essentially telling it to repeat the same rendering within a Border. So it keeps trying to instantiating the template within the template within the template... until the stack overflows. You need to do something like <DataTemplate><Button Template="{Binding ControlTemplate}" /></DataTemplate> (using whatever control has appropriate behaviour for what you want to render). But looking at your answer, what you want is a DataTemplate, not a ControlTemplate. I'll post a comment to that answer.
itowlson
A: 

This is my actual DataTemplate with binding to LayerRepresentation my challenge. LayerRepresentation could be anything so I want it to be rendered according to its ControlTemplate, if it has one.

 <HierarchicalDataTemplate DataType="{x:Type vm:MapLayerModel}" ItemsSource="{Binding Path=Children, Mode=OneTime}">
                <DockPanel Height="24" Margin="2,0" LastChildFill="False">
                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsVisible}"/>                    
                    <Border BorderBrush="Black" BorderThickness="1">
                        <ContentPresenter 
                        Margin="5" 
                        Content="{Binding LayerRepresentation}" 
                        ContentTemplate="{Binding Path=ControlTemplate, RelativeSource={RelativeSource AncestorType={x:Type esriSymbols:Symbol}}}"    />
                    </Border>                                                                                                                                                                                                                                                                         
                    <Image 
                        x:Name="img" 
                        DockPanel.Dock="Left" 
                        Margin="0,0,4,0"
                        Source="{Binding Path=Icon, Mode=OneTime}"                                            
                        Width="16" Height="16" />                                                               
                    <TextBlock Text="{Binding Path=Name, Mode=OneTime}" Foreground="Black" VerticalAlignment="Center" DockPanel.Dock="Left" />                    
                </DockPanel>
            </HierarchicalDataTemplate>
e28Makaveli
<ContentPresenter ContentTemplate="{Binding Path=ControlTemplate}" /> won't work because ContentTemplate needs to be a *DataTemplate*, not a ControlTemplate. Change your property from being of type ControlTemplate to type DataTemplate and you should be good to go. (Also I think it might need to be a nested path e.g. Path=NonUIElement.Template -- not quite sure.)
itowlson
Thanks again for the response but I still cannot get this item to render. Using actual names, the class I am trying to render is ESRI.ArcGIS.Client.Symbol that has two members: A Dispatcher and ControlTemplate. I want to render visual tree within Symbol.ControlTemplate on each treeview node through MapLayerModel.LayerRepresentation which could be an instance of Symbol The DataTemplate I have is <DataTemplate x:Key="AnyControlTemplate' ><ContentControl Template="{Binding ControlTemmplate}" /> </DataTemplate>.
e28Makaveli
Used in my treeview template like this: <HierarchicalDataTemplate DataType="{x:Type vm:MapLayerModel}" ItemsSource="{Binding Path=Children, Mode=OneTime}"><Border BorderBrush="Black" BorderThickness="1"><ContentPresenter Margin="5" Content="{Binding LayerRepresentation}" ContentTemplate="{StaticResource AnyControlTemplate}" /></Border></HierarchicalDataTemplate> all I see is the border, even though the Symbol has a something defined in its ControlTemplate. Where am I going wrong? I apologize for not getting it but you've given me more insight to templating that what I could find on MSDN.
e28Makaveli
A: 

I finally got it with this;

<HierarchicalDataTemplate DataType="{x:Type vm:MapLayerModel}" ItemsSource="{Binding Path=Children, Mode=OneTime}">   
**<ContentControl Margin="5" Content="{Binding LayerRepresentation}" Template="{Binding LayerRepresentation.ControlTemplate}" Mode=OneTime/>**
</HierarchicalDataTemplate>

This has been a great personal lesson on WPF templating and its content control model. Thanks again itowlson.

e28Makaveli