tags:

views:

166

answers:

2

I'm trying to make a grid based on a UniformGrid to show the coordinates of each cell, and I want to show the values on the X and Y axes like so:

   _A_ _B_ _C_ _D_
1 |___|___|___|___|
2 |___|___|___|___|
3 |___|___|___|___|
4 |___|___|___|___|

Anyway, in order to do that I need to know the number of columns and rows in the Uniform grid, and I tried overriding the 3 most basic methods where the arrangement / drawing happens, but the columns and rows in there are 0, even though I have some controls in my grid. What method can I override so my Cartesian grid knows how many columns and rows it has?

C#:

public class CartesianGrid : UniformGrid
{
    protected override Size MeasureOverride(Size constraint)
    {
        Size size = base.MeasureOverride(constraint);

        int computedColumns = this.Columns; // always 0
        int computedRows = this.Rows; // always 0

        return size;
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        Size size = base.ArrangeOverride(arrangeSize);

        int computedColumns = this.Columns; // always 0
        int computedRows = this.Rows; // always 0

        return size;
    }

    protected override void OnRender(DrawingContext dc)
    {`enter code here`
        int computedColumns = this.Columns; // always 0
        int computedRows = this.Rows; // always 0

        base.OnRender(dc);
    }
}

XAML:

<local:CartesianGrid>
    <Label Content="Hello" />
    <Label Content="Hello" />
    <Label Content="Hello" />
    <Label Content="Hello" />
    <Label Content="Hello" />
    <Label Content="Hello" />
</local:CartesianGrid>

Any help is greatly appreciated. Thanks!

+2  A: 

There is no property exposed that gives you that information. If you have reflector you can see the calculation they do in the UpdateComputedValues method to find the number of rows and columns they use.

In the case where Rows, Columns, and FirstColumn are zero you can use something like this

int numVisibleChildren = this.Children.Count((c) => c.Visibility != Visibility.Collapsed);
int numColumns = (int)Math.Ceiling(Math.Sqrt(numVisibleChildren));
int numRows = (int)Math.Floor(Math.Sqrt(numVisibleChildren));

I haven't actually run the code though so there are probably some typos.

Bryan Anderson
Wow your solution is really simple, only I had to change numRows to use Math.Ceiling too. I came up with another solution myself, but I like yours a lot more (posted my solution in the question). Thanks!
Carlo
Sorry, but your solution didn't work, although is much simpler. In our case the number of columns and number of rows are not the same. So I had to use my solution to calculate the real number of columns and rows, without assuming those numbers are the same. Thanks!
Carlo
Actually nvm, it was easier from the beginning, in our project the columns and rows are specifically set through a DependencyProperty, your solution should work fine when the Columns and Rows are NOT set, which is what I was aiming for at first. Thanks!
Carlo
+1  A: 

Here's the complete solution, handles specifying or not specifying the Columns and Rows of the UniformGrid:

public class CartesianGrid : UniformGrid
{
    private int _columns;
    private int _rows;
    private int _margin = 20;

    public CartesianGrid()
    {
        // add some margin so the letters and numbers do show up
        this.Margin = new Thickness(_margin, _margin, 0, 0);
    }

    protected override void OnRender(DrawingContext dc)
    {
        double xOffset = (this.RenderSize.Width / _columns);
        double yOffset = (this.RenderSize.Height / _rows);

        double xCenterOffset = xOffset / 2;
        double yCenterOffset = yOffset / 2.3;

        for (int i = 0; i < _columns; i++)
        {
            dc.DrawText(
                new FormattedText((i + 1).ToString(),
                CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface("Arial"),
                20,
                Brushes.Black), new Point((i * xOffset) + xCenterOffset, _margin * -1));
        }

        for (int i = 0; i < _rows; i++)
        {
            dc.DrawText(
                new FormattedText(((char)(i + 65)).ToString(),
                CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface("Arial"),
                20,
                Brushes.Black), new Point(_margin * -1, (i * yOffset) + yCenterOffset));
        }

        base.OnRender(dc);
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        if (this.Columns != 0 && this.Rows != 0)
        {
            _rows = this.Rows;
            _columns = this.Columns;

            return base.ArrangeOverride(arrangeSize);
        }
        else
        {
            Size arrangedSize = base.ArrangeOverride(arrangeSize);

            double maxChildDesiredWidth = 0.0;

            double maxChildDesiredHeight = 0.0;

            //  Measure each child, keeping track of max desired width & height.  
            for (int i = 0, count = Children.Count; i < count; ++i)
            {
                UIElement child = Children[i];

                Size childDesiredSize = child.DesiredSize;

                if (maxChildDesiredWidth < childDesiredSize.Width)
                {
                    maxChildDesiredWidth = childDesiredSize.Width;
                }
                if (maxChildDesiredHeight < childDesiredSize.Height)
                {
                    maxChildDesiredHeight = childDesiredSize.Height;
                }
            }

            if (maxChildDesiredHeight == 0 || maxChildDesiredWidth == 0)
                return arrangedSize;

            _columns = Convert.ToInt32(Math.Floor(this.DesiredSize.Width / maxChildDesiredWidth));
            _rows = Convert.ToInt32(Math.Floor(this.DesiredSize.Height / maxChildDesiredHeight));

            return arrangedSize;
        }
    }
}

Btw, this is what I wanted to achieve:

http://wpfdude.blogspot.com/2010/06/cartesian-grid.html

Thanks!

Carlo