Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Enhanced DataSet Quick Watch

0.00/5 (No votes)
21 Feb 2005 1  
A VS.NET add-in to visualise standard and typed DataSets, DataTables and DataRows whilst debugging.

Sample Image - DSWatchEx1.png

Introduction

This add-in is essentially an update to Mohammed Barqawi's excellent DataSet Quick Watch add-in. Please reference the article for the original concept and code.

The add-in's implementation was great but notably didn't support typed datasets. I've added a few things to take the original ideas a little further and hopefully make life even easier to visualise a dataset whilst debugging.

What's New?

Typed DataSet support

This allows all Typed DataSets (directly inherited from System.Data.DataSet) to be used with the add-in. This also works with typed DataTables and DataRows.

Support for DataTables and DataRows

This allows you to select a table or row (in code) and load the dataset (selecting that table or row in the output).

Row Filter Support

A free text row filter and 'DataViewRowState' filter has also been added to help with debugging. This functions on a per table basis.

Visual Enhancements

The DataSet Watch form has been amended to use a custom 'EnhancedGrid' control to provide filtering and a slightly more 'colourful' output. If you're not keen on the look of the grid then you can easily change it by changing the EnhancedGrid UserControl.

I didn't quite get round to adding the support for user preferences!

Using the code

Debugger Expressions

The original concept still stands, but I've made a few amendments to the way we get and check debugger expressions. I refactored the construction of each expression to work based on the input language:

    private string GetEvaluationExpression(string type, object selectedObject, 
                    string propOrMethod, string SourceType)
    {
        switch (SourceType)
        {
            case LangCSharp:
            {
                return "(" + type + ")" + selectedObject + propOrMethod;
            }
            case LangVB:
            {
                if (type == "int")
                    type = "System.Int32";
                    //HACK to cope with language differences

                return "ctype(" + selectedObject + ", " 
                                + type + ")" + propOrMethod;
            }
            default :
            {
                throw new ApplicationException("Invalid Source Type : " 
                                            + "Expected 'cs' or 'vb'");
            }
        }
    }

Where str (the selected text in the debugger) == "myDataTable", the line below will assign a language-specific debugger expression string to getTableNameFromTableExpression:

getTableNameFromTableExpression = 
   GetEvaluationExpression(TypeDataTable, str, ".TableName", fileExtension);
  • C# - System.Data.DataTable)myDataTable.TableName
  • VB - ctype(System.Data.DataTable, myDataTable).TableName

This particular expression is used to get the tablename from a selected DataTable.

Typed DataSets

The support for typed datasets comes in when we evaluate an expression that returns an unexpected type. This will happen because the debugger returns the 'actual' type of the expression you've selected - e.g., MyNamespace.MyTypedDataSet.MyTableRow.

If you selected something other than a DataSet, DataTable or DataRow (or anything that derives directly from any of these), the ExpressionHasError method would find the text "error:" in the expression's value.

In the case we return a typed data object (and we return the type name), we then find the base type and evaluate the expression again... If it's a typed dataset, table or row, then we should find the correct (System.Data...) type second time around.

    // Calling code.....  Filtering down -

    // checking for DataSet, then DataTable, then DataRow


    //Check which worked!

    if (ExpressionHasError(exprDataSet, "{System.Data.DataSet}", str))
    {
        //Check for DataTable

        if (ExpressionHasError(exprDataTable, "{System.Data.DataTable}", str))
        {
            //Check for DataRow

            if (ExpressionHasError(exprDataRow, "{System.Data.DataRow}", str))
            {
                MessageBox.Show("This is not a DataSet, DataTable or DataRow!",
            "DSWatch",MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }


    //.....


    private bool ExpressionHasError(Expression expr, 
                 string type, string originalContext)
    {
        EnvDTE.Debugger debugger = applicationObject.Debugger;

        if (type != null)
            if (expr.Value.IndexOf("error:")>-1)
                return true;
            else
            {
                if (expr.Value != type)
                {
                    //Check for base type (only going one level down 

                    //   - so assume here that every 'typed' dataset

                    //is a direct descendent of System.Data.DataSet


                    //need to remove any quotes if we're in a recursive call

                    Expression baseExpression = 
                      debugger.GetExpression(originalContext + 
                      ".GetType().BaseType.Name" , true, 500);
                    string val = baseExpression.Value.Replace("\"", String.Empty);
                    string subType = type.Substring(type.LastIndexOf(".") 
                                          +1).Replace("}", String.Empty); 
                    return (val != subType);
                }
                else
                    //All is OK

                    return false;
            }
        else
            return expr.Value.IndexOf("error:")>-1;
    }

Selecting specific rows

If we've selected a row in the debugger by a variable: myRow or with an indexer: myDataSet.Tables[0].Rows[0], we know we can find the ordinal position of the row (in the table) via the rowID property. We can then use this information in the output form to select the appropriate row (and table).

Row Filtering

Row Filtering

This works with a combination of standard DataView filtering - using a free text row filter, and a DataViewRowState filter (useful to show all rows in a particular state). The following applies a combination of both filters.

    private void ApplyFilter(bool showAll)
    {
        try
        {
            if (_dataSource == null)
                return;

            //Filter the contents of the grid

            if (showAll || (this.rowStateFilter.SelectedIndex == 0 && 
                            this.rowFilterExpression.Text == String.Empty))
                _view = _dataSource.DefaultView;
            else
            {
                _view = new DataView(_dataSource);
                if (this.rowStateFilter.SelectedIndex != 0)
                    _view.RowStateFilter = 
                      (DataViewRowState)Enum.Parse(typeof(DataViewRowState), 
            this.rowStateFilter.SelectedItem.ToString(), true);

                _view.RowFilter = this.rowFilterExpression.Text;
            }
            this.grid.DataSource = _view;
            if (this.FilterChanged != null)
                this.FilterChanged(this.grid, 
                     new FilterChangedEventArgs(_dataSource, 
                     _view.Count, _dataSource.Rows.Count - _view.Count));

            this.grid.Refresh();
        }
        catch (Exception ex)
        {
            MessageBox.Show(String.Format("There was a problem" + 
               " applying the row filter\n\n{0}", ex.Message), 
               "Row Filter", MessageBoxButtons.OK, 
               MessageBoxIcon.Exclamation);
        }

    }

The EnhancedGrid UserControl defines a FilterChanged event, passing context information to allow the parent form to alter the text in its Status Bar.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here