I am working with the openxml sdk and have a table to write to a word doc. Once i have written my table, is there anyway to select all the columns for that table and delete one of them based on the text for that column? For example:
foreach (var column in table.columns)
{
if (column.Text == "Hello")
{
table[column].delete();
}
}
Is there a real world implementation of the above pseudo code?
Unfortunately, there is no concept of a "column" in a word processing table (DocumentFormat.OpenXml.WordProcessing.Table), only rows and cells. If you can assume the first row contains column names, and all rows contain the same number of cells, you can accomplish your task. One thing you have to be careful of (and one problem in your pseudocode above) is that you can't delete things from the enumeration you're currently iterating over. Here's how I would accomplish this:
var rows = table.Elements<TableRow>();
var firstRowCells = rows.ElementAt(0).Elements<TableCell>();
var cellNumbersToDelete = new List<int>();
for( int i=0; i<firstRowCells.Count(); i++ )
if( GetCellText( firstRowCells.ElementAt( i ) ) == "Hello" )
cellNumbersToDelete.Add( i );
foreach( var cellNumberToDelete in cellNumbersToDelete ) {
foreach( var row in rows ) {
cellToDelete = row.ElementAt( cellNumberToDelete );
row.RemoveChild( cellToDelete );
}
}
I don't have time right now to test this solution, so it'll probably require some tweaking, but it should give you a general idea about how to accomplish your task. The method GetCellText referenced above would look something like this:
string GetCellText( TableCell cell ) {
var p = cell.Elements<Paragraph>().First();
var r = p.Elements<Run>().First();
var t = r.Elements<Text>().First();
return t.Text;
}
Related
I have seen many posts about finding the last row of a given column for Google Sheets API v4 in C#, but I can't seem to find anything about finding the last column of a given row. I didn't find any questions about this specifically - but if I'm mistaken please direct me to the right place.
In my sheet, I have headers for each column. Over time, I anticipate I will need to add or remove columns as needed - it would be great to not have to update my code every time this happens.
I'm at the beginning stages of writing my code that gathers my data from Google Sheets - but here is what I have so far. I know that I will need to change the way my variable "range" is written, just don't know what.
static void ReadEntries()
{
var range = $"{sheet}!A1:ET";
var request = service.Spreadsheets.Values.Get(SpreadsheetId, range);
var response = request.Execute();
var values = response.Values;
if(values != null && values.Count>0)
{
foreach (var row in values)
{
System.Diagnostics.Debug.WriteLine("{0} | {1} | {2}", row[0], row[1], row[2]);
}
}
else
{
System.Diagnostics.Debug.WriteLine("No data found.");
}
}
EDIT: SOLVED
I used the pseudo code provided by Nazi A for this. I was having issues with the if(row[col]) piece with casting and other system exceptions. It turns out foreach allows for us to not have to check if that row[col] is in range. Below is my final code in case anyone needs it in the future. I plan to let column "ET" declared in var range = $"{sheet}!A1:ET; be big enough to accommodate any future columns being added to my spreadsheet. Thanks for your help!
static void ReadEntries()
{
var range = $"{sheet}!A1:ET";
var request = service.Spreadsheets.Values.Get(SpreadsheetId, range);
var response = request.Execute();
var values = response.Values;
int max = 0;
int currMax;
if (values != null && values.Count>0)
{
foreach(var row in values)
{
currMax = 0;
foreach(var col in row)
{
currMax++;
}
if (max < currMax)
{
max = currMax;
}
}
}
else
{
System.Diagnostics.Debug.WriteLine("No data found.");
}
System.Diagnostics.Debug.WriteLine(max);
}
So basically, you need to have a nested loop to traverse all rows and columns in values.
I have tested this psuedo code and worked but since I have no any means to run a C# code, this is all I can give to you. This is a pseudo code that should be readable to you.
var max = 0;
foreach(var row in values){
var currMax = 0;
foreach(var col in row){
if(row[col]){ // as long as data exists, currMax will increment
currMax++;
continue;
}
break; // stop loop if last cell being checked is empty
}
if(max < currMax){ // assign the largest currMax to max
max = currMax;
}
}
So in this psuedo code, max will contain the value of the largest column of all rows in the range. this code above should replace your foreach call
If you have any questions, feel free to clarify below.
What is the best way to find the names of the columns of a ListView?
I converted a DataTable to a List using a procedure I found on this forum, but I cannot make it to put the Id column first, especially because not all of my DataTables have a column "Id".
I can search in collection listView.Columns.ToString() but the format I am seeing is:
"ColumnHeader: Text: Id"
which I have to parse to find the proper name "Id".
This does not look like the spirit of C#.
I also tried: listView.SelectedItems[0].SubItems["Id"]
but that does not compile.
Ok Here is the complete code.
The exact problem is that the user selects a row in the listView with Courier Names and Ids, but it could also be Ids and Names, in that order. The fastest way to find the Id of the selected courier would be:
ListViewItem si = listCouriers.SelectedItems[0];
CourierId = si.SubItems["Id"].Text;
but that does not work. The hardcoded way would be this, but I cannot guarantee that some day the wrong column will be used:
ListViewItem si = listCouriers.SelectedItems[0];
CourierId = si.SubItems[1].Text;
Using #HuorSwords method leads to this not-so-simple solution, which works for me, but depends on the reasonable assumption that the order of columns in the ColumnHeaderCollection corresponds to the display on the form:
ListViewItem si = listCouriers.SelectedItems[0];
string CourierId = null;
int icol = 0;
foreach (ColumnHeader header in listCouriers.Columns)
{
if (header.Text == "Id")
{
CourierId = si.SubItems[icol].Text;
break;
}
icol++;
}
As listView.Columns is of type ListView.ColumnHeaderCollection, then it contains ColumnHeader objects.
The ColumnHeader.Text contains the column title, so you can check for concrete column with:
foreach (ColumnHeader header in listView.Columns)
{
if (header.Text == "Id")
{
// Do something...
}
}
I don't know if is the best approach, but you don't need to parse the results to find "Id" value...
UPDATE
Also, have you tried to reference it with the String indexer? > listView.Columns["Id"]
use this code:
private ColumnHeader GetColumn(string Text)
{
for (int i = 0; i < listView1.Columns.Count; i++)
for (int j = 0; j < listView1.Items.Count; j++)
if (listView1.Items[j].SubItems.Count - 1 >= i)
if (listView1.Items[j].SubItems[i].Text == Text)
return listView1.Columns[i];
return null;
}
just give item text to this code and get everything you want of a column.
Enjoy ;)
I am currently using one button for inserting/updating content within a table. It then takes the uploaded CSV and inserts or updates it into a data table depending on whether the row exists or not.
Here is the code fired after the button's OnClick:
if (ExcelDDL.SelectedValue == "Time Points" && fileName == "TimePoints.csv")
{
var GetTPoints = (SEPTA_DS.TimePointsTBLDataTable)tpta.GetDataByCategory(CategoryDDL.SelectedItem.ToString());
//Loop through each row and insert into database
int i = 0;
foreach (DataRow row in TempRouteDataTable.Rows)
{
//Gather column headers
var category = Convert.ToString(CategoryDDL.SelectedItem);
var agency = Convert.ToString(row["Agency"]);
if (agency == null || agency == "")
{
//If row is empty skip it entirely
goto skipped;
}
var route = Convert.ToString(row["Route"]);
var GetRShortName = (SEPTA_DS.RoutesTBLDataTable)rta.GetDataByRouteID(route);
var newRoute = "";
if (GetRShortName.Rows.Count > 0)
{
newRoute = Convert.ToString(GetRShortName.Rows[0]["route_short_name"]);
}
var direction = Convert.ToString(row["Direction"]);
var serviceKey = Convert.ToString(row["Service Key"]);
var language = Convert.ToString(row["Language"]);
var stopID = Convert.ToString(row["Stop ID"]);
var stopName = Convert.ToString(row["Stop Name"]);
if (stopName.Contains("accessible"))
{
string[] noHTML = stopName.Split('>');
int insertH = Convert.ToInt32(hta.InsertHandicapRow(newRoute,noHTML[2]));
}
var sequence = Convert.ToString(row["Sequence"]);
var origID = -1;
if (GetTPoints.Rows.Count > 0)
{
origID = Convert.ToInt32(GetTPoints.Rows[i]["TPointsID"]);
var GetID = (SEPTA_DS.TimePointsTBLDataTable)tpta.GetDataByID(origID);
if (GetID.Rows.Count < 1)
{
origID = -1;
}
}
if (origID == -1)
{
int insertData = Convert.ToInt32(tpta.InsertTimePoints(category, agency, newRoute, direction, serviceKey, language, stopID, stopName, sequence));
}
else
{
int updateData = Convert.ToInt32(tpta.UpdateTimePoints(category, agency, newRoute, direction, serviceKey, language, stopID, stopName, sequence, origID));
}
skipped:
i++;
}
}
You can see how I check whether to insert or update around the bottom. I am using this method across other sections of this program and it works just fine. But in this case it is distorting my datatable immensely and I can't figure out why.
This is the bottom part of my table after inserting [no items currently within the database]:
This is the table after reuploading the CSV with data already existing within the table:
I am also getting this error when updating There is no row at position 2230.
What is going wrong in the code to cause this huge shift? I am just checking to see if the ID exists and if it does update rather than insert.
Also the reason i am using goto is because there are blank rows in the document that need to be skipped.
Is your TPointsID column, a auto-generated number? If so, since you are skipping the empty row, some referential integrity problem might be occuring,because of empty data in the skipped rows in the database.
From the error : There is no row at position 2230 , it is also understood that, because of the skipping you might be trying to access some non existent row in the datatable.
Also, if possible consider using the ADO.NET DataAdapter which has got the CRUD operation capability. You can find more about it at : http://support.microsoft.com/kb/308507
I am reading my DataTable as follow:
foreach ( DataRow o_DataRow in vco_DataTable.Rows )
{
//Insert More Here
}
It crash; because I insert more records.
How can I read my DataTable without reading the new records? Can I read by RowState?
Thanks
Since I don't know what language you are using I can only give general advice.
In most (all?) languages it's not possible to do a foreach over a collection if you are modifying the collection. There are two common ways to deal with this.
Wild ass guessing pseudo code follows:
// first way uses array notation (if possible)
var no_of_rows = vco_DataTable.Rows.count();
for(var i = 0; i < no_of_rows; i++) {
DataRow o_DataRow = vco_DataTable.Rows[i];
//Insert More Here
}
// The second way copies the data
var my_copy = vco_DataTable.Copy()
foreach ( DataRow o_DataRow in my_copy.Rows )
{
//Insert More into vco_DataTable Here
}
copy.Dispose() // delete/destroy the copy
I have long tables generated by datagrid control that go beyond the page width. I would like to convert that into separate table for each row or definition list where each field name is followed by field value.
How would I do that.
Uses jquery. If you have more than one table you'll need to change it to accommodate that. Also, just appends to the end of the document. If you want it elsewhere, find the element you want to place it after and insert it into the DOM at that point.
$(document).ready(
function() {
var headers = $('tr:first').children();
$('tr:not(:first)').each(
function(i,row) {
var cols = jQuery(row).children();
var dl = jQuery('<dl></dl>');
for (var i=0, len = headers.length; i < len; ++i) {
var dt = jQuery('<dt>');
dt.text( jQuery(headers[i]).text() );
var dd = jQuery('<dd>');
dd.text( jQuery(cols[i]).text() );
dl.append(dt).append(dd);
}
$('body').append(dl);
}
);
$('table').remove();
}
);
Here's a reference:
http://www.mail-archive.com/flexcoders#yahoogroups.com/msg15534.html
The google terms I think you want are "invert datagrid". You'll get lots of hits.