Tuesday, February 15, 2011

Selecting the next editable cell in a Datagridview

One aspect of DataGridViews that you can often find being discussed in a healthy and vigorous fashion on the internet is read-only cells, specifically the lack of TabStop = false functionality. It's very common to see dev's trying to force the selected cell to change when a Read-only one is focused and then they post on a board somewhere about a Reentrant Exception from SetCurrentCellAddressCore, the following code will stop the exception happening and find the next available cell (or control):

        //a delegate is needed to avoid a circular loop when selecting a cell when in a cell selection event
        private delegate void SetColumnAndRowOnGrid(DataGridView grid, int i, int o);

        //can this cell be entered?
        //***********************************************
        //The Cell Enter Event Handler - First step in the code flow
        //***********************************************
        private void grid_CellEnter(object sender, DataGridViewCellEventArgs e)
        {
            //typesafe check
            if (sender is DataGridView)
            {
                DataGridView grid = (DataGridView)sender;
                if (grid.Rows[e.RowIndex].Cells[e.ColumnIndex].ReadOnly)
                {
                    //this cell is readonly, find the next tabable cell
                    if (!setNextTabableCell(grid, e.ColumnIndex, e.RowIndex))
                    {
                        //or tab to the next control
                        setNextTabableControl();
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("this method can only be applied to controls of type DataGridView");
            }
        }


        //***********************************************
        //Find the next cell that we want to be selectable
        //***********************************************
        private bool setNextTabableCell(DataGridView grid, int nextColumn, int nextRow)
        {
            //keep selecting each next cell until one is found that isn't either readonly or invisable
            do
            {
                //at the last column, move down a row and go the the first column
                if (nextColumn == grid.Columns.Count - 1)
                {
                    nextColumn = 0;
                    //at the last row and last column exit this method, no cell can be selected
                    if (nextRow == grid.Rows.Count - 1)
                    {
                        return false;
                    }
                    else
                    {
                        nextRow = Math.Min(grid.Rows.Count - 1, nextRow + 1);
                    }
                }
                else
                {
                    nextColumn = Math.Min(grid.Columns.Count - 1, nextColumn + 1);
                }
            }
            while (grid.Rows[nextRow].Cells[nextColumn].ReadOnly == true ||
                        grid.Rows[nextRow].Cells[nextColumn].Visible == false);

            //a cell has been found that can be entered, use the delegate to select it
            SetColumnAndRowOnGrid method = new SetColumnAndRowOnGrid(setGridCell);
            grid.BeginInvoke(method, grid, nextColumn, nextRow);
            //that's all I have to say about that
            return true;
        }

        //***********************************************
        // Method pointed to by the delegate
        //***********************************************
        private void setGridCell(DataGridView grid, int columnIndex, int rowIndex)
        {
            grid.CurrentCell = grid.Rows[rowIndex].Cells[columnIndex];
            grid.BeginEdit(true);
        }

        //***********************************************
        // All the cells in the grid have been tried, none could be tabbed
        // to so move onto the next control
        //***********************************************
        private void setNextTabableControl()
        {
            this.SelectNextControl(this, true, true, true, true);
        }

Datagridview Validating Event Handler

Overwriting the value of a datagridview cell on validating, seems impossible, all the ways you can get to the value of the cell tell you they are Read-Only, the key is the EditingControl Property:

 private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) {
  DataGridViewCell cell =
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex];

  if (cell.IsInEditMode)
  {
    Control c = dataGridView1.EditingControl;
    c.Text = CleanInputNumber(c.Text);
  }
}

Thursday, January 13, 2011

Changing the Namespace of an element without altering the namespace of child elements

When tranforming xml there is occasionally a need to alter the namespace of a parent element without changing the namepace of the child element e.g.

<p781:parent xmlns:p781="http://v1.parent.codemumbler.co.uk/" xmlns:p463="http://v1.child.codemumbler.co.uk/">
    <p463:childNumber>12345</p463:childNumber>
    <p463:childDate>2010-12-24</p463:childDate>
</p781:parent>

note the parent elemnt has two namespace declaration attributes, one for p781 that it uses itself and one for p463 that is used by its child elements.

Now, there is no mechanism in XSLT to copy a node and change its namespace on the fly. As far as xml is concerned the namespace of an element is one of its fundemantal identifying parts and should not be messed with (which may make you wonder why I am doing it, but I’m a well behaved coder and do what I’m told. By which I mean it was a case of JFDI). One way around this is to create a new element with the same name and set the namespace to be the one I want. e.g.

<!--The parent element has to be moved from the parent namespace to the newParent namespace-->
<xsl:template match="//*[local-name()='parent' and namespace-uri()='http://v1.parent.codemumbler.co.uk']">
    <!--Specify the QName for the new parent element-->
    <xsl:element name="newParent:parent">
        <!--now carry on down the rest of the structure-->
        <xsl:apply-templates select="@*|*|node()"/>

The newParent alias will already of been set in the xslt - I'll show this in the final example containing the whole transform.

There is a problem now though, the namespace declaration for p463 is missing, you can leave it out altogether, the transform will cope with it and add the declaration to the child node but it will be very very messy. Another option is to iterate through the namespace declations in the original element, find the one for the namespace we want to include and take it over WITH its original alias, this will result in the output xml looking almost as nice when it comes out of the transform as when it went in. e.g.

<!--The existing structure has a namespace declaration that is not copied over with the attributes, so it has to be added manually-->
<xsl:for-each select="namespace::node()">
    <xsl:choose>
        <!--specific namespace-->
        <xsl:when test=".= 'http://v1.child.codemumbler.co.uk'">
            <!--This will add a namespace declation to the new element but preserve the prefix of the declaration from the source element-->
            <xsl:namespace name="{name(.)}">
                <xsl:value-of select="."/>
            </xsl:namespace>
        </xsl:when>
    </xsl:choose>
</xsl:for-each>


And done :-/

The output of the transform is:


<newParent:parent xmlns:p463="http://v1.newparent.codemumbler.co.uk/">
    <p463:childNumber xmlns:p781="http://v1.parent.codemumbler.co.uk">12345</p463:childNumber>
    <p463:childDate xmlns:p781="http://v1.parent.codemumbler.co.uk">2010-12-24</p463:childDate>


I know that the old namespace is still being carried down into the child elemnts, but the whole thing is now compliant with the output schema I am using and as I stated at the start - transforms are not designed to play about with namespaces. If you wanted the child elements to be totally tidy you could add the original namespace back in at the parent level or rebuild the entire structure.

Entire transform:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:newParent=http://v1.newParent.codemumbler.co.uk>
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <!--copy every element unless a template match is specified lower down-->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <!--The parent element has to be moved from the parent namespace to the newParent namespace-->
    <xsl:template match="//*[local-name()='parent' and namespace-uri()='http://v1.parent.codemumbler.co.uk']">
        <!--Specify the QName for the new parent element-->
        <xsl:element name="newParent:parent">
            <!--The existing structure has a namespace declaration  that is not copied over with the attributes, so it has to be added manually-->
            <xsl:for-each select="namespace::node()">
                <xsl:choose>
                    <!--specific namespace-->
                    <xsl:when test=".= 'http://v1.child.codemumbler.co.uk'">
                        <!--This will add a namespace declation to the new element but preserve the prefix of the declaration from the source element-->
                        <xsl:namespace name="{name(.)}">
                            <xsl:value-of select="."/>
                        </xsl:namespace>
                    </xsl:when>
                </xsl:choose>
            </xsl:for-each>
            <!--now carry on down the rest of the structure-->
            <xsl:apply-templates select="@*|*|node()"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>