Positioning elements generated by a data template on a Grid or Canvas

I'm trying to use an XML data source and a data template to generate some TextBlock elements from this data and present them using an ItemsControl. The TextBlocks are generated correctly, but I can't figure out how to position them on a Grid or Canvas panel. It seems the attached properties (Grid.Column, Grid.Row, Canvas.Left, etc) that I define on the TextBlocks are somehow ignored.

This is the XAML code I'm using:

<StackPanel xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005">

<StackPanel.Resources>
    <XmlDataProvider x:Key="dataSource" XPath="/Test">
        <Test xmlns="">
            <Key Name="test"/>
        </Test>
    </XmlDataProvider>

    <DataTemplate x:Key="dataTemplate">
        <TextBlock Canvas.Left="20" Canvas.Top="20" Text="{Binding XPath=@Name}"/>
    </DataTemplate>

    <Canvas x:Key="dataCanvas"/>
</StackPanel.Resources>

<ItemsControl ItemsSource="{Binding Source={StaticResource dataSource}, XPath=Key}" ItemTemplate="{StaticResource dataTemplate}" ItemsPanel="{StaticResource dataCanvas}" />

</StackPanel>


This XAML markup generates a single TextBlock element as defined in the XML source but doesn't position it correctly on the Canvas. I get the same results with a Grid panel. What is wrong

Also, when I tried traversing the logical tree (using LogicalTreeHelper) to see if I was missing something I couldn't get past the ItemsControl element; it said it had no children. Does the data template generate a logical tree under the ItemsControl element If so, how can one access this tree


Answer this question

Positioning elements generated by a data template on a Grid or Canvas

  • arpicheck

    In the case that the property is being set, the Panel cannot have children.  The current implementation of the addition of ColumnDefinitions & RowDefinitions treats them as if they were children even though they're objects for the Grid.RowDefinitions & Grid.ColumnDefinitions collections.  So, there are actually no Rows & Columns that are being created in the first markup.  In the second, this restriction doesn't exist.  

  • Vertexwahn

    Slowly getting there... now still trying to understand as the tools really don't give my any good guidance.

    Can anyone explain why the following (exerpt from the sample) does not compile:

    <ItemsControl ItemsSource="{Binding Source={StaticResource sudokuxml}, XPath=testitem}"
                        ItemTemplate="{StaticResource itemdatatemplate}">
            <ItemsControl.Style>
              <Style TargetType="{x:Type ItemsControl}">
                <Setter Property="Template">
                  <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ItemsControl}">
                      <Grid IsItemsHost="true">
                        <RowDefinitions>
                          <RowDefinition/>
                          <RowDefinition/>
                        </RowDefinitions>
                        <ColumnDefinitions>
                          <ColumnDefinition/>
                          <ColumnDefinition/>
                          <ColumnDefinition/>
                        </ColumnDefinitions>
                      </Grid>
                    </ControlTemplate>
                  </Setter.Value>
                </Setter>
              </Style>
            </ItemsControl.Style>

    and the if I leave the "RowDefinitions" and "ColumnDefinitions" out, it DOES compile   I seems awkward as RowDefinitions and ColumnDefinitions seem to me the only two valid descendants for a Grid element

    How did you guys learn about contentpresenter and itemscontrol   Any good references   It seems like they are certainly not encouraged to be used when reading the docs.  Soooo cool I got this to work!

    Thanks a  lot,
    Best regards,

    Christof

  • tao84

    Thanks for the tip. That works for the Canvas, but for some reason it doesn't work for the Grid. Take this example:


    <StackPanel xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"> 

       <StackPanel.Resources> 
          <XmlDataProvider x:Key="mapData" XPath="/Map"> 
             <Map xmlns=""> 
                <Tile X="0" Y="0" Image="0.png"/> 
                <Tile X="1" Y="0" Image="1.png"/> 
                <Tile X="0" Y="1" Image="2.png"/> 
                <Tile X="1" Y="1" Image="3.png"/> 
             </Map> 
          </XmlDataProvider> 
       </StackPanel.Resources> 

       <ItemsControl ItemsSource="{Binding Source={StaticResource mapData}, XPath=Tile}"> 
          <ItemsControl.ItemsPanel> 
             <Grid> 
                <RowDefinition/><RowDefinition/> 
                <ColumnDefinition/><ColumnDefinition/> 
             </Grid> 
          </ItemsControl.ItemsPanel> 

          <ItemsControl.ItemTemplate> 
             <DataTemplate> 
                <Image Source="{Binding XPath=@Image}"/> 
             </DataTemplate> 
          </ItemsControl.ItemTemplate> 

          <ItemsControl.ItemContainerStyle> 
             <Style TargetType="{x:Type ContentControl}"> 
                <Setter Property="Grid.Column" Value="{Binding XPath=@X}"/> 
                <Setter Property="Grid.Row" Value="{Binding XPath=@Y}"/> 
             </Style> 
          </ItemsControl.ItemContainerStyle> 
       </ItemsControl>

    </StackPanel>


    With this code all the items appear on top of each other, even though I define the Grid.Row and Grid.Column attached properties of their containers correctly. It seems the Grid object is instantiated with no rows and columns, even though the <RowDefinition/> and <ColumnDefinition/> tags are there.

    On the other hand, if I define the items panel through a style that overrides the control template for the ItemsControl I get the correct behavior:


    <StackPanel xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"> 

       <StackPanel.Resources> 
          <XmlDataProvider x:Key="mapData" XPath="/Map"> 
             <Map xmlns=""> 
                <Tile X="0" Y="0" Image="0.png"/> 
                <Tile X="1" Y="0" Image="1.png"/> 
                <Tile X="0" Y="1" Image="2.png"/> 
                <Tile X="1" Y="1" Image="3.png"/> 
             </Map> 
          </XmlDataProvider> 
       </StackPanel.Resources> 

       <ItemsControl ItemsSource="{Binding Source={StaticResource mapData}, XPath=Tile}"> 
          <ItemsControl.Style> 
             <Style TargetType="{x:Type ItemsControl}"> 
                <Setter Property="Template"> 
                   <Setter.Value> 

                      <ControlTemplate TargetType="{x:Type ItemsControl}"> 
                         <Grid IsItemsHost="true"> 
                            <RowDefinition/><RowDefinition/> 
                            <ColumnDefinition/><ColumnDefinition/> 
                         </Grid> 
                      </ControlTemplate> 

                   </Setter.Value> 
                </Setter> 
             </Style> 
          </ItemsControl.Style> 

          <ItemsControl.ItemTemplate> 
             <DataTemplate> 
                <Image Source="{Binding XPath=@Image}"/> 
             </DataTemplate> 
          </ItemsControl.ItemTemplate> 

          <ItemsControl.ItemContainerStyle> 
             <Style TargetType="{x:Type ContentControl}"> 
                <Setter Property="Grid.Column" Value="{Binding XPath=@X}"/> 
                <Setter Property="Grid.Row" Value="{Binding XPath=@Y}"/> 
             </Style> 
          </ItemsControl.ItemContainerStyle> 
       </ItemsControl>

    </StackPanel>


    What is the difference between the two alternatives

  • singlark

    I wrote a complete example on how to databind a position on a Canvas (not a Grid). You can find the sample here: http://www.i-constructions.com/myblog/avalonpositiondatabinding.html.
    In your case, since you're using a Grid, you could set the column and row definitions in the ItemsPanelTemplate (in my case, I just set the Height of the Canvas).


  • richardghome

     Christof wrote:

    But is this valid xaml please :

                      <ControlTemplate TargetType="{x:Type ItemsControl}"> 
                         <Grid IsItemsHost="true"> 
                            <RowDefinition/><RowDefinition/> 
                            <ColumnDefinition/><ColumnDefinition/> 
                         </Grid> 

                      </ControlTemplate> 

    I was also struggling with where to set the amount of rows and columns in the grid.  Can you copy paste the source maybe :-)

    I believe that markup is valid. It's what I'm using to set the number of rows and columns. Didn't it work for you

     Christof wrote:

    Is the contentpresenter a real control   Or is it some GUI-less abstract class that accidently can be used for this purpose...

    If I'm not mistaken, ContentPresenter is just an element whose only purpose is displaying the content for a ContentControl. It acts as a placeholder that means "the content for the control goes here". If you look at the CheckBox's template, for instance, you'll see there's a ContentPresenter placed at the point where the checkbox's content should go:

    http://winfx.msdn.microsoft.com/library/default.asp url=/library/en-us/wcp_conceptual/html/bfdaec96-d101-4d3d-864d-c27e6b621d03.asp

    Same for the other ContentControls (e.g. Button).


    As for where I learnt about ContentPresenter and ContentControl, I believe it was mostly from posts on this forum and some Avalon bloggers.

  • MFC

    Could someone be so kind to rewrite this little sample code into something that work with the latest WinFX build please (I believe beta 1 )

    I kept on trying to get this to work but for some reason:
    - columndefinitions and rowdefinitions are not dependencyproperties on a grid and so they are not allowed to be set from within a style
    - also setting the number of rows and colums in a controltemplate does not work

    I think this is a great post as it show how to generate databound items that are positioned automatically in a grid!!  I assume that a lot of people are looking for some non-existent "datagrid" control like they had in classic winforms.  While the way to accomplish it is exactly using the code posted in this sample.

    Thanks for any help! 

    Best regards!
    Christof

  • tom-

    Thanks for your answer so much Javier.  But is this valid xaml please :

                      <ControlTemplate TargetType="{x:Type ItemsControl}"> 
                         <Grid IsItemsHost="true"> 
                            <RowDefinition/><RowDefinition/> 
                            <ColumnDefinition/><ColumnDefinition/> 
                         </Grid> 
                      </ControlTemplate> 

    I was also struggling with where to set the amount of rows and columns in the grid.  Can you copy paste the source maybe :-)

    Is the contentpresenter a real control   Or is it some GUI-less abstract class that accidently can be used for this purpose...

    I would like to understand more of this I guess.  All I have read is the Oreilly book; so any good other resources would be welcome too!

    Best regards and thanks again for your time!
    Christof


  • Beaver01

    The ItemsControl uses an intermediate element as a "wrapper" or "container" for the generated content.  Therefore, the TextBlock with the attached Canvas properties is not seen by the Canvas.  Try changing to the following:

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005">

    <StackPanel.Resources>
        <XmlDataProvider x:Key="dataSource" XPath="/Test">
            <Test xmlns="">
                <Key Name="test"/>
            </Test>
        </XmlDataProvider>

        <DataTemplate x:Key="dataTemplate">
            <TextBlock Text="{Binding XPath=@Name}"/>
        </DataTemplate>

       <Style x:Key="MyContainerStyle">
          <Setter Property="Canvas.Left" Value="20"/>
          <Setter Property="Canvas.Top" Value="20"/>
       </Style>

        <Canvas x:Key="dataCanvas"/>
    </StackPanel.Resources>

    <ItemsControl ItemsSource="{Binding Source={StaticResource dataSource}, XPath=Key}" ItemTemplate="{StaticResource dataTemplate}" ItemsPanel="{StaticResource dataCanvas}" ItemContainerStyle="{StaticResource MyContainerStyle}"/>

    </StackPanel>




  • sirbill

    All I had to do to make it work with the WinFX November CTP build was change the target type of the item container's style from ContentControl to ContentPresenter, like this:

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"> 

        ...

          <ItemsControl.ItemContainerStyle> 
             <Style TargetType="{x:Type ContentPresenter}"> 
                <Setter Property="Grid.Column" Value="{Binding XPath=@X}"/> 
                <Setter Property="Grid.Row" Value="{Binding XPath=@Y}"/> 
             </Style> 
          </ItemsControl.ItemContainerStyle> 
       </ItemsControl>

    </StackPanel>


  • Site Defense

     Christof wrote:
    How did you guys learn about contentpresenter and itemscontrol   Any good references   It seems like they are certainly not encouraged to be used when reading the docs.  Soooo cool I got this to work!

    At this moment, the most up to date information is in the Platform SDK. Chris Sells' book is slightly out of date (e.g. the stuff about navigation is obsolete), and the articles on MSDN are much more out of date. I watched all the PDC2005 WPF presentations, they give a good overview of all the WPF features and they are reasonably up to date (apart from the Nov CTP changes). And I read the blogs of the Avalon bloggers and read this forum.

  • Positioning elements generated by a data template on a Grid or Canvas