I have a Word 2007 VSTO addin for creating document templates. It allows you to add special content controls for cycles or conditions. Content controls are bound to custom xml part data (using content control ID as a reference).
I need to allow user to copy and paste CC. Word automatically change its ID so I lost the reference to custom xml part.
Is there any way to hook Word paste command and access the pasted range - Content Controls?
You can also use the Document.SelectionChange event on the VstoObject of the document, you can get it by using Microsoft.Office.Tools.Word.Document.GetVstoObject()
Example:
using WordTools = Microsoft.Office.Tools.Word;
WordTools.Document vstoDocument = Microsoft.Office.Tools.Word.Document.GetVstoObject(Globals.ThisAddIn.Application.ActiveDocument);
vstoDocument.SelectionChange += new WordTools.SelectionEventHandler(Document_SelectionChange);
private void Document_SelectionChange(object sender, WordTools.SelectionEventArgs e)
{
if (e.Selection.Range.Text != null)
{
// your code
}
}
I am not sure this is going to help you,
Wire up the event XMLSelectionChange in your project. This event has following parameters
(Selection Sel, XMLNode OldXMLNode, XMLNode NewXMLNode, ref int Reason)
Sel.Range should give the range which you are looking for.
Related
I have proper .dotm template.
When I create a new file based on a template by double clicking in explorer it creates the correct file (based on this template). Created file size after save is 16Kb (without any content).
But if I want to use .CreateFromTemplate method in my code I cannot open a newly created .docx file in MS Word.
New file size is 207Kb (just like .dotm file). MS Word display "run-time error 5398" and not open the file.
I'm using nuget package DocumentFormat.OpenXml 2.19.0, Word 365 version 16.0.14931.20648 - 32bit and code like this:
using (WordprocessingDocument doc = WordprocessingDocument.CreateFromTemplate(templatePath))
{
doc.SaveAs(newFileName);
}
Google is silent about this error, ChatGPT says that:
The "Run-time Error 5398" error means that the file you are trying to open is corrupted or not a valid docx file. Possible reasons for this error may be the following:
The file was not saved correctly after making changes. Verify that the Save() method was called after making changes to the file.
The file was saved with the wrong extension, e.g. as DOTM instead of DOCX
The file was saved in an invalid format.
There may have been some unhandled exceptions in your code.
When I manually change the extension of a new file from docx to dotm, there is no error when opening, but the file does not open.
What am I doing wrong with CreateFromTemplate method?
I tried to reproduce the behavior you described, using the following unit tests:
public sealed class CreateFromTemplateTests
{
private readonly ITestOutputHelper _output;
public CreateFromTemplateTests(ITestOutputHelper output)
{
_output = output;
}
[Theory]
[InlineData("c:\\temp\\MacroEnabledTemplate.dotm", "c:\\temp\\MacroEnabledDocument.docm")]
[InlineData("c:\\temp\\Template.dotx", "c:\\temp\\Document.docx")]
public void CanCreateDocmFromDotm(string templatePath, string documentPath)
{
// Let's not attach the template, which is done by default. If a template is attached, the validator complains as follows:
// The element has unexpected child element 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:attachedTemplate'.
using (var wordDocument = WordprocessingDocument.CreateFromTemplate(templatePath, false))
{
// Validate the document as created with CreateFromTemplate.
ValidateOpenXmlPackage(wordDocument);
// Save that document to disk so we can open it with Word, for example.
wordDocument.SaveAs(documentPath).Dispose();
}
using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(documentPath, true))
{
// Validate the document that was opened from disk, just to see what Word would open.
ValidateOpenXmlPackage(wordDocument);
}
}
private void ValidateOpenXmlPackage(OpenXmlPackage openXmlPackage)
{
OpenXmlValidator validator = new(FileFormatVersions.Office2019);
List<ValidationErrorInfo> validationErrors = validator.Validate(openXmlPackage).ToList();
foreach (ValidationErrorInfo validationError in validationErrors)
{
_output.WriteLine(validationError.Description);
}
if (validationErrors.Any())
{
// Note that Word will most often be able to open the document even if there are validation errors.
throw new Exception("The validator found validation errors.");
}
}
}
In both tests, the documents are created without an issue. Looking at the Open XML markup, both documents look fine. However, while I don't get any runtime error, Word also does not open the macro-enabled document.
I am not sure why that happens. It might be related to your security settings.
Depending on whether or not you really need to use CreateFromTemplate(), you could create a .docm (rather than a .dotm) and create new macro-enabled documents by copying that .docm.
I opened an issue in the Open XML SDK project on GitHub.
I have a number of WPF dialogs in my Word Add-In. For one of them (and only one, strangely), it is sometimes not focused when opened. I believe I need to set its parent.
I know there is a way to set the owner of a WPF window to a HWND, but is there any way to get a HWND in Word 2010? I found this HWND property but it is Word 2013 and later only. Is there any other way to get Word's HWND, other than using GetForegroundWindow() which does not guarantee the handle for the window I actually want (or any other similar kludge)?
I found something helpful in Get specific window handle using Office interop. But all those answers are based on getting the handle for a window you're newly creating. I modified it somewhat to get an existing window, and stuffed it into a utility method.
doc is the current document.
using System.Windows.Interop;
using System.Diagnostics;
public void SetOwner(System.Windows.Window pd)
{
var wordProcs = Process.GetProcessesByName("winword").ToList();
// in read-only mode, this would be e.g. "1.docx [Read-Only] - Microsoft Word"
var procs = wordProcs.Where(x =>
x.MainWindowTitle.StartsWith(Path.GetFileName(doc.FullName))
&&
x.MainWindowTitle.EndsWith("- Microsoft Word"));
if (procs.Count() >= 1)
{
// would prefer Word 2013's Window.HWND property for this
var handle = procs.First().MainWindowHandle;
WindowInteropHelper wih = new WindowInteropHelper(pd);
wih.Owner = handle;
}
}
Unfortunately it doesn't seem to be possible to account for multiple windows with the same document name (in different folders), because the number of processes is never greater than 1. But I think that's an acceptable limitation.
I have created a Word add-in following the article here. I press F5 and run the project and It works as expected and I would think that the add-in has been installed in my machine. So, now I open another instance of Word 2007 and create a document and I dont see that code working on the new document. Am I missing something?
Following is the code I am using :-
using Word = Microsoft.Office.Interop.Word;
namespace WordAddIn1
{
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.DocumentBeforeSave +=
new Word.ApplicationEvents4_DocumentBeforeSaveEventHandler(Application_DocumentBeforeSave);
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
void Application_DocumentBeforeSave(Word.Document Doc, ref bool SaveAsUI, ref bool Cancel)
{
Doc.Paragraphs[1].Range.InsertParagraphBefore();
Doc.Paragraphs[1].Range.Text = "Text was added by using code.---";
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
From here:-
When you finish developing a project, remove the add-in assembly, registry entries, and security settings from your development computer. Otherwise, the add-in will continue to run every time that you open Word on your development computer.
To clean up the completed project on your development computer
In Visual Studio, on the Build menu, click Clean Solution.
Now, when I dont clean the solution, I should have the add-in for the Word 2007 all the time, right? I don't see that happening at all.
Depending on the way you are doing things there are a few possibilities.
Most likely is that when you open the new instance, the debugger isn't attached, so your breakpoints are not being hit.
Another possible reason is that it is not a new instance but actually a new document in the same instance, and the the same instance of the same add-in is shared between the two documents. In that case ThisAdd.Loaded event will not be fired again, you have to listen for new documents being activated (from memory word doesn't have a NewDocument event)
What sort of code is not working? Is it the effects that cannot be seen, or a breakpoint not being hit?
I see that removing that add-in from word 2007 resolves the problem. If you face this problem, try removing the add-in from Word 2007 and build the solution the solution again and your change will take effect. For some reason "Clean solution" does not remove the word add-in sometimes (atleast sometimes in my case :-))
I want to get the path of current selected file in Eclipse workspace but my project is a simple view plug-in project.
I just want to display the name/path of the file selected as soon as user opens the view.
You get the current selection as mentioned by #Danail Nachev. See http://www.eclipse.org/articles/Article-WorkbenchSelections/article.html for information on working with the selection service.
Once you have the selection, the most common pattern is:
if (selection instanceof IStructuredSelection) {
IStructuredSelection ssel = (IStructuredSelection) selection;
Object obj = ssel.getFirstElement();
IFile file = (IFile) Platform.getAdapterManager().getAdapter(obj,
IFile.class);
if (file == null) {
if (obj instanceof IAdaptable) {
file = (IFile) ((IAdaptable) obj).getAdapter(IFile.class);
}
}
if (file != null) {
// do something
}
}
Edit:
Usually you get an InputStream from the IFile and process it that way. Using some FileSystemProviders or EFS implementations, there might not be a local path to the file.
PW
You can retrieve the current workbench selection using two methods with the following code:
through the Workbench SelectionService
getViewSite().getSelectionProvider().getSelection()
getViewSite().getWorkbenchWindow().getSelectionService()
You can find more information in this article.
Using the global workbench selection is the better approach, because it enables your view to get selection from everywhere, which is something the user might expect (I at least do). Also, almost all of the views in Eclipse (and I don't know exceptions to this rule) are using this approach.
If you absolutely must to tie your view to another view, then you can get all IWorkbenchPage iterate over them and search for the view by its ID and when you find the view, you call get its SelectionProvider to get the selection.
Only by reading this explanation, makes my hair go straight up. Considering that there might be multiple instances of the same view, you can end up with a selection from a random view. I'm not sure whether the user will make sense of how things work, unless you have some clear rules, which exactly view you need. It is up to you.
`
I developing an application in zend framework. I want to store certain secure images and pdf documents outside public folder like /project/data/uploads or /projects/application/data/uploads.
I am not able to access the images/pdf documents other than public folder.
Can someone suggest a way to do it.
thank you
You have to have a separate action that knows how to fetch and deliver all that stuff. Something like this:
public function viewpdfAction()
{
$id = (int) $this->_getParam('id', 0);
// You implement some function - either here in your controller
// or someplace else - to get the pdf name from the passed id.
// Alternatively, you can pass the name itself. Up to you, of course.
// The key is that the request somehow identifies the file requested.
$pdfName = $this->_mapPdfIdToName($id);
// The path to the real pdf
$pdfFile = '/project/data/uploads/pdf/' . $pdfName;
// Disable rendering
$this->_helper->viewRenderer->setNoRender(true);
// Send the right mime headers for the content type
$this->getResponse()
->setBody('')
->setHeader('Cache-control', 'public') // needed for IE, I have read
->setHeader('Content-type', 'application/pdf')
->setHeader('Content-Disposition', sprintf('attachment; filename="%s"', $pdfName));
// Send the content
readfile($pdfFile);
}
Of course, some of this can be pushed down into service classes to keep the controller as thin as possible. Everyone has different tastes in this regard.
I confess that this code not completely tested, mostly trying to give the basic idea. If I have made a total bonehead error in here, please let me know.
Hope it helps!