Adding Shapes to a New Visio Document - c#

I have this code that creates a new Visio document and adds a rectangle. It works, but I don't like having to open another document to get the Masters collection from it. The issue is the new document has an empty Masters shape collection. I couldn't find a method in the Document class to add shapes to the Masters collection and all the examples I could find for adding shapes assumed you had an existing document. Is there a better way to do what I want?
// create the new application
Visio.Application va = new Microsoft.Office.Interop.Visio.Application();
// add a document
va.Documents.Add(#"");
// Visio.Documents vdocs = va.Documents;
// we need this document to get its Masters shapes collection
// since our new document has none
Visio.Document vu = vdocs.OpenEx(#"C:\Program Files (x86)\Microsoft Office\Office12\1033\Basic_U.vss", (short)Microsoft.Office.Interop.Visio.VisOpenSaveArgs.visOpenDocked);
// set the working document to our new document
Visio.Document vd = va.ActiveDocument;
// set the working page to the active page
Microsoft.Office.Interop.Visio.Page vp = va.ActivePage;
// if we try this from the Masters collection from our new document
// we get a run time since our masters collection is empty
Visio.Master vm = vu.Masters.get_ItemU(#"Rectangle");
Visio.Shape visioRectShape = vp.Drop(vm, 4.25, 5.5);
visioRectShape.Text = #"Rectangle text.";

You're right - the Masters collection is ReadOnly. Documents normally start off with an empty masters collection. The collection gets populated by dropping masters from a stencil document.
If you want to create a new document with a pre-populated Masters collection then you could create your own template (.vst) and then base your new document on that. For example:
Visio.Document vDoc = vDocs.Add("MyTemplateFile.vst");
Normally you would package your stencils and templates together and then always create shapes by dropping a master from the respective stencil document (.vss).
Masters also have a MatchByName property. Dropping a master when this property is set to true, Visio first checks that a master of the same exists in the drawing document masters collection. If it does an instance of that master will be dropped. If not a new master will be added based on the original stencil. Have a look at these two links for more information:
http://msdn.microsoft.com/en-us/library/aa201768%28office.10%29.aspx
http://msdn.microsoft.com/en-us/library/ff766298.aspx
If you really want to create your own masters in code, you can draw / drop your own shapes on the page and then use the Document.Drop method to add it to the masters collection.
Also if you want to use a master by name then you'll need to loop through the masters collection to check that it exists before you use it.

I think you will find this on-line book extremely useful : http://msdn.microsoft.com/en-us/library/aa245244(v=office.10).aspx

Related

How to insert hyperlink in powerpoint slide using Open XML?

I have a scenario where I have to replace the certain variables from slide template with tabular data but in this case data and slide text is overlapping and after some research I found out that PowerPoint is not designed for such cases [MS Link] (img 1). To overcome this I though instead of replacing the variables with tabular data, I should replace the variable with the link which will point the newly created slide where I can post my tabular data (img 2).
So come back to my question, Is there any way I can write the data without messing the template? OR How can I replace the variable with the hyperlink to the slide?
According to the Documentation you can do something like this, just adjust to your case and how to find the variable1.
// Open the presentation file as read-only.
using (PresentationDocument document = PresentationDocument.Open(fileName, false))
{
// Iterate through all the slide parts in the presentation part.
foreach (SlidePart slidePart in document.PresentationPart.SlideParts)
{
IEnumerable<Drawing.HyperlinkType> links = slidePart.Slide.Descendants<Drawing.HyperlinkType>();
// Iterate through all the links in the slide part.
foreach (Drawing.HyperlinkType link in links)
{
// Iterate through all the external relationships in the slide part.
foreach (HyperlinkRelationship relation in slidePart.HyperlinkRelationships)
{
// If the relationship ID matches the link ID…
if (relation.Id.Equals(link.Id))
{
// Add the URI of the external relationship to the list of strings.
ret.Add(relation.Uri.AbsoluteUri);
}
}
}
}
}

Revit : Get All ViewSheet is very slow

for a plugin I need to get all the viewsheet in the rvt file and display informations from them in an xaml dialog
but my process is very very slow the first time I use it
(with the debuger : 500 ms for 83 viewplan , it is very slow without the debuger too)
(if I execute my code again, the execution is istantaneous)
my code bellow
can you help me ?
thanks in advance
Luc
protected IEnumerable<Element> GetAllEl(Document document)
{
var filteredElementCollector = new FilteredElementCollector(document);
filteredElementCollector = filteredElementCollector
.OfCategory(BuiltInCategory.OST_Sheets)
.WhereElementIsNotElementType()
.OfClass(typeof(ViewSheet));
var fcElements = filteredElementCollector.ToElements();
return fcElements;
}
I do not think there is currently a known generic solution for that problem.
Here is a recent discussion with the development team on this:
Question: for a given element id, we need to find the list of sheet ids displaying it.
Current solution: we loop through all the sheets and views and use FilteredElementCollector( doc, sheet.Id)
With the results from that, we perform one more call to FilteredElementCollector( doc, view.Id) and look for the element id.
Issue: the current solution takes a lot of time and displays a Revit progress bar saying Generating graphics.
Is there any better way to know if a given element id is available in the sheet or not?
For example, something like this would be very useful:
getAllSheets(ElementId) // returns array of sheet id
hasGuid(ElementId,sheetId) // return true/false
Does the API provide any such methods, to check whether a given ElementId is available in the sheet?
Answer: So the goal is to find a view that displays a particular element on a sheet?
Many model elements could be visible on multiple views, while most annotation elements are typically present only in one view.
What type of elements are you checking for?
And what will you do with that info?
Response: the goal is to find a view that displays a particular element on a sheet.
It can be any type of element.
Answer: Here are some previous related discussions:
Determining Views Showing an Element
The inverse, Retrieving Elements Visible in View
Response: The problem is that the first call to FilteredElementCollector( doc, viewId ) shows generating graphics in the progress bar.
Only the first time search does so. The second time, search on the same view has no issues with performance.
Answer: The first time is slow because in order to iterate on the elements visible in a view the graphics for that view must be generated.
I can't think of a workaround to get a precise answer.
You might be able to skip sheets which don't have model views in their viewport list to save a bit of time.
Some sheets may only have drafting views and schedules and annotations.
The development team provided a very helpful suggestion which helped work around the generating graphics call in a special case,
to Loop through sheets - generating graphics.
Maybe you can optimise in a similar manner for your specific case?
I think you may be over-filtering the ElementCollector. In my add-in, I just use this code to get the view sheets: new FilteredElementCollector(_doc).OfClass(typeof(ViewSheet));

Save RichTextBox content containing both images and custom token elements

Struggling with how to successfully save and load my System.Windows.Controls.RichTextBox content containing all of the following: formatted text, images, custom type-defined token elements, custom dynamic token elements.
By token elements I mean my custom classes inheriting from System.Windows.Documents.Run where type-defined is such that does not need to remember any dynamically set property values (since action is taken based on the type which needs to be remembered after loading) and dynamic ones are such that need to also retain dynamically set properties (action is taken not only based on type, but also based on these set values).
I know of the following 3 methods to save/load, neither of which is sufficient:
1)
string xamlStream = System.Windows.Markup.XamlWriter.Save(myRichTxtBx.Document);
and then saving the string
2)
TextRange content = new TextRange(myRichTxtBx.Document.ContentStart, myRichTxtBx.Document.ContentEnd);
content.Save(myFileStream, DataFormats.XamlPackage, true);
3)
TextRange content = new TextRange(myRichTxtBx.Document.ContentStart, myRichTxtBx.Document.ContentEnd);
content.Save(myFileStream, DataFormats.Xaml, true);
These are the problems with those:
1) unable to load image after restarting the application (but remembers properties)
2) does not remember the properties (but is able to load image after restarting the app)
3) won't load image not even in the same instance of the app and also does not remember the property values
I could only find answers resolving image saving issues (2) or property issues (1), but never both.
The goal is to have a tokenizable RichTextBox, where tokens are either replaced by values from database based on provided ORM object (= type-defined token) or by dynamically set values by user based again on a provided ORM object.
I have solved the issue by a very ugly workaround:
To save the document I use the method (1) described above. Before this, I traverse the FlowDocument by a custom walker and replace each image element with a custom inline token element (much like the other tokens). A hash ID is assigned as a property to this substitute element and the image itself is saved having the hash as its file name (serves to identify token with the image file). Images, along with the main document (saved by method (1)), are packaged into a single file.
When loading everything back, package is unpacked, document loaded keeping the tokens with their properties and substitute image elements are replaced by the actual images from the files saved in the package using once again the before-mentioned custom walker and the established hash token-file relation.

Any way to get the position of an AcroField in PdfSharp without "/Rect"?

I am developing a program with C# and the library PDFSharp.
I am currently using the following code to get the X and Y coordinates of a specific AcroField in a PDF document:
PdfTextField imageField = (PdfTextField)inForm.Fields[elementName];
PdfRectangle rect = imageField.Elements.GetRectangle(PdfAnnotation.Keys.Rect);
This works fine if there is only 1 Field with the same name present in the PDF Document. However, if there are two fields both named "FirstName", even if they are on separate pages, this seems to remove the "/Rect" and "/P" flags, so I cannot use these to find the position or the page relevant to that field.
Is there any other way to get the position of a Field in the PDF, or any way to activate the "/Rect" and "/P" flags?
Thanks, RBrNx
What Mihai posted fits what I discovered from reverse engineering the PDF via PdfSharp. If there are multiple fields in the same document, they are nested under a parent container, and it is a reference to this parent container which PdfSharp will give you when using the AcroForm.Fields accessor. To get the Page and Rectangle elements for each field, you have to look at the children of that container.
To get the values you are looking for, you'll want to do something like this:
PdfTextField imageField = (PdfTextField)inForm.Fields[elementName];
var fieldRectangles = new List<PdfRectangle>();
if( imageField.HasKids )
{
PdfArray kids = (PdfArray) Elements[Keys.Kids];
foreach( var kid in kids )
{
var kidValues = ((PdfReference) kid).Value as PdfDictionary;
var rectangle = kidValues.Elements.GetRectangle(PdfAnnotation.Keys.Rect);
fieldRectangles.Add(rectangle);
}
}
The page reference element ("/P" tag) is also available from these "Kid" elements.
I'm not familiar with PDFSharp API but this is how it works in PDF:
- form fields have document scope and not page scope.
- 2 or more fields with the same name are in fact a single field with 2 or more widgets (widget annotations, the visual representation of a field). The /Rect and /P entries are stored at widget level. When the field has one widget, the widget is merged with the field so the /Rect and /P entries appear to be part of the field.
In your scenario you have to look for the /Kids key which is an array. Drill down through the /Kids array (a child can have his own kids and so on) till the last level where the /Kids is no longer present. At this level you should find the /Rect and /P keys.
Each widget can have its own /Rect and /P keys since they can appear on different pages at different positions.

Add dynamic table row using microsoft work automation c#

I have a word document template which contains several form fields that need to be filled using c# code.
below is the document image
The code below is used to reach and fill the document form fields,
But when i reach the table sections sometimes the rows need to be filled are more than what is pre defined inside the template.
red marked area is the table which i want to fill it with data and create as many rows as needed.
the code i use for filling the data is
string fileName = Path.GetTempFileName();
File.WriteAllBytes(fileName, Properties.Resources.DocumentTemplate);
Word.Application word = new Word.Application();
Word.Document doc = new Word.Document();
doc = word.Documents.Add(fileName);
doc.Activate();
doc.FormFields["file_num"].Range.Text = "some text";
doc.FormFields["fam_size"].Range.Text = "another text";
doc.FormFields["nationality"].Range.Text = "another text";
for(int i =0; i< rowsInDatabaseCount; i++)
{
//i don't know how to add row and reach each form field inside
}
I hope someone can help me on how to achieve what i want.
Thank you in advance...
There are multiple ways to handle that.
1) Since the data is coming from a database, one way is to use InsertDatabase method.
2) You could insert the block as tab or comma separated text then convert to a table using ConvertToTable() method.
3) You might use Rows and Cols collections (of Table) and .Add method to add new rows.
4) You might instead create your template as an XSL and transform data (XML) using that XSL to generate final HTM. Word is able to open an HTM file.
(All these options worked for me in "resume", "test results" ... creations which have similar layouts to the ones you gave. I found final option 4 to be the most dynamic one, where end users could define their own templates using simple HTML editors - worked better than word templates for me).

Categories