14

Suppose that you have 2 or more grids that share the same structure (Rows/Columns count and properties are equal).

Is it possible to create a Style and set RowDefinitions/ColumnDefinitions?

I have tried that. But I get this exception when the application tries to parse the xaml:

Xamarin.Forms.Xaml.XamlParseException: Position 14:9. Property Value is null or is not IEnumerable

My Source code:

<StackLayout>
    <StackLayout.Resources>
        <ResourceDictionary>
            <Style TargetType="Grid">
                <Setter Property="RowDefinitions">
                    <Setter.Value>
                        <RowDefinition />
                        <RowDefinition />
                    </Setter.Value>
                </Setter>
            </Style>
        </ResourceDictionary>
    </StackLayout.Resources>

    <Grid>
        <Label Grid.Row="0" Text="(Grid #1) Row #1" />
        <Label Grid.Row="1" Text="(Grid #1) Row #2" />
    </Grid>

    <Grid>
        <Label Grid.Row="0" Text="(Grid #2) Row #1" />
        <Label Grid.Row="1" Text="(Grid #2) Row #2" />
    </Grid>

    <Grid>
        <Label Grid.Row="0" Text="(Grid #3) Row #1" />
        <Label Grid.Row="1" Text="(Grid #3) Row #2" />
    </Grid>
</StackLayout>

Position 14:9 in the exception refers to the first <RowDefinition /> tag.

j.f.
  • 3,908
  • 2
  • 29
  • 42
Hadi Fooladi Talari
  • 1,180
  • 1
  • 13
  • 36

5 Answers5

20

You can't define RowDefinition and ColumnDefinition within a style, but you can create them as reusable resources. I think this is what you're looking for. Defining a ColumnDefinitionCollection and a RowDefinitionCollection like so allows you to reuse them with many grids to create a consistent look.

<ResourceDictionary>
    <ColumnDefinitionCollection
        x:Key="MyColumnDefinitionCollection">
        <ColumnDefinition
            Width="50" />
        <ColumnDefinition
            Width="100" />
        <ColumnDefinition
            Width="150" />
    </ColumnDefinitionCollection>
    <RowDefinitionCollection
        x:Key="MyRowDefinitionCollection">
        <RowDefinition
            Height="50" />
        <RowDefinition
            Height="50" />
        <RowDefinition
            Height="100" />
        <RowDefinition
            Height="100" />
    </RowDefinitionCollection>
</ResourceDictionary>
<Grid 
    ColumnDefinitions="{StaticResource MyColumnDefinitionCollection}"
    RowDefinitions="{StaticResource MyRowDefinitionCollection}">
</Grid>
j.f.
  • 3,908
  • 2
  • 29
  • 42
  • Very good suggestion from @jf Alternatively you can create a class derived from Grid, set everything up and then use it everywhere. Well, I will put that as answer too :-) – Yuri S Aug 14 '17 at 18:57
  • Great answer! I was struggling making two columns of slightly different width in Android and Windows and this answer gave me the solution. Just defined the resource with and worked perfectly! – Francesco Blue Jan 24 '18 at 19:01
  • 2
    Doesn't seem to work for me, I get the errors: `The type "ColumnDefinitionCollection" does not include any accessible constructors` and `The property "ColumnDefinitions" does not have an accessible setter.` – Alfie Mar 13 '19 at 09:39
  • @Alfie, I just tried this again and it still seems to work just fine for me. I suggest creating another question with steps on how to reproduce your issue. – j.f. Mar 13 '19 at 13:32
11

I found a better and simpler solution. The answer was just adding RowDefinitionCollection tag around the RowDefinition tags.

like this:

<StackLayout>
    <StackLayout.Resources>
        <ResourceDictionary>
            <Style TargetType="Grid">
                <Setter Property="RowDefinitions">
                    <Setter.Value>
                        <RowDefinitionCollection>
                            <RowDefinition />
                            <RowDefinition />
                        </RowDefinitionCollection>
                    </Setter.Value>
                </Setter>
            </Style>
        </ResourceDictionary>
    </StackLayout.Resources>

    <Grid>
        <Label Grid.Row="0" Text="(Grid #1) Row #1" />
        <Label Grid.Row="1" Text="(Grid #1) Row #2" />
    </Grid>

    <Grid>
        <Label Grid.Row="0" Text="(Grid #2) Row #1" />
        <Label Grid.Row="1" Text="(Grid #2) Row #2" />
    </Grid>

    <Grid>
        <Label Grid.Row="0" Text="(Grid #3) Row #1" />
        <Label Grid.Row="1" Text="(Grid #3) Row #2" />
    </Grid>
</StackLayout>
Hadi Fooladi Talari
  • 1,180
  • 1
  • 13
  • 36
7

Thanks to @j.f., I found the solution. I combined his idea with mine and here is the result:

The trick was adding the declaration into a separate resource and referencing it inside style.

<StackLayout>
    <StackLayout.Resources>
        <ResourceDictionary>
            <RowDefinitionCollection x:Key="MyRowDefinitionCollection">
                <RowDefinition />
                <RowDefinition />
            </RowDefinitionCollection>

            <Style TargetType="Grid">
                <Setter Property="RowDefinitions" Value="{StaticResource MyRowDefinitionCollection}" />
            </Style>
        </ResourceDictionary>
    </StackLayout.Resources>

    <Grid>
        <Label Grid.Row="0" Text="(Grid #1) Row #1" />
        <Label Grid.Row="1" Text="(Grid #1) Row #2" />
    </Grid>

    <Grid>
        <Label Grid.Row="0" Text="(Grid #2) Row #1" />
        <Label Grid.Row="1" Text="(Grid #2) Row #2" />
    </Grid>

    <Grid>
        <Label Grid.Row="0" Text="(Grid #3) Row #1" />
        <Label Grid.Row="1" Text="(Grid #3) Row #2" />
    </Grid>
</StackLayout>
Hadi Fooladi Talari
  • 1,180
  • 1
  • 13
  • 36
3

You can create a class derived from Grid

public class MyGrid : Grid

set it up and then use everywhere in xamls as

<local:MyGrid> 

As j.f mentioned, it is called user control

Yuri S
  • 5,355
  • 1
  • 15
  • 23
0

Nou you can't and in my opinion you shouldn't.

Defining a Grid isn't about styling, it's about layout.

From Xamarin.Forms version 3, there will be an introduction of more CSS-like styling capabilities. Maybe you then will be able to style a grid some more like you would in HTML/CSS. But not much is known about what and how will be implemented.

Gerald Versluis
  • 30,492
  • 6
  • 73
  • 100
  • Hi Gerald Versluis, Thanks for quick reply. But it would be nice if it was possible. Because like a method you can change the code in one place and it would effect in multiple places. That's my opinion. Maybe creating a custom control will solve this. – Hadi Fooladi Talari Aug 14 '17 at 18:35