How do you call "Document Format" programmatically from C#? - c#

I'm writing a simple VS add-in and would like to programmatically invoke the "Document Format" option (under Edit) within code. Google isn't being very friendly to me today....

Command cmd = _applicationObject.Commands.Item("Edit.FormatDocument", -1);
object dummy = null;
_applicationObject.Commands.Raise(cmd.Guid, cmd.ID, ref dummy, ref dummy);

If you have a reference to your document (of type Window), and you have a reference to the _DTE object, you can call it like this:
myDocument.Activate();
myDTE.ExecuteCommand("Edit.FormatDocument", string.Empty);
Most of the time, you can get a reference to the _DTE object from the parameters passed into your add-in.

You'll need to use the standard command editors, called with the VSStd2KCmdId.FORMATDOCUMENT command enumeration.

Related

How to set current cell on SAP GUIContainerShell in C#?

I am automating my work with SAP GUI script at the moment and whilst trying to recreate the recorded macro I am having an issue at one particular point which I don't know how to translate.
session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell").setCurrentCell 1,"MAKTX2"
session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell").doubleClickCurrentCell
session.findById("wnd[1]/tbar[0]/btn[0]").press
I have read through the SAP GUI Scripting API pdf and am struggling to see how I action the .setCurrentCell 1,"MAKTX2" part. I am accessing the container cell with the following:
GuiContainerShell materials = (GuiContainerShell)session.FindById("wnd[0]/shellcont/shell/shellcont[1]/shell");
How do I make "materials" double click "MAKTX2"?
Edit: Full SAP GUI script:
SapROTWr.CSapROTWrapper sapROTWrapper = new SapROTWr.CSapROTWrapper();
object SapGuilRot = sapROTWrapper.GetROTEntry("SAPGUI");
object engine = SapGuilRot.GetType().InvokeMember("GetScriptingEngine", System.Reflection.BindingFlags.InvokeMethod, null, SapGuilRot, null);
GuiApplication GuiApp = (GuiApplication)engine;
GuiConnection connection = (GuiConnection)GuiApp.Connections.ElementAt(0);
GuiSession session = (GuiSession)connection.Children.ElementAt(0);
GuiFrameWindow frame = (GuiFrameWindow)session.FindById("wnd[0]");
GuiTextField jobsite = (GuiTextField)session.FindById("wnd[0]/usr/subSA_0100_1:SAPMZCX_CSDSLSBM5001_OFS_OTS:2410/subSA_2410_1:SAPMZCX_CSDSLSBM5001_OFS_OTS:2510/ctxtKUWEV-KUNNR");
jobsite.Text = "I033";
frame.SendVKey(0);
GuiLabel aggregates = (GuiLabel)session.FindById("wnd[1]/usr/lbl[12,3]");
aggregates.SetFocus();
GuiFrameWindow frame2 = (GuiFrameWindow)session.FindById("wnd[1]");
frame2.SendVKey(1);
GuiContainerShell materials = (GuiContainerShell)session.FindById("wnd[0]/shellcont/shell/shellcont[1]/shell");
To be honest I can't help you with C#, but perhaps the SAP interface is generic enough anyway. Thing is, session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell") gives you a reference to an object of type GuiShell or GuiContainerShell or whatever it's called. On this reference, you can call the methods defined for this type. So in the same way, when you do
session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell").setCurrentCell 1,"MAKTX2"
You're just getting the reference first, and then applying the method setCurrentCell on it, all on the same line.
When you did in C#
GuiContainerShell materials = (GuiContainerShell)session.FindById("wnd[0]/shellcont/shell/shellcont[1]/shell");
you gave this reference a name materials, and provided that line works correctly, I guess you can just say now:
materials.setCurrentCell(1, "MAKTX2")
materials.doubleClickCurrentCell

IVsDropdownBarClient and GetEntryText: Problems with text buffers

In my Visual Studio extension, I'm going to read the text that is in the navigation bar. Therefore I listen to window created events and from the IVsCodeWindow object I get the IVsDropdownBar to get the current selection in the dropdown bar. This works fine. Then I'm using the following code snippet to extract the text of the current selection:
string text;
barClient.GetEntryText(MembersDropdown, curSelection, out text);
if (hr == VSConstants.S_OK)
{
Debug.WriteLine("Text: " + text);
} else {
Debug.WriteLine("No text found!");
}
However, this does not work. My extension crashes with an unhandled exception in the second line of the code snippet. I read the documentation and could find the following note:
The text buffer returned in ppszText is typically created by the
IVsDropdownBarClient object and the buffer must persist for the life
of the IVsDropdownBarClient object. If you are implementing this
interface in managed code and you need to have the string disposed of
by the caller, implement the IVsCoTaskMemFreeMyStrings interface on
the IVsDropdownBarClient interface.
I assume that this is part of my problem, but I can't really understand what I have to change in my code to get it working. Any hints?
I'm pretty sure now that the Visual Studio SDK Interop DLLs have the wrong marshalling information for IVsDropDownbarClient.GetEntryText and that there's no way to call that method using that interface.
The best workaround I've found so far is:
Use the tlbimp tool to generate an alternate Interop DLL for textmgr. (You can safely ignore the dozens of warnings including the one about GetTextEntry.)
tlbimp "c:\Program Files (x86)\Microsoft Visual Studio 11.0\VSSDK\VisualStudioIntegration\Common\Inc\textmgr.tlb"
(Optional) If you're using source control, you'll probably want to copy the resulting file (TextManagerInternal.dll) to a subdirectory of your extension project and check it in as an external dependency.
In your Visual Studio extension project, add a reference to the file (TextManagerInternal.dll).
Add the following method that should properly handle the string marshalling.
static public string HackHackGetEntryText(IVsDropdownBarClient client, int iCombo, int iIndex)
{
TextManagerInternal.IVsDropdownBarClient hackHackClient = (TextManagerInternal.IVsDropdownBarClient) client;
string szText = null;
IntPtr ppszText = IntPtr.Zero;
try
{
ppszText = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr)));
if(ppszText == IntPtr.Zero)
throw new Exception("Unable to allocate memory for IVsDropDownBarClient.GetTextEntry string marshalling.");
hackHackClient.GetEntryText(iCombo, iIndex, ppszText);
IntPtr pszText = Marshal.ReadIntPtr(ppszText);
szText = Marshal.PtrToStringUni(pszText);
}
finally
{
if(ppszText != IntPtr.Zero)
Marshal.FreeCoTaskMem(ppszText);
}
return szText;
}
}

MS Word Interop - access CentimetersToPoints method

I am trying to set the print margins in a Word Doc I am printing from my c# application but I am having trouble accessing the methods that I need to call (based on what they are in MS Word VBA)
In VBA the code looks like this:
Options.printBackground = False
With ActiveDocument.PageSetup
.TopMargin = CentimetersToPoints(0.61)
.BottomMargin = CentimetersToPoints(0.43)
.LeftMargin = CentimetersToPoints(1.27)
.RightMargin = CentimetersToPoints(0.43)
.Gutter = CentimetersToPoints(0)
End With
Here is my c# code
oWordDoc.PageSetup.TopMargin = Microsoft.Office.Interop.Word.Application.CentimetersToPoints(float.Parse ("0.61")) ;
The error I am getting is:
An object reference is required for the non-static field, method, or property 'Microsoft.Office.Interop.Word._Application.CentimetersToPoints(float)'
I have tried several varaiations after searching the VS 2010 Object Browser for CentimetersToPoints
The available interfaces in the Object Browser are:
Microsoft.Office.Interop.Word._Application.CentimetersToPoints(float)
Microsoft.Office.Interop.Word.ApplicationClass.CentimetersToPoints(float)
Microsoft.Office.Interop.Word._Global.CentimetersToPoints(float)
Microsoft.Office.Interop.Word.GlobalClass.CentimetersToPoints(float)
how can I access methods like this?
thanks
As the error message indicates, you need a reference to the Word.Application object to access this method, since it is not static.
I guess you have a oWordApp somewhere that you use to create or open the oWordDoc, so you can access the method from this object.
Or you can retreive the instance of the Word.Application from your oWordDoc (Word.Document) object.
oWordDoc.PageSetup.TopMargin = oWordDoc.Application.CentimetersToPoints(float.Parse("0.61"));

Check if the current document in Visual Studio 2012 is a Code Window

I am looking for a way to have my extension check if the currently opened window in visual studio 2012 is one, where the user can write code (or any kind of text, really).
To check if the currently opened window has changed, I use
_DTE.Events.WindowEvents.WindowActivated.
This gives me the EnvDTE.Window that received the focus.
When I look at the properties of that window while debugging, and I look at EnvDTE.Window.Document.Type and it's value is "Text".
However, if I stop debugging and try to access the Document.Type property, it does not exist.
If I look for this property in the documentation of EnvDTE.Window.Document, its description says
Infrastructure. Microsoft Internal Use Only.
So now I am looking for any advice on how I could check if the currently active window is one, where I can write code (or anything else), or some other kind of document (like the solution properties for example).
Edit:
I also tried checking Window.Type and Window.Kind of the active window, but they just tell me that it's a document, not making any differentiation between a resource file, an image file or an actual source file, which is what I'm trying to find out.
Edit²:
The reason why I want to check if the current document is one where I can write code in, is because I want my extension to store information about some of those documents and I want to modify the right-click context menu based on the information I have stored, if any.
It is not a "real" answer, but you can follow status of VS GoTo command - it is available only for text editors:
bool isCodeWindow = IsCommandAvailable("Edit.GoTo");
private bool IsCommandAvailable(string commandName)
{
EnvDTE80.Commands2 commands = dte.Commands as EnvDTE80.Commands2;
if (commands == null)
return false;
EnvDTE.Command command = commands.Item(commandName, 0);
if (command == null)
return false;
return command.IsAvailable;
}
You can check to see if the document is a 'TextDocument'
bool isCodeWindow = dte.CurrentDocument.Object() is EnvDTE.TextDocument;

Cannot set Word Style's Base Style From C#

Microsoft.Office.Interop.Word version 14.0.0.0. .NET 4.0 VS 2010.
The MS Word API's Style class has a BaseStyle property that can be used to set the style's base (based on) style. That property works fine for me in VBA.
However from C# using Word interopt there is no BaseStyle property. However, there are two (undocumented as far as I can tell) functions set_BaseStyle() and get_BaseStyle().
When I call set_BaseStyle() I get a COMException with the message:
"This command is not available."
I think this means that the COM interface does not support the procedure (command). But why? Why does it appear in intellisense and compile? Is there a workaround?
This simple example works on my machine (VS 2012, Office 2007)
Application application = new Application {Visible = true};
string styleName1 = "Heading 1";
object styleNameObject1 = styleName1;
string styleName2 = "Heading 2";
object styleNameObject2 = styleName2;
var document = application.Documents.Add();
document.Select();
application.Selection.set_Style(ref styleNameObject2);
Style style = (Style)application.Selection.get_Style();
Style baseStyle = style.get_BaseStyle();
style.set_BaseStyle(ref styleNameObject1);
application.Selection.Range.Text = "This is the title";
application.Quit(false);
So the problem probably lies in your setup. The message is rather vague and it says word cannot do stuff, for other examples look at C# and Word2010 : DeleteAllComments throws "This command is not available." or search and replace in Word documents via .NET automation.
Is the file readonly? Does it happen with other styles or simpler files (such as my example)? Are Macros allowed in Word?
I found the problem.
The sample code posted by Vadim was a big help, as it did work, and I slowly slowly converted in to my code and eventually broke it, them moved back and forth until I homed in on the problem.
However, I can't explain what I found!
I was specifying all parameters when I opened the (existing) document with Application.Documents.Open(). It turns out that if I specify false (0) for the isvisible parameter, the code fails. If I secify true (-1) it works.
Note that in either case I can make 100s of other changes to the document. For some reason I cannot change the base style if it is invisible.
strange.
Thanks for your help.

Categories

Resources