VariableSizedWrapGrid inside an ItemsControl with ColumnSpan and RowSpan

Recently, I used an ItemsControl control to my XAML page and wanted to use a VariableSizedWrapGrid as the container. The idea was to show the first item in my collection as large (2×2 in the grid) and the rest of the items to be normally sized.

The effect I wanted was essentially this:

image

 

So what’s the problem?

The problem is that binding ColumnSpan and RowSpan on a DataTemplate of an ItemsControl simply does not work – the issue is that when the visual tree is built, ItemsControl builds a wrapper for each DataTemplate and since VariableSizedWrapGrid needs the elements specifying ColumnSpan and RowSpan to be direct descendants, it just doesn’t work.

So what’s the solution?

We will create a derived class from ItemsControl (that is built to specifically work with VariableSizedWrapGrid). First, we add dependency properties for the converters that will be used to determine the row/col span:

public static readonly DependencyProperty ColumnSpanConverterProperty =
        DependencyProperty.Register("ColumnSpanConverter", typeof(IValueConverter), typeof(VariableSizedWrapGridItemsControl), new PropertyMetadata(null));
public static readonly DependencyProperty RowSpanConverterProperty =
        DependencyProperty.Register("RowSpanConverter", typeof(IValueConverter), typeof(VariableSizedWrapGridItemsControl), new PropertyMetadata(null));

public VariableSizedWrapGridItemsControl()
{

}

public IValueConverter ColumnSpanConverter
{
    get { return (IValueConverter)GetValue(ColumnSpanConverterProperty); }
    set { SetValue(ColumnSpanConverterProperty, value); }
}

public IValueConverter RowSpanConverter
{
    get { return (IValueConverter)GetValue(RowSpanConverterProperty); }
    set { SetValue(RowSpanConverterProperty, value); }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Then, PrepareContainerForItemOverride() needs to be overridden so that we can use the converters – the item will be passed into the converter and it will decide what the size is:

protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
        int? columnSpan = GetIntFromConverter(ColumnSpanConverter, item);
        if (columnSpan.HasValue)
        {
            VariableSizedWrapGrid.SetColumnSpan((UIElement)element, columnSpan.Value);
        }

        int? rowSpan = GetIntFromConverter(RowSpanConverter, item);
        if (rowSpan.HasValue)
        {
            VariableSizedWrapGrid.SetRowSpan((UIElement)element, rowSpan.Value);
        }

    base.PrepareContainerForItemOverride(element, item);
}

For our converter, we simply check if the element is the first one in the list and we return 2 in that case:

public object Convert(object value, Type targetType, object parameter, string language)
{
int result = 1;
if (value == Collection.OfType<object>().FirstOrDefault())
{
result = 2;
}

return result;
}

 

Note that Collection is an IEnumerable dependency property that can be bound to.

Finally, here’s the XAML used. First, defining the converter as a resource:

<common:LayoutAwarePage.Resources>
<local:FirstItemBigConverterBig x:Key="bigConverter" Collection="{Binding Items}">
</local:FirstItemBigConverterBig>
</common:LayoutAwarePage.Resources>

And the actual ItemsControl derived class with the converter set in the correct properties:

<local:VariableSizedWrapGridItemsControl x:Name="itemsControl" ItemsSource="{Binding Items}" RowSpanConverter="{StaticResource bigConverter}"  ColumnSpanConverter="{StaticResource bigConverter}">
<local:VariableSizedWrapGridItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical" ItemWidth="160" ItemHeight="160"/>
</ItemsPanelTemplate>
</local:VariableSizedWrapGridItemsControl.ItemsPanel>
<local:VariableSizedWrapGridItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="4" BorderBrush="Pink" Margin="0,0,12,12">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding}" FontSize="50"/>
</Border>
</DataTemplate>
</local:VariableSizedWrapGridItemsControl.ItemTemplate>
</local:VariableSizedWrapGridItemsControl>

 

And that’s it! You can download the full source code from here.

Advertisements
This entry was posted in Dev, Windows8. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s