Change Grid Coordinate System

Go To StackoverFlow.com

6

The grid in WPF currently has a grid system like this:

    Cols
   +   +   +   +   +
   | 0 | 1 | 2 | 3 | 
+--+---|---|---|---|---
 0 |   |   |   |   |
+--+---|---|---|---|---  Rows
 1 |   |   |   |   |   
+--+---|---|---|---|---
 2 |   |   |   |   | 
+--+---|---|---|---|---

Is there a way to make it behave like this:

    Cols
   +   +   +   +   +
   | 0 | 1 | 2 | 3 | 
+--+---|---|---|---|---
 2 |   |   |   |   |
+--+---|---|---|---|---  Rows
 1 |   |   |   |   |   
+--+---|---|---|---|---
 0 |   |   |   |   | 
+--+---|---|---|---|---

Ideally I would like the RowSpan to extend an item upwards instead of downwards.

Example:

My datasource stores a cube on the map as 0,0 with the intent of it being displayed on the bottom left corner. However the grid in WPF will put that cube on the top left. The other problem is the datasource gives me a 2x2 with the position of the bottom left "anchor" position with a width and height. The width and height are bound to ColSpan and RowSpan. The RowSpan is an issue because it will be expanded down the grid and not up.

2012-04-05 20:29
by Robert
Could you give an example which illustrates why you need that - sll 2012-04-05 20:35
I added an exampl - Robert 2012-04-05 21:54
cool diagram : - Aftnix 2012-04-25 20:09


1

You should be able to do this without having to create a custom or user control by using attached properties.

Here's a class that I think should be able to do what you want. Instead of binding the values of Grid.Row and Grid.RowSpan to your row and height, bind GridEx.RowFromBottom and GridEx.RowSpanFromBottom to them. The property change handlers for these properties will compute the new value of Grid.Row based on the values of those properties and the number of rows in the grid.

One potential problem is that this may not update correctly if you're adding or subtracting rows from the grid at runtime.

public static class GridEx
{
    public static readonly DependencyProperty RowFromBottomProperty = DependencyProperty.RegisterAttached("RowFromBottom", typeof(int?), typeof(GridEx), new FrameworkPropertyMetadata(default(int?), FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure, OnRowFromBottomChanged));
    public static readonly DependencyProperty RowSpanFromBottomProperty = DependencyProperty.RegisterAttached("RowSpanFromBottom", typeof(int?), typeof(GridEx), new FrameworkPropertyMetadata(default(int?), FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure, OnRowSpanFromBottomChanged));

    private static void OnRowFromBottomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var grid = GetContainingGrid(d);
        int? rowFromBottom = (int?) e.NewValue;
        int? rowSpanFromBottom = GetRowSpanFromBottom(d);
        if (rowFromBottom == null || rowSpanFromBottom == null) return;
        int rows = grid.RowDefinitions.Count;
        int row = Math.Max(0, Math.Min(rows, rows - rowFromBottom.Value - rowSpanFromBottom.Value));
        Grid.SetRow((UIElement) d, row);
    }

    private static void OnRowSpanFromBottomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var grid = GetContainingGrid(d);
        int? rowFromBottom = GetRowFromBottom(d);
        int? rowSpanFromBottom = (int?)e.NewValue;
        if (rowFromBottom == null || rowSpanFromBottom == null) return;
        int rows = grid.RowDefinitions.Count;
        int row = Math.Max(0, Math.Min(rows, rows - rowFromBottom.Value - rowSpanFromBottom.Value));
        Grid.SetRow((UIElement)d, row);
        Grid.SetRowSpan((UIElement)d, rowSpanFromBottom.Value);
    }

    public static int? GetRowFromBottom(DependencyObject obj)
    {
        return (int?) obj.GetValue(RowFromBottomProperty);
    }

    public static void SetRowFromBottom(DependencyObject obj, int? value)
    {
        obj.SetValue(RowFromBottomProperty, value);
    }

    public static int? GetRowSpanFromBottom(DependencyObject obj)
    {
        return (int?)obj.GetValue(RowSpanFromBottomProperty);
    }

    public static void SetRowSpanFromBottom(DependencyObject obj, int? value)
    {
        obj.SetValue(RowSpanFromBottomProperty, value);
    }

    private static Grid GetContainingGrid(DependencyObject element)
    {
        Grid grid = null;
        while (grid == null && element != null)
        {
            element = LogicalTreeHelper.GetParent(element);
            grid = element as Grid;
        }
        return grid;
    }
}

If you have any questions about what's going on here, feel free to ask.

2012-04-26 03:58
by Michael Davis


1

You could achieve this by writing your own custom control. You could inherit from Grid, or alternatively, use a UserControl with a Grid on it. Either way, you would provide the attached properties similarly to a Grid, and you could then manipulate the values as you see fit, and then pass them on to the underlying Grid.

2012-04-05 22:01
by Kendall Frey
Could you be more specific about creating a custom control that inherits from Grid? For example what methods to override, it's fairly confusing - Robert 2012-04-06 16:35
I don't think it should be a big problem. You should research how to implement attached properties, since that is how Panels perform layout. If you run into specific problems, feel free to ask another question on SO - Kendall Frey 2012-04-07 02:55


1

Is the Grid where you're displaying the cubes a fixed size? If so, you could consider writing a ViewModel that converts/reverses the model coordinates in order to work in the view, i.e., the cube would have have the value (0,0) and the ViewModel would expose that value as (0,2).

Just an idea that may be easier than rolling your own control.

2012-04-05 23:48
by sellmeadog


1

Try this converter. XAML look a little bit complicated, but no ViewModel(s) or UserControl(s) are required:

Converter to flip rows:

public class UpsideDownRowConverter : IMultiValueConverter
{
    public int RowCount
    {
        get;
        set;
    }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length == 2 && values[0] is int && values[1] is int)
        {
            var row = (int)values[0];
            var rowSpan = (int)values[1];

            row = this.RowCount - row - rowSpan;

            return row;
        }

        return DependencyProperty.UnsetValue;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

XAML. The first Grid is original, the second is flipped:

<Window.Resources>
    <local:UpsideDownRowConverter x:Key="UpsideDownRowConverter"
                                    RowCount="3"/>
</Window.Resources>
<UniformGrid Rows="2">
    <Grid Name="Original"
            Margin="0,0,0,10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Rectangle Fill="Green"
                    Grid.Column="0"
                    Grid.Row="2"/>
        <Rectangle Fill="Red"
                    Grid.Column="1"
                    Grid.Row="1"/>
        <Rectangle Fill="Blue"
                    Grid.Column="2"
                    Grid.RowSpan="3"/>
        <Rectangle Fill="Yellow"
                    Grid.Column="3"
                    Grid.RowSpan="2"/>
    </Grid>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Rectangle Fill="Green"
                    Grid.Column="0">
            <Grid.Row>
                <MultiBinding Converter="{StaticResource UpsideDownRowConverter}">
                    <Binding Path="Children[0].(Grid.Row)"
                                ElementName="Original"/>
                    <Binding Path="(Grid.RowSpan)"
                                RelativeSource="{RelativeSource Self}"/>
                </MultiBinding>
            </Grid.Row>
        </Rectangle>
        <Rectangle Fill="Red"
                    Grid.Column="1">
            <Grid.Row>
                <MultiBinding Converter="{StaticResource UpsideDownRowConverter}">
                    <Binding Path="Children[1].(Grid.Row)"
                                ElementName="Original"/>
                    <Binding Path="(Grid.RowSpan)"
                                RelativeSource="{RelativeSource Self}"/>
                </MultiBinding>
            </Grid.Row>
        </Rectangle>
        <Rectangle Fill="Blue"
                    Grid.Column="2"
                    Grid.RowSpan="3">
            <Grid.Row>
                <MultiBinding Converter="{StaticResource UpsideDownRowConverter}">
                    <Binding Path="Children[2].(Grid.Row)"
                                ElementName="Original"/>
                    <Binding Path="(Grid.RowSpan)"
                                RelativeSource="{RelativeSource Self}"/>
                </MultiBinding>
            </Grid.Row>
        </Rectangle>
        <Rectangle Fill="Yellow"
                    Grid.Column="3"
                    Grid.RowSpan="2">
            <Grid.Row>
                <MultiBinding Converter="{StaticResource UpsideDownRowConverter}">
                    <Binding Path="Children[3].(Grid.Row)"
                                ElementName="Original"/>
                    <Binding Path="(Grid.RowSpan)"
                                RelativeSource="{RelativeSource Self}"/>
                </MultiBinding>
            </Grid.Row>
        </Rectangle>
    </Grid>
</UniformGrid>

Unfortunately, it is unable to pass row count as a third value, because RowDefinitionCollection doesn't notify about changes. That's why I added RowCount as property of converter.

2012-04-26 11:13
by Marat Khasanov


0

You can convert the row to reverse format as below:

    private void ReverseRow(Grid grd)
    {
        int totalRows = grd.RowDefinitions.Count-1;
        foreach (UIElement ctl in grd.Children)
        {
            int currentRowIndex = Grid.GetRow(ctl);
            Grid.SetRow(ctl, totalRows - currentRowIndex);
        }
    }

This will revert the row.

2012-05-01 06:05
by Naresh Goradara
Ads