I have a requirement to create read-only merge fields in Word.
I've already tried using the Locked property which can be seen below.The description of this property states - When a field is locked you cannot update field results which sounds like a perfect fit for my problem but this doesn't seem to be working
Below is the code i use to add the merge field to MS Word:
using Word = Microsoft.Office.Interop.Word;
Word.Range currentRange = Globals.ThisAddIn.Application.Selection.Range;
Word.MailMerge merge = Globals.ThisAddIn.Application.ActiveDocument.MailMerge;
merge.Fields.Add(currentRange, selectedNode.LocalName).Locked = true;
Once I run the code above and the field is created in Word, I am still able to right click it and select "Edit Field" where I can potentially rename the field or perform other changes without getting any errors or preventions from Word.
If anyone implemented something like this before, please share your knowledge.
Here is some insight on the technologies:
The solution is targeted at MS Word Office 2010
It must be written in .NET C# 3.5
Cannot use Open Xml SDK, the fix must be performed using Office Interop
The solution must achieve the desired goals without making the whole document read-only
Thank you for the replies, however I needed to only make the merge fields read-only the rest of the document should still stay as is.
A colleague of mine found a nice way to achieve what I was looking for, just sharing it in case someone else may need this functionality:
All you need to do is create a ContentControl object and add your merge field to the content control.
Set the LockContents property to true. This property is used to determine whether a user is allowed to edit the contents of a content control.
using Word = Microsoft.Office.Interop.Word;
object missing = System.Type.Missing;
Word.Selection PosRange = Globals.ThisAddIn.Application.Selection;
Microsoft.Office.Interop.Word.ContentControl cntCtrl;
cntCtrl = PosRange.Range.ContentControls.Add(Microsoft.Office.Interop.Word.WdContentControlType.wdContentControlRichText, ref missing);
object fldType = Microsoft.Office.Interop.Word.WdFieldType.wdFieldMergeField;
object fldText = "Employee";
Microsoft.Office.Interop.Word.Field fld = cntCtrl.Range.Fields.Add(cntCtrl.Range, ref fldType, ref fldText);
cntCtrl.LockContents = true;
In the image below a merge field is hosted inside a content control, note users are now unable to edit the field
Using the Locked property will only prevent the value of the field from changing, which I'm assuming is not what you want.
The only way I know to prevent the field code from changing is to protect the document. The fields themselves should still be updatable.
Related
I need to create a new word 2016 file, using VS2017, insert content (that's the easy part), and also to control it like doing the following:
Merge certain cells in same row, or same column
Define Right to Left or LTR
color the text/the background.
and more similar tasks.
I can open a document using
using Microsoft.Office;
using Word = Microsoft.Office.Interop.Word;
I can add text and save the document, yet still I don't see a way to fine control the color/direction and more parameters. After reading the documentation, it seems that this is probably not supported, unless I missed it.
I would appreciate if anyone can guide to a detailed documentation how to edit a word file from C# program.
Anyway, I can bypass it by creating an excel file which is simple using Interop and then insert it.
Here is a working solution for merging cells in a table, using VS2017 c#
var doc = DocX.Create(word_fname);
Table table = doc.AddTable(tableSize, 3);
table.Rows[row_cnt].MergeCells(1, 2); // to merge the 2nd & 3rd cells in the specific row
I need to use epplus instead of interop and in my excel file there is one "field" in the pivot table I can not get a hold of...
!(https://imgur.com/EPIwKQU)
This "Values" field is automatically generated from my code and does not show up in any of the fields I am able to access so far. Not sure if it is a field anyway.
var filesField = pivotTable.Fields["Files"];
filesField.Name = "Files";
pivotTable.DataFields.Add(filesField);
var sizeField = pivotTable.Fields["Size"];
sizeField.Name = "Size";
pivotTable.DataFields.Add(sizeField);
This creates the fields I need for my pivot table/chart and from those this "Values" field in the picture is created.
With interop I did this:
Pivotfield datafield = pivotTable.PivotFields("Daten");
But this does not work with eeplus, since the field "Daten" is not there.
I am trying to something like this, but it also could be that here i need to access the chart by itself?
var datafield = // some code to catch this sum
pivotTable.ColumnFields.Add(datafield);
I finally found an answer to my question. Since this "Values" field is auto generated and i could not access it I tried something else.
Turns out this line:
pivotTable.DataOnRows = false;
did the trick.
Did not research it yet, but I thought I share the answer to my problem.
I've been experimenting with reading SharePoint 2013 Site Column metadata from within a Word 2010 Application-level C# VSTO.
For testing I've set-up Site Columns for every type that SharePoint uses, then created a Document Content Type that ties to them all -- thus all these columns are embedded into the Word document (looks to be stored within customXml within the document file).
By reading from the _Document.ContentTypeProperties property within the VSTO's code, I can access most types, but I'm having difficulty accessing a 'Person or Group' Site Column's data -- I'm getting COM Exceptions attempting to read or write to an item's .Value property.
By looking at the XSD schema in customXml, I can see a single-value User column is made up of three values: DisplayName (type string), AccountType (type string) and AccountId (type UserId) -- however I don't see a way to read/write from/to this within the VSTO? Multi-value User columns appear to be completely different, and are made up of two string values: an ID (appears to be the SharePoint user's ID) and a string-based ID (or at least that's what I think the i:0#.w|domain\userid is, anyway).
Word itself can edit both single- and multi-valued User column data via the Document Panel, but only if Word is currently connected to SharePoint -- otherwise the functionality is disabled. I'd assume the same would be true for the VSTO, if I could access the values at all...
My two questions are:
Is there a way to read/write single- and multi-value User fields from within VSTO code (even if it's not via the _Document.ContentTypeProperties property)?
Is there a way to do Q1 when if not connected to SharePoint (if, say, the values are known to the code)?
(I've been somewhat overly verbose in case my workings so far are useful to someone else even if I get no answers; there doesn't seem to be a great amount of information about this anywhere)
With some provisos, I believe you can do read/update these fields using VSTO - although I haven't actually created a working example using VSTO, the same objects as I'd use in Word VBA are available - the code snippets below are VBA.
The person/group values that are displayed in the DIP are stored in a Custom XML Part, even when the SharePoint server is unavailable. So the problem is not modifying the values - it's a CRUD operation, in essence - but knowing what values you can use, particularly in the multi-valued case. If you know how to construct valid values (let's say you have an independent list of email addresses) then you can make the modifications locally. Personally, I don't know how I would construct a valid value for the multi-valued case so I'd basically have to contact the server.
So assuming you have the data you need to update locally...
When SharePoint serves a Word Document, it inserts/updates several Custom XML Parts. One contains a set of schemas (as you have discovered). Another contains the data. All you really need to do is access the correct Custom XML Part, find the XML Element corresponding to your SharePoint user/group column, then it's a CRUD operation on the subElements of that Element.
You can find the correct Custom XML Part using the appropriate namespace name, e.g.
Const metaPropDataUri as String = _
"http://schemas.microsoft.com/office/2006/metadata/properties"
Dim theDoc as Word.Document
Dim cxp as Office.CustomXMLPart
Dim cxps as Office.CustomXMLParts
Set theDoc = ActiveDocument
Set cxps = theDoc.CustomXMLParts.SelectByNamespace(metaPropDataUri)
If there is more than one part associate with that Namespace, I don't know for sure how to choose the correct one. AFAIK Word/Sharepoint only ever creates one, and experiments suggest that if there is another one, SharePoint works with the first one. So I use
Set cxp = cxps(1)
At this point you need to know the XML Element name of the person/group column. It may be the same as the external name (the one you can see in the SharePoint list), but if for example someone called the Sharepoint column "person group", the Element name will be "person_x0020_group". If the name isn't going to vary, you can get it from the schema XML as a one-off task. Or it may be easy to generate the correct element name from any given SharePoint name. Otherwise, you can get it dynamically from the Schema XML, which you can get (as a string) using
theDoc.ContentTypeProperties.SchemaXML
What you need to do then is find the element with attribute ma:displayName="the external name" and get the value of the name attribute. I would imagine that's quite straightforward using c#, a suitable XML object, and a bit of XPath, say
//xsd:element[#ma:displayName='person group'][1]/#name
which should return 'person_x0020_group'
You can then get the Element node for your data, e.g. something along the lines of
Dim cxn As Office.CustomXMLNode
Set cxn = cxp.SelectSingleNode("//*[name()='person_x0020_group'][1]")
Or you may find it's preferable to get the namespace Uri of the Elements in this Custom XML Part and use that to help you locate the correct node. The name is a long hex string assigned by SharePoint. You can get it from the Schema XML using, e.g.
//xsd:schema[1]/#targetNamespace
Once you have your node, you would use the known structures (i.e. the ones you have found in the Schemas) to get/modify/create child nodes as required.
of course you can. You should use the SharePoint Client-side Object model (CSOM) to manipulate SharePoint data from a location away from the server. The only thing you will need is the URL of your SharePoint site.
You can then connect through CSOM like this:
ClientContext context = new ClientContext("SITEURL");
Site site = context.Site;
Web web = context.Web;
context.Load(site);
context.Load(web);
context.ExecuteQuery();
See here an example to set a single user field:
First get the ID of the user through ensuring the username
u = context.Web.EnsureUser(UserOrGroupName);
context.Load(u);
context.ExecuteQuery();
To set the value, you can use this string format:
userid;#userloginname;#
To set the field use this:
item[myusercolumn] = "userid;#userloginname;#";
item.Update();
context.ExecuteQuery();
To set a multi user field, you can use the same code, just use ;# to concat the different usernames, such as:
item[myusercolumn] = "userid1;#userloginname1;#userid2;#userloginname2;#userid3;#userloginname3;#";
item.Update();
context.ExecuteQuery();
Hope this helps
Howdie,
Having some issues with implementing a video like system in umbraco and was wondering if any uber smart people were willing to make me feel dumb(learn something) and point me in the right direction.
The problem:
As I have edited properties on the documents before I decided to create a custom media type with an int “likes” property. I would then increment this if the user hasn’t liked this video before on post back or disable the button if they have.
I imagined doing something like this:
Document doc = new Document(mediaItemId);
int curValue = doc.getProperty("likes").Value;
doc.getProperty("likes").Value = (curValue + 1);
doc.Save();
http://our.umbraco.org/wiki/reference/api-cheatsheet/modifying-document-properties
The issue arose when I discovered that umbraco treats the document types and media types differently and the code I was using previously (insert code) no longer works.
Been hacking away for some time and the only two possibilities I have left I don't really want to do. The first being to create a new media item, copy over the properties and then "save over" the original in the db, the other is to create a custom table and not worry about the umbraco API.
http://our.umbraco.org/documentation/Reference/management/Media/
I am sure there has to be an easier way to do this (hoping I am being thick).
Thanks for taking the time to read and respond!
you should be able to exactly what you've already done but replace the line:
Document doc = new Document(mediaItemId);
with
Media doc = new Media(mediaItemId);
You will of course have to make sure that your Media type has a "likes" property. This can be done in the "Settings > Media types" section of Umbraco in the same way that you can add properties to document types.
Access added a new data type in the 2007 version--the Attachment type. We are currently working on a WinForms application with .NET 3.5 (C#) that uses an Access 2007 database. We want to be able to add new attachments through the WinForms interface. I can't seem to locate any information on how to insert or select attachment data with .NET. I did try using DAO (version 12) but it didn't seem to have the SaveToFile or LoadFromFile methods discussed here: http://msdn.microsoft.com/en-us/library/bb258184.aspx
So, how can I get at the attachments with .NET?
I finally got this working in C# using a reference to Microsoft.Office.Interop.Access.Dao.
DBEngine dbe = new DBEngine();
Database db = dbe.OpenDatabase("C:\\SomeDatabase.accdb", false, false, "");
Recordset rs = db.OpenRecordset("SELECT * FROM TableWithAttachmentField", RecordsetTypeEnum.dbOpenDynaset, 0, LockTypeEnum.dbOptimistic);
rs.MoveFirst();
rs.Edit();
Recordset2 rs2 = (Recordset2)rs.Fields["AttachmentFieldName"].Value;
rs2.AddNew();
Field2 f2 = (Field2)rs2.Fields["FileData"];
f2.LoadFromFile("C:\\test.docx");
rs2._30_Update();
rs2.Close();
rs._30_Update();
rs.Close();
This will add test.docx to the Attachment field "AttachmentFieldName" in table "TableWithAttachmentField". One thing to note is that attempting to add the same file twice will throw an error.
Interesting question. I don't use A2007, but have the runtime installed, so I used the Access object browser to see what's in there. I discovered something really odd -- there are two FIELD objects, Field and Field2. The attachment functions are members of Field2 but not Field. So, my suggestion would be that perhaps what you need to do is convert this:
Recordset.Fields("FileData").LoadFromFile(<filename>)
to something like this:
Dim rs As DAO.Recordset
Dim fld2 As DAO.Field2
Set rs = CurrentDb.OpenRecordset("[SQL]")
Set fld2 = Recordset.Fields("FileData")
fld2.LoadFromFile(<filename>)
rs.Close
Set fld2=Nothing
Now, I don't know if that will correct the problem for you, but it seems to me that given the two Field objects with different properties/methods/members, you need to be explicit about which Field object you're using. The code example you cite is specifically for use in Access and maybe Access does something to automatically resolve the differences between the two object (perhaps it uses the Field object by default for non-ACCDB databases and the Field2 object for ACCDB files).
Look at this write up on the Access team blog It has basically what David is suggesting with a bit of a twist. It sets a Recordset2 type object equal to the value of the attachment field. Then appends a record to that recordset a puts the file contents in that new record.
I've been struggling to try and do this same thing. There is a reference you can include in your project to "Microsoft.Office.Interop.Access.Dao" that will get you the Recordset2 and Field2 Interfaces, but no implementation classes.
That's about as far as I've gotten, I'll post more once/if I figure it out...
I was not able to get this working in C#, so I moved on to a different solution. I would love to know how to do this though if someone figures it out.