Domanda

I'm pretty new to C# and WPF, and my exposure is mostly self-inflicted, so I figure I'm probably missing something obvious. However it appears to be so obvious that it's not showing up in a couple of hours of searching.

Since I'm just trying to work out the processing, all the data is arbitrary, simplistic and a bit silly.

I have this data in a DataTable named dt1:

Name   Count  Date
Fred   1      12/01/13
Fred   2      12/02/13
Fred   3      12/03/13
Fred   4      12/04/13
Barney 4      12/01/13
Barney 3      12/02/13
Barney 2      12/03/13
Barney 1      12/04/13
Wilma  1      12/01/13
Wilma  2      12/02/13
Wilma  3      12/03/13
Wilma  4      12/04/13
Betty  4      12/01/13
Betty  3      12/02/13
Betty  2      12/03/13
Betty  1      12/04/13

(the columns are string, int and string, respectively)

What I'm trying to do is create a second DataTable with one row for each name, and the count for each date displayed in a separate column, thusly:

Name  12/01/13  12/02/13  12/03/13  12/04/13
Fred  1         2         3         4
...

Here's the code I'm using to populate the second DataTable:

DataTable dt2 = new DataTable();

//add columns for the target dates
dt2.Columns.Add("Name", typeof(string));
for (int n = 1; n < 5; n++)
{
    dt2.Columns.Add(String.Format("12/0{0}/13", n.ToString()), typeof(int));
}

DataRow pivotRow = dt2.NewRow();

foreach (DataRow row in dt1.Rows)   //step through the rows in the source table
{
    if (pivotRow[0].ToString() != row[0].ToString())   //if this is a "new" name in the data set
    {
        if (pivotRow[0].ToString() != "")  //and it's not the first row of the data set
            dt2.Rows.Add(pivotRow);  //add the row we've been working on
        pivotRow = dt2.NewRow();  //create a new row for the "next" name
        pivotRow[0] = row[0].ToString();  //add the "next" name to the name column
    }
    //match the string date stored in column 2 of the source DataTable to the column name in the target one, and put the associated int value in that column
    pivotRow[row[2].ToString()] = (int)row[1];
}
//once we've finished the whole source DataTable, add the final row to the target DataTable
dt2.Rows.Add(pivotRow);

//at this point, looking at it through the locals window everything *appears* to be peachy in dt2

GridView.ItemsSource = dt2.DefaultView;  //and here, it's all the pits.

This is what displays in the DataGrid:

Name        12/01/13    12/02/13    12/03/13    12/04/13
Fred
Barney
Wilma
Betty

Plainly something is getting saved, since its saving the names at the beginning of the rows, but equally plain is that it's not saving the rest of the data points.

My skull is all mushy from banging against this particular wall, so I've decided to ask for help from folks who know (much) better than I do.

Any insights or suggestions will be greatly appreciated.

È stato utile?

Soluzione

If you look at the Output window while debugging, you should notice a whole slew of binding errors when the grid is trying to display your data. The problem is that the "/" character is being used to parse the binding to the underlying object, so it can't get the data from your view.

You can get this working by replacing the '/' character in the ColumnName but placing it in the Caption.

//add columns for the target dates
DataTable dt2 = new DataTable();
dt2.Columns.Add("Name", typeof(string));
for (int n = 1; n < 5; n++)
{                
    var dataColumn = dt2.Columns.Add(String.Format("12_0{0}_13", n), typeof (int));
    dataColumn.Caption = String.Format("12/0{0}/13", n);                
}

DataRow pivotRow = dt2.NewRow();

foreach (DataRow row in dt1.Rows)   //step through the rows in the source table
{
    if (pivotRow[0].ToString() != row[0].ToString())   //if this is a "new" name in the data set
    {
        if (pivotRow[0].ToString() != "")  //and it's not the first row of the data set
            dt2.Rows.Add(pivotRow);  //add the row we've been working on
        pivotRow = dt2.NewRow();  //create a new row for the "next" name
        pivotRow[0] = row[0].ToString();  //add the "next" name to the name column
    }
    //match the string date stored in column 2 of the source DataTable to the column name in the target one, replacing the '/', and put the associated int value in that column
    pivotRow[row[2].ToString().Replace("/", "_")] = (int)row[1];
}
//once we've finished the whole source DataTable, add the final row to the target DataTable
dt2.Rows.Add(pivotRow);

Altri suggerimenti

Here is a solution that will work for arbitrary dates and arbitrary order of names in the original data table:

DataTable dt2 = new DataTable();
dt2.Columns.Add("Name", typeof(string));

IEnumerable<DateTime> distinctDates = dt1.AsEnumerable().Select(row => (DateTime)row["Date"]).Distinct().OrderBy(date => date);
foreach (var distinctDate in distinctDates)
{
    dt2.Columns.Add(distinctDate.ToString("MM__dd__yy", CultureInfo.InvariantCulture), typeof(int));
}

IEnumerable<string> distinctNames = dt1.AsEnumerable().Select(row => (string)row["Name"]).Distinct();
foreach (var distinctName in distinctNames)
{
    DataRow outputRow = dt2.NewRow();
    outputRow["Name"] = distinctName;

    IEnumerable<Tuple<int, DateTime>> inputRowsForName = dt1.AsEnumerable()
        .Where(row => (string)row["Name"] == distinctName)
        .Select(row => new Tuple<int, DateTime>((int)row["Count"], (DateTime)row["Date"]));

    foreach (var inputRowForName in inputRowsForName)
    {
        string columnName = inputRowForName.Item2.ToString("MM__dd__yy", CultureInfo.InvariantCulture);
        outputRow[columnName] = inputRowForName.Item1;
    }
    dt2.Rows.Add(outputRow);
}

GridView.ItemsSource = dt2.DefaultView;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top