I am using Microsoft Visio as a COM object in my C# application. I want to auto arrange shapes on a Visio page. What should I code for this task? The shapes are database entities.
userView.Shapes.SomeMethod();
userView is name of COM object but what should SomeMethod be?
I know this is an 'older' question, but
I'm working on something quite similar, and have managed to 'Auto-Layout' a flow chart with the following code:
public enum GraphStyles { TopDown, LeftRight };
public void ArrangeGraph(GraphStyles Style)
{
if (Style == GraphStyles.TopDown)
{
// set 'PlaceStyle'
var placeStyleCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOPlaceStyle).ResultIU = 1;
// set 'RouteStyle'
var routeStyleCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLORouteStyle).ResultIU = 5;
// set 'PageShapeSplit'
var pageShapeSplitCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOSplit).ResultIU = 1;
}
else if (Style == GraphStyles.LeftRight)
{
// set 'PlaceStyle'
var placeStyleCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOPlaceStyle).ResultIU = 2;
// set 'RouteStyle'
var routeStyleCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLORouteStyle).ResultIU = 6;
// set 'PageShapeSplit'
var pageShapeSplitCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOSplit).ResultIU = 1;
}
else { throw new NotImplementedException("GraphStyle " + Style.ToString() + " is not supported"); }
VisApp.ActivePage.Layout();
}
Hopefully this saves someone some time.
It took me awhile to figure it out.
I'm using visio 2010 and visual studio 2010
This might help
Pertinent quote
To lay out a subset of the shapes of a page, master, or group,
establish a Selection object in which the shapes to be laid out are
selected, and then call the Layout method. If the Layout method is
performed on a Selection object and the object has no shapes selected,
all shapes in the page, master, or group of the selection are laid
out.
EDIT: noonand 2012-09-21 Added information about the LayoutIncremental method
Just had another look at the object model and it appears the method you want is the LayoutIncremental method
Excerpt from the relevant help topic says:
Page.LayoutIncremental(AlignOrSpace, AlignHorizontal, AlignVertical, SpaceHorizontal, SpaceVertical, UnitsNameOrCode)
I needed to do something similar a while ago..
I used Microsofts Glee library for the layout. There are very good samples included with the download which show you how to add nodes and relations and make them "auto arrange". However do note that Glee is not free for commercial use.
http://research.microsoft.com/en-us/downloads/f1303e46-965f-401a-87c3-34e1331d32c5/default.aspx
And then I used this example for converting the calculated positions from Glee to a Visio drawing.
http://www.syntaxwarriors.com/2012/generating-visio-uml-diagrams-from-c/
Basically what I do is add all my nodes and relations too Glee and then get a list of nodes and their positions and add them to Visio using the second link.
Here is a graph example of what Glee can do:
Related
I have a Visio stencil document with some shapes and I want to add a shape contained inside it to my document. Based on this example I was able to do it, the only issue is how to get rid of the dock panel which appears when opening stencil using Microsoft.Office.Interop.Visio.VisOpenSaveArgs.visOpenDocked flag.
So after import I close the opened stencil document but the dock panel stays. Maybe I could close it programmatically too, but then I should consider complicated logic with tracking wheater this was opened or not to keep UI unchanged if the user opened this panel previously etc.
My question is there another option to import a shape from a stencil or a workaround for this panel and stencil document opening options (for instance to open stencil document hidden for user and close it afterwards silently)
// Microsoft.Office.Interop.Visio.Application Application
var documents = Application.Documents;
var document = documents.Add("");
var page = Application.ActivePage;
var visioStencil = documents.OpenEx(
#"c:\Users\user\Desktop\stencil.vssx",
(short)Microsoft.Office.Interop.Visio.VisOpenSaveArgs.visOpenDocked);
var masters = visioStencil.Masters;
for (var i = 1; i <= masters.Count; ++i)
{
var item = masters.get_ItemU(i);
var name = item.Name;
if (name == "Master.2")
{
page.Drop(item, 10, 10);
break;
}
}
visioStencil.Close();
You can open the stencil document in a 'hidden' state and also use the Masters.Drop method to add directly to the target masters collection like this:
var targetDoc = vApp.Documents.Add("");
var sourceDoc = vApp.Documents.OpenEx(
#"c:\Users\user\Desktop\stencil.vssx",
(short)Microsoft.Office.Interop.Visio.VisOpenSaveArgs.visAddHidden);
var sourceMasters = sourceDoc.Masters;
for (var i = 1; i <= sourceMasters.Count; ++i)
{
var sourceMaster = sourceMasters[i];
if (sourceMaster.Name == "Master.2")
{
targetDoc.Masters.Drop(sourceMaster, 10, 10);
break;
}
}
sourceDoc.Close();
Note that the if the target document already contains a master of the same name Visio will create a new master and append a number on the end. Also, bear in mind that Name and NameU may be different so you might want to match on the latter instead.
No need to loop through all the shapes in the stencil. You can access the shape by name:
targetDoc.Masters.Drop(sourceMasters["Master.2"], 10, 10);
The code works for Word and Outlook but fails with PowerPoint in that only the first character or first word of the textbox ever gets selected. Is this a bug? Is there any workaround? Try this on a simple PowerPoint slide in PowerPoint 2013.
private static async Task<string> getText(double x, double y)
{
string result = null;
try
{
var location = new System.Windows.Point(x, y);
AutomationElement element = AutomationElement.FromPoint(location);
object patternObj;
if (element.TryGetCurrentPattern(TextPattern.Pattern, out patternObj))
{
var textPattern = (TextPattern)patternObj;
var range = textPattern.RangeFromPoint(location);
range.ExpandToEnclosingUnit(TextUnit.Word);
range.Select();
var text = range.GetText(-1).TrimEnd('\r');
return text.Trim();
}
else
{
return "no text found";
}
}
catch (Exception ex)
{
return ex.Message;
}
}
You cannot see it from the screenshot, but the mouse is on "first" not "stuck", but regardless of where the mouse is placed, it always is stuck. Maybe this is fixed in PowerPoint 2016?
When I look at the bounding box for the range it is always the whole element, rather than the selected word. That could be part of the problem of why RangeToPoint is not working.
Original posted in MSDN but no response...
Update. If I use
text = printRange(range, text);
while (range.Move(TextUnit.Word, 1) > 0)
{
text += Environment.NewLine;
text = printRange(range, text);
}
I get
This behavior is probably due to a limitation in PowerPoint 2013, and I expect you can't work around it using UIA. When you call RangeFromPoint(), the UIA provider hit beneath the mouse, (ie the one that's implementing IUIAutomationTextPattern::RangeFromPoint(),) is meant to return a degenerative (ie empty) range where the mouse cursor is. Then the UIA client can expand the returned range to get the surrounding character, word, line or paragraph.
However, as you point out, PowerPoint 2013 isn't doing that. I've just written the test code below, (using a managed wrapper for the native Windows UIA API generated by tlbimp.exe,) and found that PowerPoint apparently returns a TextRange for the entire text box beneath the cursor. When I ran the code, I found that I did get the expected word beneath the cursor in WordPad, Word 2013 and PowerPoint OnLine, but not PowerPoint 2013. I got the same results when I ran the Text Explorer tool that's part of the Inspect SDK tool. The image below shows Text Explorer reporting that the text returned from PowerPoint 2013 is the entire text in the a text box, when the mouse is hovering over one of those words.
(I should add that for the test code below to work at all, I think the current display scaling setting needs to be at 100%. I've not added code to account for some other scaling being active.)
I don't know if this is fixed in PowerPoint 2016, I'll try to look into that and let you know.
Thanks,
Guy
private void buttonGetTheText_Click(object sender, EventArgs e)
{
labelText.Text = "No text found.";
IUIAutomation uiAutomation = new CUIAutomation8();
Point ptCursor = Cursor.Position;
tagPOINT pt;
pt.x = ptCursor.X;
pt.y = ptCursor.Y;
// Cache the Text pattern that's available through the element beneath
// the mouse cursor, (if the Text pattern's supported by the element,) in
// order to avoid another cross-process call to get the pattern later.
int patternIdText = 10014; // UIA_TextPatternId
IUIAutomationCacheRequest cacheRequestTextPattern =
uiAutomation.CreateCacheRequest();
cacheRequestTextPattern.AddPattern(patternIdText);
// Now get the element beneath the mouse.
IUIAutomationElement element =
uiAutomation.ElementFromPointBuildCache(pt, cacheRequestTextPattern);
// Does the element support the Text pattern?
IUIAutomationTextPattern textPattern =
element.GetCachedPattern(patternIdText);
if (textPattern != null)
{
// Now get the degenerative TextRange where the mouse is.
IUIAutomationTextRange range = textPattern.RangeFromPoint(pt);
if (range != null)
{
// Expand the range to include the word surrounding
// the point where the mouse is.
range.ExpandToEnclosingUnit(TextUnit.TextUnit_Word);
// Show the word in the test app.
labelText.Text = "Text is: \"" + range.GetText(256) + "\"";
}
}
}
I can suggest only Python code getting caption text of the slide (for example). Sorry, I have no time to re-write it on C#. You can play with the PowerPoint.Application COM object and MSDN example of Power Point automation.
from __future__ import print_function
import win32com.client as com
pp = com.Dispatch('PowerPoint.Application')
print(pp.Presentations[0].Slides[8].Shapes[0].TextFrame.TextRange.Text)
This may be a noobish question, but in my records in Coded UI Tests, I have recorded a lot of controls that don't have enough defined properties to be found in playback.
For exemple:
public HtmlEdit UIItemEdit
{
get
{
if ((this.mUIItemEdit == null))
{
this.mUIItemEdit = new HtmlEdit(this);
#region Search Criteria
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.Id] = null;
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.Name] = null;
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.LabeledBy] = null;
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.Type] = "SINGLELINE";
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.Title] = null;
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.Class] = null;
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.ControlDefinition] = "type=\"text\" value=\"\"";
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.TagInstance] = "5";
this.mUIItemEdit.WindowTitles.Add("http://cms.home.psafe.com/");
#endregion
}
return this.mUIItemEdit;
}
In this post, I learned about SearchProperties, but it doesn't look to be an appropriate solution in this case.
Is there any other way to wrap these controls properly?
You might be able to find it if its containing element can be found. You can use the containing element to scope the search. So, find that element's parent, then find an input type=text within it:
var container = new HtmlControl(bw); //where bw is the browser window
HtmlDiv parentDiv = new HtmlDiv(container);
parentDiv.SearchProperties[HtmlDiv.PropertyNames.Id] = "theIdOfYourDiv";
HtmlEdit edt = new HtmlEdit(parentDiv); //the search scope is narrowed down to the div only. This may be enough to find your control with the search property.
edt.SearchProperties[HtmlEdit.PropertyNames.Type] = "SINGLELINE";
You have two options:
Try crowcoder's solution of searching in the parent. The problem with this solution is when you move a control around you're going to be changing code a lot.
Add an Id property to all your controls in the HTML, this will make your Coded UI more robust and responsive to changes in the UI.
I'm looking for the source code to collapse every methods of my active document using the VS2010 Addin.
For the moment I parse the text content of the document trying to match if the line is a method signature. If it is the case, I collapse the method.
TextSelection selection = (TextSelection)_applicationObject.ActiveDocument.Selection;
var editPoint = selection.ActivePoint.CreateEditPoint();
editPoint.MoveToLineAndOffset(1, 1);
while (!editPoint.AtEndOfDocument)
{
editPoint.StartOfLine();
var line = editPoint.GetText(editPoint.LineLength).TrimStart();
if (line.StartsWith("public"))
{
selection.MoveToLineAndOffset(editPoint.Line, 1);
_applicationObject.ExecuteCommand("Edit.ToggleOutliningExpansion");
}
// go to the next line
}
Does anyone could tell me if I'm on the good way or if there is an easiest way ?
Maybe I asked not so well my question. My real goal was to collapse all the code : properties, methods, comments with ///, using; but not the regions.
Here is one solution :
// reduce everything like Ctrl+M+O
_applicationObject.ExecuteCommand("Edit.CollapsetoDefinitions");
// save the cursor position
TextSelection selection = (TextSelection)_applicationObject.ActiveDocument.Selection;
var selectedLine = selection.ActivePoint.Line;
var selectedColumn = selection.ActivePoint.DisplayColumn;
// open the regions
selection.StartOfDocument();
while (selection.FindText("#region", (int)vsFindOptions.vsFindOptionsMatchInHiddenText))
{
// do nothing since FindText automatically expands any found #region
}
// put back the cursor at its original position
selection.MoveToDisplayColumn(selectedLine, selectedColumn);
I hope this could help
I am developing a C# application to communicate with a MUD/MOO server. Basically it takes text and displays it on the screen. At the moment i'm using a RichTextBox to display the text and I have coloured text working fine and I only have to implement URLs, however while doing this I discovered that to add URLs with custom text (e.g. NOT http://, like: Click here) I need to use a Win32 API, I can't do this... at all. This needs to work in mono on linux (and possibly mac). Is there anyway to make this work? or what alternative avenues should i pursue? (I was considering switching to HTML, but is there a good cross-platform HTML control?) (All of this HAS to be free).
Thanks in advanced.
I managed to do it in the end using:
Stack<Link> Links = new Stack<Link>();
internal class Link
{
public int starts = 0;
public int ends = 0;
public string url = "";
}
private string GetLink(RichTextBox rtb, Point point)
{
int index = rtb.GetCharIndexFromPosition(point);
foreach (Link link in Links.ToArray())
if (link.starts <= index && link.ends >= index)
return link.url;
return "";
}
just append all links to the Links stack, and use GetLink inside the MouseDown event :)
as an alternative you can use GtkTextView, it should be cross platform. You can add a hyper link styled text using code below:
TextTag tag = new TextTag("link");
tag.Foreground = "blue";
tag.Underline = Pango.Underline.Single;
textview1.Buffer.TagTable.Add(tag);
Gtk.TextIter iter = textview1.Buffer.GetIterAtOffset(0);
textview1.Buffer.InsertWithTagsByName(ref iter, "link text", "link");
where textview1 is Gtk.TextView.
you should be able to change the cursor and react on mouse clicks using "motion-notify-event" and "event-after" events.
hope this helps, regards