Import a shape from Visio VSSX document - c#

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);

Related

TestStack White performance slow and speed up

I am using TestStack White to automate a test scenario in a Telerik Winforms application.
The application has plenty of elements and if I directly search for an element, the run would just stale and never end.
So I did the manual hierarchy search to dig into element levels to save the performance and make it work.
Then the code looks in a not neat way as I heavily use foreach to do the loop myself.
Is this the proper way to cope with the performance or we have better ways to
have both neat code and good performance when using TestStack White?
Cheers:
[TestMethod]
public void TestMethod1()
{
CoreAppXmlConfiguration.Instance.RawElementBasedSearch = true;
CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 2;
var applicationDirectory = "D:\\Software\\ABC Handling System";
var applicationPath = Path.Combine(applicationDirectory, "ABCClient.GUI.exe");
Application application = Application.Launch(applicationPath);
Thread.Sleep(5000);
Window window = application.GetWindow("[AB2] - ABC Handling System 2 - [Entity Search]", InitializeOption.NoCache);
ListBox listbox = window.Get<ListBox>();
ListItem dispatchButton = listbox.Items.Find(i=>i.Name.Equals("Display"));
dispatchButton.Click();
Thread.Sleep(5000);
SearchCriteria sc = SearchCriteria.ByControlType(ControlType.Pane).AndIndex(3);
UIItemContainer groupbox = (UIItemContainer)window.MdiChild(sc);
UIItemContainer pane1 = null;
foreach (IUIItem automationElement in groupbox.Items)
{
if (automationElement.Name == "radSplitContainer1")
{
pane1 = (UIItemContainer)automationElement;
break;
}
}
UIItemContainer pane2 = null;
foreach (IUIItem automationElement in pane1.Items)
{
if (automationElement.Name == "splitPanel1")
{
pane2 = (UIItemContainer)automationElement;
break;
}
}
Table table = null;
foreach (IUIItem automationElement in pane2.Items)
{
if (automationElement.Name == "Telerik.WinControls.UI.RadGridView ; 247;14")
{
table = (Table)automationElement;
break;
}
}
TableRow row = table.Rows[9];
string s = row.ToString();
}
Update on 05/11/2019:
It turned out TestStack White is not the best solution for my multi-row grid app regarding its performance.
Actually we managed to have developed something from the grid side by developers, and we hook those functions up. So we were using AutoIt + Customized Application side hooks. Some really good controls there.
Yes, we succeeded in this approach.
Yeah instead of doing the looping yourself you would just use the Get on each of the retrieved elements. Oddly when I dig down through the code it is using AutomationElement.FindFromPoint which I am not sure how it traverses the tree to find it's elements. I would give the follow code a try and see if it is more or less performant in your application.
CoreAppXmlConfiguration.Instance.RawElementBasedSearch = true;
CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 2;
var applicationDirectory = "D:\\Software\\ABC Handling System";
var applicationPath = Path.Combine(applicationDirectory, "ABCClient.GUI.exe");
Application application = Application.Launch(applicationPath);
Thread.Sleep(5000);
Window window = application.GetWindow("[AB2] - ABC Handling System 2 - [Entity Search]", InitializeOption.NoCache);
ListBox listbox = window.Get<ListBox>();
ListItem dispatchButton = listbox.Get<ListItem>(SearchCriteria.ByText("Display"));
dispatchButton.Click();
Thread.Sleep(5000);
UIItemContainer groupbox = window.MdiChild(SearchCriteria.ByControlType(ControlType.Pane).AndIndex(3));
UIItemContainer pane1 = groupbox.Get<UIItemContainer>(SearchCriteria.ByText("radSplitContainer1"));
UIItemContainer pane2 = pane1.Get<UIItemContainer>(SearchCriteria.ByText("splitPanel1"));
Table table = pane2.Get<Table>(SearchCriteria.ByText("Telerik.WinControls.UI.RadGridView ; 247;14")); ;
TableRow row = table.Rows[9];
string s = row.ToString();
I am getting everything from the previous element in an attempt to limit the portion of the tree it is scanning. From the looks of White's code this may have no affect other than reducing the amount of boiler plate on the front end depending on the performance of AutomationElement.FindFromPoint.
Also the ByText search criteria maps to the name property on a AutomationElement by calling CreateForName which is why I replaced all the checks against name with ByText.

Screenshots of multiple actionbar tabs

I am using Xamarin and C# but I suspect the problem is equally valid in a Java environment.
I have an ActionBar Activity that hosts three tabs each of which hosts a fragment. It uses a ViewPager to allow the user to swipe between the tabs.
The requirement is to programmatically screenshot each tab and then email these as attachments.
The problem is that whilst the ActionBar/ViewPager works well it also optimises the tabs - effectively it isn't creating a fragment's view until it is next in line to be shown. So, if you're on tab 0 - the first tab - then the fragment view for tab 2 is null. So it can't be screenshot.
To overcome this I have tried to set any tab/fragment that has a null view to be selected. This generates the view but because setting it to be selected does not actually render it on screen the view does not have a width or a height value so again it cannot be screenshot (this is the reason for the defensive check at the start of the code taking the screenshot).
So, I guess my question is how can I force the tab to be rendered on screen so that it is correctly filled out and can be screenshot?
My main code extracts are as follows:
private void EmailReport()
{
List <Bitmap> bitmaps = new List<Bitmap>();
List <string> summaryFiles = new List<string>();
// remember the tab we're on
var selectedTab = this.ActionBar.SelectedNavigationIndex;
// take the screenshots
for (int fragmentNumber = 0; fragmentNumber < projectFragmentPagerAdapter.Count; fragmentNumber++)
{
Android.Support.V4.App.Fragment fragment = projectFragmentPagerAdapter.GetItem(fragmentNumber);
if (fragment.View == null)
{
this.ActionBar.GetTabAt(fragmentNumber).Select();
fragment = projectFragmentPagerAdapter.GetItem(fragmentNumber);
}
bitmaps.Add(ScreenShot(fragment.View));
}
// set the active tab back
this.ActionBar.GetTabAt(selectedTab).Select();
//write the screenshots into file
int i = 0;
foreach(Bitmap bitmap in bitmaps)
{
if (bitmap != null)
summaryFiles.Add(BitmapToFile(bitmap, this.ActionBar.GetTabAt(i).Text));
i++;
}
// now send the file
EmailSupport.SendAttachments(this, summaryFiles);
}
private Bitmap ScreenShot(View fragmentRootView)
{
if (fragmentRootView == null || fragmentRootView.Width == 0 || fragmentRootView.Height == 0)
return null;
fragmentRootView.DrawingCacheEnabled = true;
//create a bitmap for the layout and then draw the view into it
Bitmap bitmap = Bitmap.CreateBitmap(fragmentRootView.Width, fragmentRootView.Height,Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
//Get the view's background
Drawable bgDrawable = fragmentRootView.Background;
if (bgDrawable!=null) // has background drawable, then draw it on the canvas
bgDrawable.Draw(canvas);
else // does not have background drawable, then draw white background on the canvas
canvas.DrawColor(Color.White);
// draw the view on the canvas
fragmentRootView.Draw(canvas);
fragmentRootView.DrawingCacheEnabled = false;
return bitmap;
}
Any help would be gratefully received.
The solution in the end was very simple. The ViewPager has a setting controlling the number of pages (fragments) that it will hold "activated". This defaults to 1. As I had 3 tabs this meant there was always one tab (fragment) out of reach.
So, whilst setting up the ViewPager do the following before the tabs are added:
reportViewPager.OffscreenPageLimit = pageCount - 1;
Or in Java
reportViewPager.setOffscreenPageLimit(pageCount - 1);
I hope this helps someone else avoid wasting hours.

auto arrange visio shapes through C#

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:

Create a VS2010 Addin to collapse every methods of my active document

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

Problem with Printing Content Of RichTextBox with Adorner Layers

I am trying to print the content of RichTextBox including the Adorner Layers inside.
I am using this code to print
double w = Editor.ExtentWidth; // Editor is the RichTextBox
double h = Editor.ExtentHeight;
LocalPrintServer ps = new LocalPrintServer();
PrintQueue pq = ps.DefaultPrintQueue;
XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(pq);
PrintTicket pt = pq.UserPrintTicket;
if (xpsdw != null)
{
pt.PageOrientation = PageOrientation.Portrait;
PageMediaSize pageMediaSize = new PageMediaSize(w, h);
pt.PageMediaSize = pageMediaSize;
xpsdw.Write(Editor);
}
The problem I'm facing is that this code only prints the content that is visible on the screen, not the whole content of the Editor.
EDIT
The pictures are adorner layers, If I print using the method above, it only prints the visible part on the screen not the whole document.
Edit
I'm trying to print each page separately but I cant force Editor.InvalidateVisual(); after doing a Editor.PageDown(); Is there a way I can do that in my method ?
When controls draw on the adorner layer, they search up the tree until they find an adorner layer. Often times this is a the window level. In some cases, you'll want an adorner layer closer to the control, or directly around the control. In this case, wrap the control with an <AdornerDecorator><RichTextBox /></AdornerDecorator>
In your case, you'd probably want to pass a parent element of adorner decorator, or the decorator itself to the print logic. This way the print logic would include the adorner layer as part of the visual. Maybe something like this:
<Grid Name="EditorWrapper">
<AdornerDecorator>
<RichTextBox />
</AdornerDecorator>
</Grid>
Then, pass "EditorWrapper" to the print logic.
EDIT
If you just want to print the contents of the RichTextBox, then you might be best to use the built-in pagination capabilities of the FlowDocument. FlowDocument implements IDocumentPaginatorSource, which will return a paginator that can print the document. Pass that paginator to the XpsDocumentWriter and it should dump the content properly.
var doc = Editor.Document;
var src = doc as IDocumentPaginatorSource;
var pag = src.DocumentPaginator;
xpsdw.Write(pag);
I found this code here:
// Serialize RichTextBox content into a stream in Xaml or XamlPackage format. (Note: XamlPackage format isn't supported in partial trust.)
TextRange sourceDocument = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
MemoryStream stream = new MemoryStream();
sourceDocument.Save(stream, DataFormats.Xaml);
// Clone the source document's content into a new FlowDocument.
FlowDocument flowDocumentCopy = new FlowDocument();
TextRange copyDocumentRange = new TextRange(flowDocumentCopy.ContentStart, flowDocumentCopy.ContentEnd);
copyDocumentRange.Load(stream, DataFormats.Xaml);
// Create a XpsDocumentWriter object, open a Windows common print dialog.
// This methods returns a ref parameter that represents information about the dimensions of the printer media.
PrintDocumentImageableArea ia = null;
XpsDocumentWriter docWriter = PrintQueue.CreateXpsDocumentWriter(ref ia);
if (docWriter != null && ia != null)
{
DocumentPaginator paginator = ((IDocumentPaginatorSource)flowDocumentCopy).DocumentPaginator;
// Change the PageSize and PagePadding for the document to match the CanvasSize for the printer device.
paginator.PageSize = new Size(ia.MediaSizeWidth, ia.MediaSizeHeight);
Thickness pagePadding = flowDocumentCopy.PagePadding;
flowDocumentCopy.PagePadding = new Thickness(
Math.Max(ia.OriginWidth, pagePadding.Left),
Math.Max(ia.OriginHeight, pagePadding.Top),
Math.Max(ia.MediaSizeWidth - (ia.OriginWidth + ia.ExtentWidth), pagePadding.Right),
Math.Max(ia.MediaSizeHeight - (ia.OriginHeight + ia.ExtentHeight), pagePadding.Bottom));
flowDocumentCopy.ColumnWidth = double.PositiveInfinity;
// Send DocumentPaginator to the printer.
docWriter.Write(paginator);
}
Adorner layers are are drawing oriented. So one option left is to convert the entire RichTextBox into a drawing and print that as an Image in XPS.
Although this poses multiple issues...
It will print the external and internal contents that occupy or occupied by the richtextbox i.e. editor toolbar (if it is part of the control template of the rich text box), internal scroll bars etc.
If there are scrollbars then the content out of the scrollbars are not going to be printed as the image will be the exact "snapshot" of the textbox (with remaining text clipped by srollbars).
Will you be happy with that?
I didn't find any way that works 100% for this problem, so I'm trying to transform all my adorner layers to actual images. I'll update the question once I get a 100% working solution.

Categories

Resources