Gembox document range inserting columns instead of rows - report

I am generating documents by merging a .docx file with a .NET object using GemBox Document. I have one special case where I need to include a table with details of a collection of child objects. However, instead of inserting a row for each object, I need to insert a column. So the "header" cells would be in the leftmost column of the table and each object in the range will have a column added.
A normal table with rows for each object is simple, just do { MERGEFIELD RangeStart:(array) } in the first column and { MERGEFIELD RangeEnd:(array) } in the last column. Is there any way to do this for columns instead of rows though?

Currently, there is no such feature in the mail merge process, but what you could do is have the merge range on the whole table and then combine the resulting tables into one after the mail merge execution.
For example, let's say this is your document:
I've also set the Title property on this table to "Details" (Table Properties -> Alt Text -> Title) so that I can easily select them.
var document = DocumentModel.Load("template.docx");
// Sample source.
var source = new
{
Details = new[]
{
new { Detail1 = "First", Detail2 = "1", Detail3 = "11", Detail4 = "111" },
new { Detail1 = "Second", Detail2 = "2", Detail3 = "22", Detail4 = "222" },
new { Detail1 = "Third", Detail2 = "3", Detail3 = "33", Detail4 = "333" },
}
};
document.MailMerge.Execute(source);
// Retrieve tables with "Details" title.
var detailsTables = document.GetChildElements(true, ElementType.Table)
.Cast<Table>()
.Where(t => t.Metadata.Title == "Details")
.ToList();
// Copy cells from other "Details" tables into the first "Details" table.
var firstTable = detailsTables[0];
for (int i = 1; i < detailsTables.Count; i++)
{
var otherTable = detailsTables[i];
for (int r = 0; r < otherTable.Rows.Count; r++)
firstTable.Rows[r].Cells.Add(
otherTable.Rows[r].Cells[0].Clone(true));
otherTable.Content.Delete();
}
document.Save("output.docx");
The result is this:
Also, instead of using a Title property to identify the targeted tables, you could use a bookmark around the merged range and then retrieve the tables with the following:
var detailsTables = bookmark.GetContent(true).GetChildElements(ElementType.Table)
.Cast<Table>();
Last, if you need something like a header column, you can add it as a separate table before the merge range. In other words, something like this:

Related

How to create multi items/records or save item/record array at one time in client script file

I want to create multiple records at the same time using client script. This is what I'm doing:
var ceateDatasource = app.datasources.Reservation.modes.create;
var newItem = ceateDatasource.item;
newItem.User = user; //'eric'
newItem.Description = description; //'000'
newItem.Location_Lab_fk = lab.value.Id; //'T'
newItem.Area_fk = area.value.Id; //'L'
newItem.Equipment_fk = equipment.value.Id; //'S'
for(var i = 0 ; i < 3; i ++) {
newItem.Start_Date = startDate;
newItem.Start_Hours = '03';
newItem.Start_Minutes = '00';
newItem.End_Date = startDate;
newItem.End_Hours = '23';
newItem.End_Minutes = '30';
// Create the new item
ceateDatasource.createItem();
}
But the result I'm getting is this one:
The three records are created but the only the first one has data. The other two records have empty values on their fields. How can I achieve this?
Thanks.
Update(2019-3-27):
I was able to make it work by putting everything inside the for loop block. However, I have another question.
Is there any method like the below sample code?
var recordData = [Data1, Data2, Data3]
var ceateDatasource;
var newItem = new Array(recordData.length) ;
for(var i = 0 ; i < recordData.length; i ++) {
ceateDatasource = app.datasources.Reservation.modes.create;
newItem[i] = ceateDatasource.item;
newItem[i].User = recordData[i].user;
newItem[i].Description = recordData[i].Description;
newItem[i].Location_Lab_fk = recordData[i].Location_Lab_fk;
newItem[i].Area_fk = recordData[i].Area_fk;
newItem[i].Equipment_fk = recordData[i].Equipment_fk;
newItem[i].Start_Date = recordData[i].Start_Date;
newItem[i].Start_Hours = recordData[i].Start_Hours;
newItem[i].Start_Minutes = recordData[i].Start_Minutes;
newItem[i].End_Date = recordData[i].End_Date;
newItem[i].End_Hours = recordData[i].End_Hours;
newItem[i].End_Minutes = recordData[i].End_Minutes;
}
// Create the new item
ceateDatasource.createItem();
First, it prepares an array 'newItem' and only calls 'ceateDatasource.createItem()' one time to save all new records(or items).
I try to use this method, but it only saves the last record 'newItem[3]'.
I need to write a callback function in 'ceateDatasource.createItem()' but Google App Maker always show a warning "Don't make functions within a loop". So, are there any methods to call 'createItem()' one time to save several records? Or are there some functions like 'array.push' which can be used?
Thanks.
As per AppMaker's official documentation:
A create datasource is a datasource used to create items in a particular data source. Its item property is always populated by a draft item which can be bound to or set programmatically.
What you are trying to do is create three items off the same draft item. That why you see the result you get. If you want to create multiple items, you need to create a draft item for each one, hence all you need to do is put all your code inside the for loop.
for(var i = 0 ; i < 3; i ++) {
var ceateDatasource = app.datasources.Reservation.modes.create;
var newItem = ceateDatasource.item;
newItem.User = user; //'eric'
newItem.Description = description; //'000'
newItem.Location_Lab_fk = lab.value.Id; //'T'
newItem.Area_fk = area.value.Id; //'L'
newItem.Equipment_fk = equipment.value.Id; //'S'
newItem.Start_Date = startDate;
newItem.Start_Hours = '03';
newItem.Start_Minutes = '00';
newItem.End_Date = startDate;
newItem.End_Hours = '23';
newItem.End_Minutes = '30';
// Create the new item
ceateDatasource.createItem();
}
If you want to save several records at the same time using client script, then what you are looking for is the Manual Save Mode. So all you have to do is go to your model's datasource and click on the checkbox "Manual Save Mode".
Then use the same code as above. The only difference is that in order to persist the changes to the server, you need to explicitly save changes. So all you have to do is add the following after the for loop block:
app.datasources.Reservation.saveChanges(function(){
//TODO: Callback handler
});

Set Different Value on WebGrid Data if One Specific Row and Column is Null

I am working on MVC using WebGrid and WebGridColumns.
I have a list of data on my Model, but some of the columns on my data are null so I want to specify a different value for null columns.
Here is a sample of my code on View:
var objData = Model.myListofUsers;
var webMyGrid= new WebGrid();
webMyGrid.Bind(objData);
//Build a list of columns
List<WebGridColumn> webColumns= new List<WebGridColumn>();
webColumns.Add(new WebGridColumn { ColumnName = "strName", Header = "Name"});
webColumns.Add(new WebGridColumn { ColumnName = "strPhone", Header = "Phone"});
Not all Names have Phone numbers so I want to specify if there is no Phone number, I want to declare some words like "User have no phone" instead on those fields.
Format property of WeGridColumn could help you.
webColumns.Add(new WebGridColumn { ColumnName = "strName", Header = "Name", Format= (item) => String.IsNullOrEmpty(item.strName)? "Your Message": item.strName});

I need to select a particular row in a table with selenium webriver

I need to select a particular row in a table with selenium web driver, I have close to 5000 rows, Navigating through each row would be difficult. Any better approach if we have?
You need to click on a checkbox to select the row right?
I'll assume thats what you meant:
The only way I know how is to check each row, select the filter (i.e. a customer number) and once you find it, click on the selection checkbox.
Mostly of the tables are similar so here is an example:
This is your table:
= = = = = = =
=================================================================================================
= = = = = = =
=================================================================================================
= = = = = = =
=================================================================================================
Lets say that in the first row, is where the checkbox is, and that in the second row is the location of the filter.
meaning the data you will use in order to know that this is the row you need....
you will need to inspect the table element in order to find its ID, once you have that info you can use a similar method as the below:
Where you will use your webdriver, data filter, tableID and the number of the column where your data filter is located.
The method will return the row you need to click, then you can simply use the row[columnNumberLocation].Click() in order to select it.
public IWebElement returnRow(IWebDriver webDriver, string filter ,string tableName, int i)
{
IWebElement tableElement = webDriver.FindElement(By.Id(tableName));
IWebElement tbodyElement = tableElement.FindElement(By.TagName("TBODY"));
List<IWebElement> rows = new List<IWebElement>(tbodyElement.FindElements(By.TagName("tr")));
foreach (var row in rows)
{
List<IWebElement> cells = new List<IWebElement>(row.FindElements(By.TagName("td")));
if (cells.Count > 0)
{
string s = cells[i].Text;
if (s.Equals(filter))
{
return row;
}
}
}
return null;
}
It depends on what is your requirement of selecting that particular row.
I assume you need to select that according to the row index.
So it's all about the your x-path to locate the element.
ex: I need to get row number 2000
WebElement element = driver.findElement(By.xpath("//tr[2000]"));

How can I set column values to GridView Columns?

I have csv file like this:
I need to show this csv file with gridview. But I must change format like this:
I must select distinct just date and mount columns and use date values on gridview columns.
How can I use values of csv file for Gridview columns?
Assuming that reading the CSV file is not an issue and you have already something like a List<ClassName>, DataTable or List<string[]>. I'm presuming that it's a List<String[]> where the first "column" is Date, the second Mount and the last % in my following approach.
You need real DateTimes and ints to be able to sum percents by date:
var formatProvider = new CultureInfo("de-DE"); // seems to be the correct format
var mountGroups = listOfStringArray
.Select(arr => new
{
Date = DateTime.Parse(arr[0].Trim(), formatProvider).Date,
Mount = arr[1].Trim(),
Percent = int.Parse(arr[2].Trim())
})
.GroupBy(x => x.Mount);
Now you have grouped by Mount, you just need to sum the percents for every day. You can use a DataTable as datasource for the GridView. Here's code that creates the table with the dynamic columns for every day:
var dataSource = new DataTable();
dataSource.Columns.Add("Mount");
var lastWeekColumns = Enumerable.Range(0, 7)
.Select(d => new DataColumn(DateTime.Today.AddDays(-6 + d).ToShortDateString(), typeof(int)))
.ToArray();
dataSource.Columns.AddRange(lastWeekColumns);
Following loop executes the LINQ query and fills the table:
foreach(var grp in mountGroups)
{
DataRow row = dataSource.Rows.Add();
row.SetField("Mount", grp.Key); // because: GroupBy(x => x.Mount);
foreach(DataColumn dayCol in lastWeekColumns)
{
DateTime day = DateTime.Parse(dayCol.ColumnName, formatProvider);
int sumPercent = grp.Where(x => x.Date == day)
.Select(x => x.Percent)
.DefaultIfEmpty(0) // fallback value for missing days
.Sum();
row.SetField(dayCol, sumPercent);
}
}
Now you just need to use it as datasource (AuthoGenerateColumns set to true)
grid.DataSource = dataSource;
grid.DataBind();

how to convert a Linq List into a dataset?

I have a Linq List in which have data more than one tables and these tables has related to each other. in this list it has some another table list property. Like table1 and table2 has related to each other and we have a data in list of table1 and in this list it has automatically table2 data.
Now i want to convert this List into a XML but it throws an error i.e. circular reference error, So now i want to convert this list into a dataset and by using this dataset i can generate a xml.
So can anyone provide us code to generate multiple tables dataset from a List....
or
convert list to xml code....
or
any other helpful comments............
You really haven't given a lot of information to work with, but I'll take a stab at a basic answer.
Given this class structure:
public class Table1
{
public string Value;
public Table2 Table2;
}
public class Table2
{
public string Value;
}
I can create this list:
var lsttable1 = new List<Table1>()
{
new Table1()
{
Value = "Foo1",
Table2 = new Table2()
{
Value = "Bar1",
},
},
new Table1()
{
Value = "Foo2",
Table2 = new Table2()
{
Value = "Bar2",
},
},
};
Now, I might want to convert this into XML that looks like this:
<Table1s>
<Table1 Value="Foo1">
<Table2 Value="Bar1" />
</Table1>
<Table1 Value="Foo2">
<Table2 Value="Bar2" />
</Table1>
</Table1s>
Here's the LINQ code that does it:
var xd =
new XDocument(
new XElement(
"Table1s",
from t1 in lsttable1
select new XElement(
"Table1",
new XAttribute("Value", t1.Value),
new XElement(
"Table2",
new XAttribute("Value", t1.Table2.Value)
)
)
)
);
Is that the kind of thing that you wanted?

Resources