tags:

views:

19

answers:

1

Just to give you a quick overview, just trying to create somewhat of a tile editor. So I made custom tile objects which will be represted through a contenttemplate displaying each as a rectangle. I am using a listbox as my container but I set the ItemsPanelTemplate of that container to use a grid. The problem is, setting Grid.Row or Grid.Column in my contenttemplate does nothing. I'm sure it has something to do with the fact that my grid is defined within the template but I'm not sure how.

Here is my XAML:

<Window x:Class="InvisTile.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CustomControls"
    Title="MainWindow" Height="200" Width="200">

<Window.Resources>
    <ControlTemplate x:Key="TileTemplate" TargetType="{x:Type ListBoxItem}">

        //Hard coded to grid location but only staying in 0,0
        <local:Tile BorderBrush="Aqua" MouseDown="Tile_MouseDoubleClick" Grid.Row="1" Grid.Column="1">
            <Rectangle Fill="Transparent" Stroke="Green"></Rectangle>
        </local:Tile>
    </ControlTemplate>
</Window.Resources>
<ListBox Name="lstTiles">
    <ListBox.ItemContainerStyle>
        <Style>
            <Setter Property="Control.Template" Value="{StaticResource TileTemplate}" />
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid ShowGridLines="True">
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>
            </Grid>
        </ItemsPanelTemplate>          
    </ListBox.ItemsPanel>     
   <local:Tile></local:Tile> 
</ListBox>  

+1  A: 

The reason is that Grid.Row and Grid.Column only have effect on the direct children of the Grid control. The children of your Grid, however, are not the Tile controls, but the ListBoxItem controls that are created implicitly. Therefore, you must set the position like this:

ListBoxItem li = new ListBoxItem();
li.Content = "item1";
Grid.SetColumn(li, 1);
Grid.SetRow(li, 1);
lstTiles.Items.Add(li);

You also can do it like this in the ItemContainerStyle:

<Setter Property="Grid.Row" Value="1"/>
<Setter Property="Grid.Column" Value="1"/> 

You even can use data binding:

<Setter Property="Grid.Row" Value="{Binding RelativeSource={x:Static RelativeSource.Self},
    Path=Content.Row}"/>
<Setter Property="Grid.Column" Value="{Binding RelativeSource={x:Static RelativeSource.Self},
    Path=Content.Column}"/> 

Here, Row and Column must be public properties of the ListBox items.

Here is a complete example to make it clear:

Screenshot

<Window x:Class="InvisTile.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:InvisTile"
    Title="Window1" Height="200" Width="200">

    <Window.Resources>
        <ControlTemplate x:Key="TileTemplate" TargetType="{x:Type ListBoxItem}">
            <Border BorderThickness="1" BorderBrush="Aqua" Background="LightGray" Margin="4">
                <Grid  VerticalAlignment="Center" HorizontalAlignment="Center">
                    <ContentPresenter/>
                </Grid>
            </Border>
        </ControlTemplate>
    </Window.Resources>
    <ListBox Name="lstTiles">
        <ListBox.ItemContainerStyle>
            <Style>
                <Setter Property="Control.Template" Value="{StaticResource TileTemplate}" />
                <Setter Property="Grid.Row" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, 
    Path=Content.Row}"/>
                <Setter Property="Grid.Column" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, 
    Path=Content.Column}"/>
            </Style>
        </ListBox.ItemContainerStyle>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid ShowGridLines="True">
                    <Grid.RowDefinitions>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition></ColumnDefinition>
                        <ColumnDefinition></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                </Grid>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.Items>
            <local:Tile Text="(0,0)" Row="0" Column="0"/>
            <local:Tile Text="(0,1)" Row="0" Column="1"/>
            <local:Tile Text="(1,0)" Row="1" Column="0"/>
            <local:Tile Text="(1,1)" Row="1" Column="1"/>
        </ListBox.Items>
    </ListBox>
</Window>

The Tile class is defined as follows:

public class Tile
{
    public Tile() {}
    public string Text { get; set; }
    public int Row { get; set; }
    public int Column { get; set; }
    public override string ToString() { return Text; }
}
fmunkert
Let me expand one thing. Would it have been better if I setup the contenttemplate of the listbox item with a contentproperty and the in that contentproperty I would then put my tiling object? I hope that clarifies what I was trying to ask before.
Ilya
One more thing. When I made a contenttemplate, I made the template replace each ListboxItem with a tile object and a rectangle within. I don't have a Content property in the template so why does Content.Row work here? It's not like I put a tile in the content property...the item itself is the tile. So, shouldn't it just be "row" and not "Content.Row"?
Ilya
Please see my edits above.
fmunkert
Cool. I saw your edits and understand completely why they work the way you structured it by peicing everything out individually. In mine though, I don't put a contentpresenter element and still somehow the visual tree looks like it places my tile element into a content property that I never specified. I'm still confused in this regard. Is a listboxitem implicitly wrapped around my item in my code at compile time?
Ilya
The `Content` property and the wrapping `ListBoxItem` are generated automatically by WPF.
fmunkert