Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change Grid Coordinate System

Tags:

c#

wpf

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.

like image 673
Robert Avatar asked Apr 05 '12 20:04

Robert


2 Answers

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.

like image 114
Michael Davis Avatar answered Oct 24 '22 10:10

Michael Davis


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.

like image 33
Kendall Frey Avatar answered Oct 24 '22 12:10

Kendall Frey