Excel "Refresh All" with OpenXML - excel-2007

I have an excel 2007 file (OpenXML format) with a connection to an xml file. This connection generates an excel table and pivot charts.
I am trying to find a way with OpenXML SDK v2 to do the same as the "Refresh All" button in Excel. So that I could automatically update my file as soon as a new xml file is provided.
Thank you.

Well there is quite good workaround for this.
Using OpenXML you can turn on "refresh data when opening the file" option in pivot table (right click on pivot table->PivotTable Options->Data tab).
This result in auto refresh pivot table when user first opens spreadsheet.
The code:
using (var document = SpreadsheetDocument.Open(newFilePath, true))
{
var uriPartDictionary = BuildUriPartDictionary(document);
PivotTableCacheDefinitionPart pivotTableCacheDefinitionPart1 = (PivotTableCacheDefinitionPart)uriPartDictionary["/xl/pivotCache/pivotCacheDefinition1.xml"];
PivotCacheDefinition pivotCacheDefinition1 = pivotTableCacheDefinitionPart1.PivotCacheDefinition;
pivotCacheDefinition1.RefreshOnLoad = true;
}
you need to determine "path" to yours pivotCacheDefinition - use OpenXML SDK 2.0 Productivity Tool to look for it.
BuildUriPartDictionary is a standard method generated by OpenXML SDK 2.0 Productivity Tool
protected Dictionary<String, OpenXmlPart> BuildUriPartDictionary(SpreadsheetDocument document)
{
var uriPartDictionary = new Dictionary<String, OpenXmlPart>();
var queue = new Queue<OpenXmlPartContainer>();
queue.Enqueue(document);
while (queue.Count > 0)
{
foreach (var part in queue.Dequeue().Parts.Where(part => !uriPartDictionary.Keys.Contains(part.OpenXmlPart.Uri.ToString())))
{
uriPartDictionary.Add(part.OpenXmlPart.Uri.ToString(), part.OpenXmlPart);
queue.Enqueue(part.OpenXmlPart);
}
}
return uriPartDictionary;
}
Another solution is to convert your spreadsheet to macroenabled, embed there a VBA script that will refresh all pivot tables.
This can happen on button click or again when user opens spreadsheet.
Here you can find VBA code to refresh pivot tables:
http://www.ozgrid.com/VBA/pivot-table-refresh.htm

You can't do this with Open XML. Open XML allows you to work with the data stored in the file and change the data and formulas and definitions and such. It doesn't actually do any calculations.
Excel automation technically would work, but it's absolutely not recommended for a server environment and is best avoided on the desktop if at all possible.

I think the only way you can do this is following this type of method..
Save Open XML workbook back to a xlsx file.
Load the workbook using the Excel object model.
Call either
ThisWorkbook.PivotCaches(yourIndex).Refresh();
or
ThisWorkbook.RefreshAll();
although I was pretty sure RefreshAll would also work.
Use the object model to Save the workbook and close it.
Reopen for use with xml namespaces.

The solution provided by Bartosz Strutyński will only work if the workbook does contain pivot tables and they share the same cache. If the workbook does not contain pivot tables, the code will throw a NullPointerException. If the workbook contains pivot tables that use different caches (which is the case when data sources are different), only one group of pivot tables that use the same cache will be refreshed. Below is the code based on Bartosz Strutyński's code, free of the aforementioned limitation, and not relying on knowing the "path" of PivotCacheDefinition object. The code also inlines BuildUriPartDictionary, which allows avoiding enumeration of uriPartDictionary in case it’s not used somewhere else, and uses explicit types, to ease searching documentation for the used classes.
Dictionary<String, OpenXmlPart> uriPartDictionary = new Dictionary<String, OpenXmlPart>();
Queue<OpenXmlPartContainer> queue = new Queue<OpenXmlPartContainer>();
queue.Enqueue(document);
while (queue.Count > 0)
{
foreach (IdPartPair part in queue.Dequeue().Parts.Where(part => !uriPartDictionary.Keys.Contains(part.OpenXmlPart.Uri.ToString())))
{
uriPartDictionary.Add(part.OpenXmlPart.Uri.ToString(), part.OpenXmlPart);
queue.Enqueue(part.OpenXmlPart);
PivotTableCacheDefinitionPart pivotTableCacheDefinitionPart;
if ((pivotTableCacheDefinitionPart = part.OpenXmlPart as PivotTableCacheDefinitionPart) != null)
{
pivotTableCacheDefinitionPart.PivotCacheDefinition.RefreshOnLoad = true;
}
}
}

Related

VSTO Msproject: Lock Task

I'm working on a small addin, when I click on a button, I'd like to set my remainig work and duration to 0 and locked my tasks
So I built something like that, my like function works but it's just to locked my tasks where I have a problem:
foreach (MSProject.Task i_objTask in g_objProject.Tasks)
{
if (i_objTask.WBS.like(WbsIndex+"%"))
{
i_objTask.RemainingWork = 0;
i_objTask.RemainingDuration = 0;
**Here I'd like to add something like : i_objtTask.Locked=true but this proprety doesn't exist****
}
}
Any idea of how could I do that?
Record Macro (under the Developer ribbon) is handy for this stuff. Using it gets you:
SetTaskField field:="Locked", value:="Yes"
Unfortunately, you have to select & iterate through tasks on the Task Sheet to manipulate tasks' fields in this way. I haven't seen a Task object property that you can set directly to manipulate the locked state.

How to add an array of datarows into an exisitng table inside my database

I'm a newbie so don't laugh :#
I'm working with 2002-2003 Microsoft access database.
Now, I want to add an array of DataRow into an existing table that I've in my database. Is there a way to do that? because right now I'm just adding the rows with a foreach loop
thank you
I think that the foreach-loop actually is the best way to do it.
foreach(DataRow row in yourRowArray)
{
dataTable.Add(row);
}
If you are using .Net Framework 3.5+ you can also use the DataRows CopyToDataTable() Method.
But you have to watch out because the Data in the DataTable is overwritten in this case.
DataTable table = yourDataTable;
DataRow[] yourRowArray = ...;
if(yourRowArray.Length > 0)
{
table = yourRowArray.CopyToDataTable();
}
I would recommend using the foreach-loop.
What you describe as array must be a saved file type i.e. excel or csv. Be sure it is a clean grid of data without extraneous non aligned rows.
Then you can link to that file with Access as a table. This is a manual step using the Access interface - in the ribbon it is the External area. This link remains good - allowing you to replace the excel/csv with a new one as long as the location path and structure of the file do not change.
Then you create an Append query to write all the records from this table into the table in your Access database.
www.CahabaData.com

Generate proper report when data is not in database [Telerik]

I'm generating telerik report for
"Number of students by level of education, field and sex"
Here the SQL query that I'm using to create this report
SELECT
[tbl_hec_ISCED].[ISCED_ID],
[tbl_hec_ISCED].[ISCED_Level],
[tbl_hec_Programme].[ISCED_ID] AS 'tbl_hec_ProgrammeISCED_ID',
[tbl_hec_Programme].[Programme_ID],
[tbl_hec_Programme].[Specialisation_ID_Number],
[tbl_hec_specialisation].[Rank_ID_Number],
[tbl_hec_specialisation].[Rank_Title],
[tbl_HEI_student].[Programme_ID] AS 'tbl_HEI_studentProgramme_ID',
[tbl_HEI_student].[Gender]
FROM ((([tbl_HEI_student]
FULL OUTER JOIN [tbl_hec_Programme]
ON [tbl_HEI_student].[Programme_ID] = [tbl_hec_Programme].[Programme_ID])
FULL OUTER JOIN [tbl_hec_specialisation]
ON [tbl_hec_Programme].[Specialisation_ID_Number] = [tbl_hec_specialisation].[Rank_ID_Number])
FULL OUTER JOIN [tbl_hec_ISCED]
ON [tbl_hec_Programme].[ISCED_ID] = [tbl_hec_ISCED].[ISCED_ID])
WHERE ([tbl_HEI_student].[Gender]='Male' or [tbl_HEI_student].[Gender]='Female') and ([tbl_hec_ISCED].[ISCED_Level]='5' or [tbl_hec_ISCED].[ISCED_Level]='6'or [tbl_hec_ISCED].[ISCED_Level]='7'or [tbl_hec_ISCED].[ISCED_Level]='8')
I'm getting null report since some values are not in database. I attached picture of it ,
HERE that view
I want generate report when there is no data in database. like below which means zero values for null rows.
HERE the expected report output
How can I overcome this challenge
My suggestion is to use document merging functionality using a template with all the report layout already set up as well as value placeholders (fields) that will be replaced by calculated values.
I'm using Aspose.Words library in my projects, but I'm sure there are others out there maybe even Telerik Reporting. This is very simple functionality so any reporting tool that can do complex stuff must be able to do this as well.
Here's some example code for Aspose. Other libraries will have different implementation.
using Aspose.Words;
void GenerateDocument(string templateFilePath, Dictionary<string, object> fieldNamesAndValues)
{
// This is our document object
Document output = null;
// Obtain the template file
if (File.Exists(templateFilePath))
{
// If the template file is successfully located, use this template
output = new Document(templateFilePath);
}
// Merge the provided values into the appropriate fields of the template
output.MailMerge.Execute(fieldNamesAndValues.Keys.ToArray(), fieldNamesAndValues.Values.ToArray());
// Save the document into a stream as PDF
MemoryStream stream = new MemoryStream();
doc.Save(stream, SaveFormat.Pdf);
// You can then do whatever with the stream:
// save it or push it to the browser for download
}
Using your expected result as an example, let's assume these are the names of your placeholders (fields) for the first row:
MALE_EDUCATION_ISCED5, MALE_EDUCATION_ISCED6, MALE_EDUCATION_ISCED7
You can then generate your report like so:
Dictionary<string, object> fieldsAndValues = new Dictionary<string, object>();
fieldsAndValues.Add("MALE_EDUCATION_ISCED5", calculatedValue1);
fieldsAndValues.Add("MALE_EDUCATION_ISCED6", calculatedValue2);
fieldsAndValues.Add("MALE_EDUCATION_ISCED7", calculatedValue3);
// and so on for other fields
GenerateDocument("~/Templates/Report.docx", fieldsAndValues);
More info on how to add fields in Microsoft Word:
https://support.office.com/en-us/article/7e9ea3b4-83ec-4203-9e66-4efc027f2cf3
More info on Aspose MailMerge:
http://www.aspose.com/docs/display/wordsnet/How+to++Execute+Simple+Mail+Merge

Can CrystalReports for VS2013 be programmed to export each page of the report to a separate pdf file

I am well into developing a billing program in VB2013 that needs to be able to export each customer bill to a pdf file that can then be attached to an email to the customer being billed. I have used CR for many, many years, but I have not found any way to programmatically make CR export to pdf. I have made activereports2 do so, but I am trying to get back down to just one report generator. I have had compatibility issues with Activereports2 by Datadynamics when running on some Windows Vista and later machines, so I was hoping to move everything to CR.
Well you can definitely generate Crystal Reports and convert to PDF on the fly in .NET.
Here's some sample code to help with that (this saves the PDF to the database, but you can remove that part if you don't need it):
public static int Crystal_PDFToDatabase(string reportName, object par1, object par2, object par3, string user, string DocName, string DocDesc, string DocType, int ClaimID, string exportFormatType)
{
try
{
CrystalReportSource CrystalReportSource1 = Crystal_SetDataSource(reportName, par1, par2, par3);
//SET EXPORT FORMAT
CrystalDecisions.Shared.ExportFormatType typ = CrystalDecisions.Shared.ExportFormatType.PortableDocFormat;
Stream str = CrystalReportSource1.ReportDocument.ExportToStream(typ);
if (str != null)
{
var memoryStream = new MemoryStream();
str.CopyTo(memoryStream);
int DocID = db_Docs.SaveNewDocument(DocName, DocDesc, DocType, user, memoryStream.ToArray(), ClaimID, null, null);
return DocID;
}
//clear out cache (to prevent other crystal reports from reusing old generated documents
CrystalReportSource1.ReportDocument.Close();
CrystalReportSource1.Dispose();
return 0;
}
catch
{
return 0;
}
}
Now a separate issue is not produce a separate PDF for each page in the report. I simply would not design it that way. I'd instead have the Crystal report generate 1 page, convert to PDF memorystream on the fly, and email it out. Then iterate on to the next customer. So much easier that way instead of having to figure out how (if its possible at all) to split that one huge Crystal Report / PDF into many slices.

Create a Crystal Report with dynamic tables at runtime

Hello I'm planning an application that is basically a reporting front-end for a database (proprietary Pervasive SQL) using an ODBC dsn-less connection string.
I am able to create the dataset in Visual Studio and link up the report(s) to the app. However in real world usage, the location of the database will be different per user. That is not the main problem. I overcame that with the connection string that allows you to set the location of the database path.
Here's the kicker...
The name of the tables that I want to read from are prefixed with a unique filename (actually the same name I mentioned above). I need crystal to re-map the table names at runtime. Just the table name prefixes really. The fields and names of the fields will not change.
Any ideas on where I should look for writing this block? I am using VS2010 & C# if that helps. I think thee should be some sort of class files that come with Crystal that can do some runtime reflection to get/set the table names?
Any thoughts would be welcome and appreciated.
Rob
Edit: I found some documents link that has API docs and other support. I will be studying them. They are all .chm files (Windows help files) so there is no "online" docs to search for.
Add a Crystal reportViewer change the name to objReport
add a public function ShowReport()
public void ShowReport(ReportDocument objReport)
{
Cursor.Current = Cursors.WaitCursor;
objReport.SetDatabaseLogon("", "dbpassword");
cRep.ReportSource = objReport;
this.Show();
Cursor.Current = Cursors.Default;
}
And Call it from anywhere in your application
ds = GetDataInDataSet();//fILL DataSet with your data
rptPSummary objRpt = new rptPSummary();
objRpt.SummaryInfo.ReportComments = "Have a nice day";
objRpt.SummaryInfo.ReportTitle = "Purchase Summary Report from " + sDate.ToString("dd/MM/yyyy") + " to " + eDate.ToString("dd/MM/yyyy");
objRpt.SetDataSource(ds);
frmReportView frmRpt = new frmReportView();
frmRpt.Text = objRpt.SummaryInfo.ReportTitle;
frmRpt.MdiParent = this;
frmRpt.ShowReport(objRpt);