0
votes

What I want to achieve: I want to have a DataTable with rows of different types with different properties. One example is a sum row at the end of the DataTable (there will be more once this works).

Inspired by the two answers to this question describing extended DataRow classes (not the accepted one!) I have implemented the following:

public class ProjectEffortTable : DataTable
{
    public ProjectEffortTable() : base() { }
    public ProjectEffortTable(String TableName) : base(TableName) { }
    protected ProjectEffortTable(System.Runtime.Serialization.SerializationInfo Info, System.Runtime.Serialization.StreamingContext Context) : base (Info, Context) { }
    public ProjectEffortTable(String TableName, String TableNamespace) : base(TableName, TableNamespace) { }

    protected override Type GetRowType()
    {
        return typeof(ProjectEffortRow);
    }
    protected override DataRow NewRowFromBuilder(DataRowBuilder Builder)
    {
        return new ProjectEffortRow(Builder);
    }
}
public class ProjectEffortRow : DataRow
{
    public ProjectEffortRow (DataRowBuilder Builder) : base (Builder)
    {
    }

    public Boolean IsSum { get; set; }
}

With the following code I can include a new sum row:

var SumRow = ProjectEfforts.NewRow() as ProjectEffortRow;
SumRow.IsSum = true;
// calculate sums for all month columns
foreach (DataColumn Column in ProjectEfforts.Columns)
{
    Decimal Sum = 0;
    foreach (DataRow CurrentRow in ProjectEfforts.Rows)
    {
        if (CurrentRow[Column] is Double)
        {
            Sum += Convert.ToDecimal(CurrentRow[Column]);
        }
    }
    SumRow[Column] = Decimal.Truncate(Sum);
}
ProjectEfforts.Rows.Add(SumRow);

The problem: The DataTable object can be manipulated by the user (using a DataGridView) and I need to save these changes to a data base in my data model (without saving the sum row).
To check for changes if have the following function:

Boolean CheckForChanges()
{
    Boolean Changed = false;

    var ProjectChanges = DataTableObject.GetChanges();
    if (ProjectChanges != null)
    {
        for (var i = 0; i < ProjectChanges.Rows.Count; i++)
        {
            if (!(ProjectChanges.Rows[i] as ProjectEffortRow).IsSum)
            {
                Changed = true;
            }
        }
    }
    return Changed;
}

Unfortunately that method always returns true because it seems that GetChanges() creates a new DataTable where the information of the property is lost.

What I don't want to do: I don't want to add columns to the DataTable for each of the properties because this would tightly couple my view with the data model. If I created new columns for each property I would do that in the model and would need to hide all these columns in the view - which I deem quite ugly.

The question: Is it possible to somehow create a DataTable containing custom types of DataRows that maintain custom properties?

Thanks in advance for any help

1

1 Answers

0
votes

After thinking some more I found a solution that works fine so far. I am not sure yet how well it scales but for the sum row I am quite satisfied. The key was to also implement GetChanges with custom code as the information about sum rows is known in that function.

Here's my current implementation:

public class ProjectEffortTable : DataTable
{
    public ProjectEffortTable() : base() { }
    public ProjectEffortTable(String TableName) : base(TableName) { }
    protected ProjectEffortTable(System.Runtime.Serialization.SerializationInfo Info, System.Runtime.Serialization.StreamingContext Context) : base (Info, Context) { }
    public ProjectEffortTable(String TableName, String TableNamespace) : base(TableName, TableNamespace) { }

    protected override Type GetRowType()
    {
        return typeof(ProjectEffortRow);
    }
    protected override DataRow NewRowFromBuilder(DataRowBuilder Builder)
    {
        return new ProjectEffortRow(Builder);
    }
    public new ProjectEffortTable GetChanges()
    {
        var Changes = Clone() as ProjectEffortTable;
        foreach (ProjectEffortRow CurrentRow in Rows)
        {
            if ((CurrentRow.RowState != DataRowState.Unchanged) && (!CurrentRow.IsSum))
            {
                Changes.ImportRow(CurrentRow);
            }
        }
        if (Changes.Rows.Count == 0)
        {
            Changes = null;
        }
        return Changes;
    }
    public new ProjectEffortTable GetChanges(DataRowState RowStates)
    {
        var Changes = Clone() as ProjectEffortTable;
        foreach (ProjectEffortRow CurrentRow in Rows)
        {
            if ((CurrentRow.RowState == RowStates) && (!CurrentRow.IsSum))
            {
                Changes.ImportRow(CurrentRow);
            }
        }
        if (Changes.Rows.Count == 0)
        {
            Changes = null;
        }
        return Changes;
    }

    public void AddSumRow()
    {
        // add line with sum for each month column
        var SumRow = NewRow() as ProjectEffortRow;
        SumRow.IsSum = true;
        Rows.Add(SumRow);
        RecalculateSums();
    }
    public Boolean HasSumRow()
    {
        var SumRowFound = false;

        if ((Rows[Rows.Count - 1] as ProjectEffortRow).IsSum)
        {
            SumRowFound = true;
        }
        return SumRowFound;
    }
    public void RemoveSumRow()
    {
        if (HasSumRow())
        {
            Rows[Rows.Count - 1].Delete();
        }
    }

    private void RecalculateSums()
    {
        if (!HasSumRow())
        {
            throw new ApplicationException("Recalculation of sum triggered without sum row being present");
        }

        foreach (DataColumn Column in Columns)
        {
            Decimal Sum = 0;
            foreach (ProjectEffortRow CurrentRow in Rows)
            {
                if ((CurrentRow[Column] is Double) && (!CurrentRow.IsSum))
                {
                    Sum += Convert.ToDecimal(CurrentRow[Column]);
                }
            }
            Rows[Rows.Count - 1][Column] = Decimal.Truncate(Sum);
        }
    }
}