I have a word template file within some content-control that one of them is a Picture-content-control, tagged with sign.
I read many documents and some questions like mines here and reached to some code like this one, but it just doesn't work and do not add the picture to document, I don't understand where is the problem.
public void AddSignHidden(Microsoft.Office.Interop.Word._Document Doc, string SignImagePath)
{
ContentControls Ccontrol = Doc.SelectContentControlsByTag("__sign");
foreach (ContentControl nativeControl in Ccontrol)
{
nativeControl.Range.InlineShapes.AddPicture(SignImagePath, Type.Missing, true, Type.Missing);
}
Doc.Save();
Doc.Close();
Doc = null;
}
Related
I have a PPT template in which I need to programmatically replace some text fields with data that comes from my database then convert the ppt to pdf and send it as attachment in an email. I am not sure of how to change the content of text fields in the PowerPoint Presentation. I think OpenXML needs to be used.
Please help me to feed dynamic data into my ppt template.
I have worked previously with the DocumentFormat.OpenXml from Microsoft but with word files. I played a bit with it to replace some Texts in a Power Point file.
Here is my simple test code snippet:
static void Main(string[] args)
{
// just gets me the current location of the assembly to get a full path
string fileName = GetFilePath("Resource\\Template.pptx");
// open the presentation in edit mode -> the bool parameter stands for 'isEditable'
using (PresentationDocument document = PresentationDocument.Open(fileName, true))
{
// going through the slides of the presentation
foreach (SlidePart slidePart in document.PresentationPart.SlideParts)
{
// searching for a text with the placeholder i want to replace
DocumentFormat.OpenXml.Drawing.Text text =
slidePart.RootElement.Descendants<DocumentFormat.OpenXml.Drawing.Text>().FirstOrDefault(x => x.Text == "[[TITLE]]");
// change the text
if (text != null)
text.Text = "My new cool title";
// searching for the second text with the placeholder i want to replace
text =
slidePart.RootElement.Descendants<DocumentFormat.OpenXml.Drawing.Text>().FirstOrDefault(x => x.Text == "[[SUBTITLE]]");
// change the text
if (text != null)
text.Text = "My new cool sub-title";
}
document.Save();
}
}
In my case i had a simple presentation with one slide and in the Text fields the entries "[[TITLE]]" and "[[SUBTITLE]]" which i replaced with this text.
For my test file this worked well but maybe you will need to adopt / change something for your specific file.
E.g. in Word i had sometimes texts which was splitted in multiple Text parts within a Run element and had to write a logic to "collect" this data and replace them with one Text element with the new text i wanted to be there or maybe you have to search for other Descendant types.
I am trying to replace the temporary text in a word document with new text from a list. It works if the text is not in a shape, but once it tries to find the text in a textbox it throws an error. Here is what I have so far:
public void FindReplace(List<repvals> replaceVals, string docLocation, int listLen)
{
//Opens a new Word application
var app = new Microsoft.Office.Interop.Word.Application();
//Opens the .docx
var doc = app.Documents.Open(docLocation, true, false);
//Selects the document
var range = doc.Range();
for (int i = 0; i < listLen; i++)
{
//Finds the parameter, then replaces
range.Find.Execute(FindText: Convert.ToString(replaceVals[i].tempVal), Replace: WdReplace.wdReplaceAll, ReplaceWith: Convert.ToString(replaceVals[i].Boxes));
var shapes = doc.Shapes;
//Finds text within textboxes, then changes them
foreach (Microsoft.Office.Interop.Word.Shape shape in shapes)
{
var initialText = shape.TextFrame.TextRange.Text;
var resultingText = initialText.Replace(Convert.ToString(replaceVals[i].tempVal), Convert.ToString(replaceVals[i].Boxes));
shape.TextFrame.TextRange.Text = resultingText;
}
}
//prints document
doc.Save();
doc.Close();
//fully closes Word
Marshal.ReleaseComObject(app);
}
The problem occurs when it hits
var initialText = shape.TextFrame.TextRange.Text;
And throws an error saying: "This object does not support attached text."
The text in the shapes are nothing special. (e.g. tDATE, tNAME, etc.)
Any ideas?
I found the answer. Turns out my code was fine, however the document I was using (which I didn't write), had another shape on the second to last page to form a place to sign your name. I replaced that with an underscore, ran the code, and everything changed perfectly.
For those who also experience this problem, try checking how many shapes your foreach loop has counted:
http://i.imgur.com/1yNrL4p.png
Thank you Andrew and varocarbas for the help
*"In 2003 the AltText default for a standard textbox WAS the contained text BUT since you can change the Alt Text to NOT match it was never a good idea to read it this way. In 2010 the default for Alt Text is blank
If the textbox is named "Text Box 2" (substitute the correct name if not)
MsgBox ActiveDocument.Shapes("Text Box 2").TextFrame.TextRange should work."*
--
John SR Wilson
http://answers.microsoft.com/en-us/office/forum/office_2010-customize/shapesalternativetext-is-blank-for-the-docx/7671c746-2c2b-41d9-b7de-389a766587a7?page=2&msgId=31041d67-e62b-4ce0-b283-57fd6a4ff6b2
I have my own custom file type similar to .CSV except with custom delimiters. The delimiter for the comma is (char)20 (looks like a square) and for the quotes it is (char)254 (looks like þ). I have created an Excel 2010 Add-in in Visual studio to parse through the document that replaces all custom delimiters with commas and double-quotes so that it is in .CSV format.
The program also creates a new toolbar and button that will start the process. It works fine on some documents but not on others and if you try to do it twice in one instance of Excel it comes up with the error "Cannot implicitly convert type 'System.DBNull' to 'string'". This is because the row.Text property is being read as {}.
Now my question is what is causing the row.Text property to be read as {} instead of the text that is inside the cell? Also why does this occur on some documents but not others even though they use the same encoding?
An example of what is in a cell is (NOTE-the comma symbol won't print here):
þITEM_IDþþBEGDOCþþENDDOCþþBEGATTþþENDATTþþPARENT_ATTACHMENTþþATTACHMENT_BATESþþ etc.
EDIT Here is my code:
public partial class ThisAddIn
{
Office.CommandBarButton toolbarCommand;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Office.CommandBar toolbar = Application.CommandBars.Add("My Toolbar",Office.MsoBarPosition.msoBarTop,false,true);
toolbarCommand = (Office.CommandBarButton)
toolbar.Controls.Add(
Office.MsoControlType.msoControlButton,
missing,
missing,
missing,
true);
toolbarCommand.Caption = "Toolbar Button";
toolbarCommand.FaceId = 59;
toolbarCommand.Click += new Office._CommandBarButtonEvents_ClickEventHandler(toolbarCommand_Click);
toolbar.Visible = true;
}
void toolbarCommand_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
{
Excel.Worksheet activeWorksheet = ((Excel.Worksheet)Application.ActiveSheet);
try
{
IterateRows(activeWorksheet);
}
catch(Exception e)
{
MessageBox.Show(e.ToString());
}
Ctrl.Click+=new Office._CommandBarButtonEvents_ClickEventHandler(toolbarCommand_Click);
}
public void IterateRows(Excel.Worksheet worksheet)
{
//Get the used Range
Excel.Range usedRange = worksheet.UsedRange;
Excel.Worksheet activeWorksheet = ((Excel.Worksheet)Application.ActiveSheet);
//Iterate the rows in the used range
if (usedRange.Rows.Count > 1)
{
foreach (Excel.Range row in usedRange.Rows)
{
//MessageBox.Show(row.Text);
char quote = (char)254;
string data = row.Text;
row.Columns[1] = data.Replace(quote, '"').Replace((char)20, ',');
row.TextToColumns(Type.Missing, Excel.XlTextParsingType.xlDelimited, Excel.XlTextQualifier.xlTextQualifierDoubleQuote, Type.Missing, Type.Missing, Type.Missing, true);
}
}
}
I have a question about creating excel button and adding vba code function on it. I have created a button and module code but don't know how to make relation between them. Can anyone show me how?
my code for Button:
Excel.Shape btn = xlWorkSheet5.Shapes.AddOLEObject("Forms.CommandButton.1", Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 300, 10, 150, 22);
Excel.OLEObject sheetBtn = (Excel.OLEObject)xlWorkSheet5.OLEObjects(btn.Name);
sheetBtn.Object.GetType().InvokeMember("Caption", System.Reflection.BindingFlags.SetProperty, null, sheetBtn.Object, new object[] { "Calculate Bus Load" });
and code for module:
String sCode = "Sub main()\r\n" +
" MsgBox \"Hello world\"\r\n" +
"end Sub";
VBA.VBComponent oModule = xlWorkBook.VBProject.VBComponents.Add(VBA.vbext_ComponentType.vbext_ct_StdModule);
oModule.Name = "Module1";
oModule.CodeModule.AddFromString(sCode);
xlWorkBook.VBProject.VBComponents.Item(1).CodeModule.AddFromString(sCode);
I have searched in Internet but didn't find anything usefull, so i cleared my mind and focused once more with c# help and I found an answer how to do it properly.
My Code:
String updateBT "...// macro code for button";
VBA.VBComponent oModule1 = xlWorkBook.VBProject.VBComponents.Add(VBA.vbext_ComponentType.vbext_ct_StdModule);
oModule1.Name = "Update";
oModule1.CodeModule.AddFromString(updateBT);
Excel.Shape btn2 = xlWorkSheet1.Shapes.AddFormControl(Excel.XlFormControl.xlButtonControl, 150, 5, 150, 22);
btn2.Name = "Update";
btn2.OnAction = "... // name of your macro code";
btn2.OLEFormat.Object.Caption = "... // Button name";
As far as I understand, you need the intermediate call to code/macro module from the button when you click on the button. So the code gets triggered and does what you want it to do.
In usual manner, for e.g.
we add a button in Excel Sheet
choose on_click event
add a code like call mySub
You need to do that within C#.
Please adjust for your module and control names. Here is a sample.
//Within your above code add,
sheetBtn.Click += new MSForms.CommandButtonEvents_ClickEventHandler(sheetBtn_Click);
}
//button click event triggers
void sheetBtn_Click()
{
call subMain
// try a test using : MessageBox.Show("button test!");
}
** PLEASE TRY THIS TUTORIAL OUT It has pretty much what you need.
As per the subject on just invoking a sheet sub or module sub written in Excel from C#, you may use run macro method.
//instead of this.application, you call refer to the Excel app object
this.Application.Run("yourMacroName",missing,missing........)
Reference:
So I have a Microsoft.Office.Interop.Excel.Workbook object. It basically uses a template Excel file to construct itself. The Excel file contains a template column color for the results section, etc. and then the code basically just prints over those template columns, it doesn't actually customize the look of the file itself, only puts the data into it.
However, this is an issue because after it's done, our template accounts for the most POSSIBLE rows it can, but a lot of the times (most of the time), we use not even half of them.
What's the easiest way to remove all rows that DO NOT have cell data in them after the file has been created, working directly with the Microsoft.Office.Interop.Excel.Workbook object. We already have a "cleanup" method that runs after creation, but I want to add that logic to it. Here's our current cleanup:
private void CleanupExcel()
{
if (!_visible && _workbook != null)
{
_workbook.Close(false, Missing.Value, Missing.Value);
}
_workbook = null;
_sheet = null;
if (_excel != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(_excel);
// WW, 5/26/09: not sure if a problem here, but it probably is since the code was taken from here
// but in the indicator, Excel exists in the process even after the app is closed. The code here seems to fix it.
GC.Collect();
GC.WaitForPendingFinalizers();
}
_excel = null;
}
P.S. It's the first of two sheets in the document by the way. I also have access to the Microsoft.Office.Interop.Excel.Worksheet object if it's easier to do that way.
Assuming that all the empty rows are at the bottom of the sheets, you should be able to select them as a range and then delete them all, something like this I think:
Excel.Range range = _sheet.get_Range("A501", "A60000");
Excel.Range row = range.EntireRow;
rowDelete(Type.Missing);
If they're not at the bottom, maybe you could do a sort so that they all end up at the bottom and then use something similar to my code.
Try the following. It basically goes through a range (which I've hard-coded to be A1:A10), checks which rows are empty, marks them for deletion, then sweeps though and deletes them.
public void RemoveRows()
{
Excel.Range rng = Application.get_Range("A1", "A10");
List<int> rowsMarkedForDeletion = new List<int>();
for(int i = 0; i < rng.Rows.Count; i++)
{
if(Application.WorksheetFunction.CountA(rng[i + 1].EntireRow) == 0)
{
rowsMarkedForDeletion.Add(i + 1);
}
}
for(int i = rowsMarkedForDeletion.Count - 1; i >= 0; i--)
{
rng[rowsMarkedForDeletion[i]].EntireRow.Delete();
}
}
To give full credit, using COUNTA is a technique I learned from OzGrid.