.NET Forum / Visual Studio.NET / Extensibility / July 2005
launching a fileless editor
|
|
Thread rating:  |
DarenYong - 09 Jul 2005 10:21 GMT Hello,
I ran across old postings by Lee Nowotny on how to launch a fileless editor.
I have a very similar situation and need to open an editor (preferably the built in Xml Editor) on a buffer in memory, and then when the user exits, save the contents back into my custom storage.
I'm having a very hard time trying to reproduce Lee's code by following the steps he mentions below.
I'm hoping that someone can help me out with a snippet of code or a sample on how do this.
Any help would be greatly appreciated.
Best regards, Daren.
Daren Yong, BSc Captaris Inc. 855 42nd Ave SE, Suite 110 | Calgary, AB, T2G 1Y8 | Canada Ph. 403.668.6332 | Fax 403.214.7334 DarenYong@Captaris.com | www.Captaris.com | NASDAQ: CAPA Lee's old post follows: ================
I have created my own custom windows form designer that works as desired when associated with a particular file extension. However, I'm trying to design things that come from a database file as opposed to a regular windows file. So, I am creating a text buffer and populating it with info that I retrieve from the database. Then I go through a series of calls to bring up the designer:
IVsUIShellOpenDocument::GetStandardEditorFactory IVsEditorFactory::CreateEditorInstance IVsUIShellOpenDocument::InitializeEditorInstance IVsWindowFrame::Show
Following these steps, the designer is up and looks good, except for the fact that the property window is no longer following the selection in my designer. In other words, if you select a control, the propety window is blank. This works fine if it is based on a regular file. So, my question is this: What needs to be done to connect my designer/selection to the property window? Any help would be greatly appreciated!
Lee Nowotny ASNA
DarenYong - 11 Jul 2005 02:12 GMT Hello,
Here is my scenario: In my custom designer I need to launch an instance of the XmlEditor on a string buffer in memory. Once the user closes this XmlEditor, the changes he made need to be saved back into a custom format inside the file being edited by the parent editor.
By going to Dr. Ex's blog, I managed to hack this code (see below) to open a toolwindow.
I have some of problems with this code. 1) How do I make my data appear in the window? (rather than just opening a blank editor). 2) How do I open the XmlEditor instead of the regular Visual Studio text editor? 3) Can I make this tool window modal? 4) If I can't make the tool window modal, maybe it is preferable to trap the saving of the text buffer in the child editor so that it _somehow_ communicates the changes made in the editor back to the parent editor that spawned it.
// Use ILocalRegistry to create text buffer and code window ILocalRegistry localRegistry = this.GetVsService<ILocalRegistry>(); Debug.Assert(localRegistry != null, "Unable to retrieve ILocalRegistry");
// Create the document (text buffer) IntPtr vsTextLines = IntPtr.Zero; Guid guidTextLines = typeof(IVsTextLines).GUID; ErrorHandler.ThrowOnFailure( localRegistry.CreateInstance(typeof(VsTextBufferClass).GUID, null, ref guidTextLines, (uint)CLSCTX.CLSCTX_INPROC_SERVER, out vsTextLines) ); IVsTextLines textLines = (IVsTextLines) Marshal.GetObjectForIUnknown(vsTextLines);
// Site it, so it can find/query for various services IObjectWithSite objectWithSite = (IObjectWithSite)textLines; objectWithSite.SetSite(vsServiceProvider);
// Create the codewindow (editor) IntPtr vsCodeWindow = IntPtr.Zero; Guid guidCodeWindow = typeof(IVsCodeWindow).GUID; ErrorHandler.ThrowOnFailure( localRegistry.CreateInstance(typeof(VsCodeWindowClass).GUID, null, ref guidCodeWindow, (uint)CLSCTX.CLSCTX_INPROC_SERVER, out vsCodeWindow) ); IVsCodeWindow codeWindow = Marshal.GetObjectForIUnknown(vsCodeWindow) as IVsCodeWindow;
// Attach buffer to code window codeWindow.SetBuffer(textLines);
// Use IVsUIShell::CreateToolWindow to create the frame. IVsUIShell vsUiShell = (IVsUIShell)this.GetVsService(typeof(SVsUIShell));
IEnumWindowFrames windowFramesEnum; vsUiShell.GetDocumentWindowEnum(out windowFramesEnum);
Guid guidNull = Guid.Empty; Guid guidPersistenceSlot = Guid.NewGuid(); int[] position = new int[1]; IVsWindowFrame windowFrame; ErrorHandler.ThrowOnFailure( vsUiShell.CreateToolWindow((uint)(__VSCREATETOOLWIN.CTW_fForceCreate), 0, codeWindow, ref guidNull, ref guidPersistenceSlot, ref guidNull, null, "VB Code Editor ToolWindow", position, out windowFrame) );
// enable editor key bindings Guid guidCmdUI_TextEditor = new Guid("{8B382828-6202-11d1-8870-0000F87579D2}"); windowFrame.SetGuidProperty((int)__VSFPROPID.VSFPROPID_InheritKeyBindings, ref guidCmdUI_TextEditor);
windowFrame.Show();
I tried to duplicate the code Lee suggested for a fileless editor, but the code below returns a windowFrame of null in InitializeEditorInstance(). I'm not sure which technique is best (the code above or the code below). At least with the code above (copied from Dr. Ex) I can actually see a newly spawned editor window and type into it.
IVsEditorFactory editorFactory; string physicalViewString; Guid textEditorFactoryGuid = VSConstants.GUID_TextEditorFactory; Guid textViewGuid = VSConstants.LOGVIEWID_TextView; IVsUIShellOpenDocument shellOpenDocument = (IVsUIShellOpenDocument) this.GetVsService(typeof(SVsUIShellOpenDocument)); shellOpenDocument.GetStandardEditorFactory(0, ref textEditorFactoryGuid, null, ref textViewGuid, out physicalViewString, out editorFactory);
// Use ILocalRegistry to create text buffer and code window ILocalRegistry localRegistry = this.GetVsService<ILocalRegistry>();// vsServiceProvider.GetService(typeof(ILocalRegistry)); Debug.Assert(localRegistry != null, "Unable to retrieve ILocalRegistry");
// Create the document (text buffer) IntPtr vsTextLines = IntPtr.Zero; Guid guidTextLines = typeof(IVsTextLines).GUID; int hr = localRegistry.CreateInstance(typeof(VsTextBufferClass).GUID, null, ref guidTextLines, (uint)CLSCTX.CLSCTX_INPROC_SERVER, out vsTextLines); Debug.Assert(hr == 0, "Failed to create instance of TextBuffer"); IVsTextLines textLines = Marshal.GetObjectForIUnknown(vsTextLines) as IVsTextLines;
// Site it, so it can find/query for various services IObjectWithSite ows = (IObjectWithSite)textLines; ows.SetSite(vsServiceProvider);
// Finally create the editor int cdw; Guid guidCmdUI; string editorCaption; IVsHierarchy hierarchy = this.Hierarchy; IntPtr docView; IntPtr docData; editorFactory.CreateEditorInstance((uint)__VSCREATEEDITORFLAGS.CEF_OPENFILE, "", physicalViewString, hierarchy, 0, vsTextLines, out docView, out docData, out editorCaption, out guidCmdUI, out cdw); // Initialize the editor IVsWindowFrame windowFrame; IVsUIHierarchy uiHierarchy = this.SpecializedProject; IOleServiceProvider oleServiceProvider = this.GetVsService<IOleServiceProvider>(); ErrorHandler.ThrowOnFailure( shellOpenDocument.InitializeEditorInstance((uint)__VSIEIFLAGS.IEI_CREATENEWDOCWIN_MASK, docView, docData, null, ref textEditorFactoryGuid, physicalViewString, ref textViewGuid, "test caption", editorCaption, uiHierarchy, 0, vsTextLines, oleServiceProvider, ref guidCmdUI, out windowFrame) );
windowFrame.Show();
Best Regards, Daren.
"Ed Dore [MSFT]" - 12 Jul 2005 19:55 GMT Hi Darren,
I don't believe the XML editor is creatable similar to the core VS editor (which is actually a component designed to be reusable and customized by implementing a language service). You may want to consider using IVsUIShellOpenDocument::OpenSpecificEditor to invoke the XML Editor. I don't have ann example of this handy, but essentially you will need to load your data into a sited VsTextBuffer object, and then pass the VsTextBuffer object via the punkDocDataExisting argument of your OpenSpecificEditor call. Note, you'll probably also need to provide a hierarchy implementation here as well.
Sincerely, Ed Dore [MSFT]
This post is 'AS IS' with no warranties, and confers no rights.
DarenYong - 12 Jul 2005 21:14 GMT Hello Ed,
Thanks for helping me out with this - I have been tearing my hair out over the weekend trying to figure out how to make all of these calls into VSIP services - with so many parameters, this is not an easy task.
You mention: "essentially you will need to load your data into a sited VsTextBuffer object"
I'm not sure how to do this. I know how to create and site the text buffer, but not how to load it with data. What services do I have to query for, and what methods do I have to call to accomplish this?
Thanks,
Best Regards, Daren.
"Ed Dore [MSFT]" - 12 Jul 2005 22:51 GMT Hi Darren,
I'm short a few more hairs after attempting this myself<g>.
You can set the initial text in the buffer after you site it. Just use IVsTextBuffer.InitializeContent. I'm not sure if you have a project or hierarchy available. When I did this, I wasn't using a VsHierarchyWindow, and had cooked up my own simple hierarchy implementation using a managed toolwindow in conjunction with a treeview control.
Sincerely, Ed Dore [MSFT]
This post is 'AS IS' with no warranties, and confers no rights.
DarenYong - 15 Jul 2005 09:14 GMT Hello Ed,
Well after much digging, I have some progress.
I read this in the VSIP help:
When you open a document in an editor, an entry for the document data (DocData) is made in the running document table (RDT). However, you can have one physical document with multiple sub-parts that need to be edited in separate windows, like the resource editor. In this case, more than one entry would be placed in the RDT for the same DocData, which would return an error. To avoid this, specify a value of CDW_fAltDocData to make one document entry in the RDT for the entire file, but open individual document windows for each of the subparts. For this to work, the main document always has to be open. For an example of specifying alternate document data, see the Solution Extender sample.
Also from docs:
- Advanced "VC Resource Editor" Style.
SlnExt: - Creating Document Window to edit a piece of Solution State
This is exactly the scenario I want to support.
Following the SlnExt sample, I managed to get a function to open up document windows on my document subparts.
I have a couple of questions:
1) If I call this function twice for the same sub-part, a text editor window is opened to the same sub part, but a second vertical scroll bar appears along the right hand side. Is there some type of checking / modification required to this code to allow me to activate an existing sub part editor from the main document?
2) Is it possible to use this code to open an Xml Editor on the subparts, rather than just the regular code window? Judging from your reply that the Xml Editor was never intended to be extended, I assume this is no.
3) Do you have any idea how I coordinate these windows to save the subparts back into the main document? When the main document closes, do I have to manually force these child windows to close?
4) I could not get OpenSpecificEditor to work. If I pass the guidEditorType as the xml editor guid (I hope I have the right guid!?) I get an invalid cast exception. Using VSConstants.GUID_TextEditorFactory works and I see the normal editor.
- Please Help!! ;-)
private void CreateDocSubPart(InvokeXmlEditorEventArgs e) { // Use ILocalRegistry to create text buffer and code window ILocalRegistry localRegistry = this.GetVsService<ILocalRegistry>(); Debug.Assert(localRegistry != null, "Unable to retrieve ILocalRegistry");
// Create the document (text buffer) IntPtr vsTextLines = IntPtr.Zero; Guid guidTextLines = typeof(IVsTextLines).GUID; ErrorHandler.ThrowOnFailure( localRegistry.CreateInstance(typeof(VsTextBufferClass).GUID, null, ref guidTextLines, (uint)CLSCTX.CLSCTX_INPROC_SERVER, out vsTextLines) ); IVsTextLines textLines = (IVsTextLines)Marshal.GetObjectForIUnknown(vsTextLines);
// Create the codewindow (editor) IntPtr vsCodeWindow = IntPtr.Zero; Guid guidCodeWindow = typeof(IVsCodeWindow).GUID; ErrorHandler.ThrowOnFailure( localRegistry.CreateInstance(typeof(VsCodeWindowClass).GUID, null, ref guidCodeWindow, (uint)CLSCTX.CLSCTX_INPROC_SERVER, out vsCodeWindow) ); IVsCodeWindow codeWindow = Marshal.GetObjectForIUnknown(vsCodeWindow) as IVsCodeWindow;
// Site it, so it can find/query for various services IObjectWithSite objectWithSite = (IObjectWithSite)textLines; objectWithSite.SetSite(vsServiceProvider);
textLines.InitializeContent(e.Xml, e.Xml.Length);
// Attach buffer to code window codeWindow.SetBuffer(textLines);
IVsUIShell vsUiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
uint rdtFlags; uint readLocks; uint editLocks; string mkDocument; IVsHierarchy hierarchy; uint testItemId; IntPtr testDocData; runningDocTable.GetDocumentInfo(docCookie, out rdtFlags, out readLocks, out editLocks, out mkDocument, out hierarchy, out testItemId, out testDocData);
IVsUIHierarchy uiHierarchy = this.SpecializedProject;
Guid xmlGuid = new Guid("{fa3cd31e-987b-443a-9b81-186104e8dac1}"); //Guid editorGuid = Guid.Empty; string physicalView = e.Name; Guid guidCmdUI_TextEditor = new Guid("{8B382828-6202-11d1-8870-0000F87579D2}"); int[] defaultPosition = new int[1]; IOleServiceProvider oleServiceProvider = (IOleServiceProvider)this.GetVsService(typeof(IOleServiceProvider)); IVsWindowFrame windowFrame;
vsUiShell.CreateDocumentWindow((uint)__VSCREATEDOCWIN.CDW_fAltDocData, mkDocument, uiHierarchy, itemID, vsCodeWindow, docData, ref xmlGuid, physicalView, ref guidCmdUI_TextEditor, oleServiceProvider, "[Workflow1.cw]", e.Name, null, out windowFrame);
windowFrame.Show(); }
Code to OpenSpecificEditor for Xml:
private void OpenXmlView(InvokeXmlEditorEventArgs e) { // Use ILocalRegistry to create text buffer and code window ILocalRegistry localRegistry = this.GetVsService<ILocalRegistry>(); Debug.Assert(localRegistry != null, "Unable to retrieve ILocalRegistry");
// Create the document (text buffer) IntPtr vsTextLines = IntPtr.Zero; Guid guidTextLines = typeof(IVsTextLines).GUID; ErrorHandler.ThrowOnFailure( localRegistry.CreateInstance(typeof(VsTextBufferClass).GUID, null, ref guidTextLines, (uint)CLSCTX.CLSCTX_INPROC_SERVER, out vsTextLines) ); IVsTextLines textLines = (IVsTextLines)Marshal.GetObjectForIUnknown(vsTextLines);
// Site it, so it can find/query for various services IObjectWithSite objectWithSite = (IObjectWithSite)textLines; objectWithSite.SetSite(vsServiceProvider);
textLines.InitializeContent(e.Xml, e.Xml.Length);
// NOTE that at this point, the IVsTextLines exists without an entry in the RDT. InspectRdt();
IVsUIShellOpenDocument shellOpenDocument = (IVsUIShellOpenDocument)this.GetVsService(typeof(SVsUIShellOpenDocument)); string mkDocument = ".xml"; Guid xmlGuid = new Guid("{fa3cd31e-987b-443a-9b81-186104e8dac1}"); *** this fails with InvalidCastException. *** //Guid textEditorFactoryGuid = VSConstants.GUID_TextEditorFactory; *** this succeeds ***
string physicalView = null; Guid logicalViewGuid = VSConstants.LOGVIEWID_Primary; string ownerCaption = e.Name; IVsUIHierarchy uiHierarchy = new VirtualHierarchy(); uint myItemId = 0; IOleServiceProvider oleServiceProvider = this.GetVsService<IOleServiceProvider>(); IVsWindowFrame windowFrame;
shellOpenDocument.OpenSpecificEditor((uint)__VSOSPEFLAGS.OSPE_OpenAsNewFile, mkDocument, ref xmlGuid, physicalView, ref logicalViewGuid, ownerCaption, uiHierarchy, myItemId, vsTextLines, oleServiceProvider, out windowFrame); windowFrame.Show(); }
Best Regards, D.
DarenYong - 15 Jul 2005 09:19 GMT Whoops - sorry for long post.
Here is my naive implementation of IVsUIHierarchy, I suspect this is the source of the InvalidCastException!
public class VirtualHierarchy : IVsHierarchy, IVsUIHierarchy, IVsWindowPane, IOleCommandTarget, IOleServiceProvider, ISelectionContainer, IVsBroadcastMessageEvents, IVsSolutionEvents, IConnectionPointContainer { #region IVsHierarchy Members
public int AdviseHierarchyEvents(IVsHierarchyEvents pEventSink, out uint pdwCookie) { pdwCookie = 0; return VSConstants.S_FALSE; }
public int Close() { return VSConstants.S_FALSE; }
public int GetCanonicalName(uint itemid, out string pbstrName) { pbstrName = string.Empty; return VSConstants.S_FALSE; }
public int GetGuidProperty(uint itemid, int propid, out Guid pguid) { pguid = Guid.Empty; return VSConstants.S_FALSE; }
public int GetNestedHierarchy(uint itemid, ref Guid iidHierarchyNested, out IntPtr ppHierarchyNested, out uint pitemidNested) { ppHierarchyNested = IntPtr.Zero; pitemidNested = 0; return VSConstants.S_FALSE; }
public int GetProperty(uint itemid, int propid, out object pvar) { pvar = null; return VSConstants.S_FALSE; }
public int GetSite(out Microsoft.VisualStudio.OLE.Interop.IServiceProvider ppSP) { ppSP = null; return VSConstants.S_FALSE; }
public int ParseCanonicalName(string pszName, out uint pitemid) { pitemid = 0; return VSConstants.S_FALSE; }
public int QueryClose(out int pfCanClose) { pfCanClose = 1; return VSConstants.S_FALSE; }
public int SetGuidProperty(uint itemid, int propid, ref Guid rguid) { rguid = Guid.Empty; return VSConstants.S_FALSE; }
public int SetProperty(uint itemid, int propid, object var) { return VSConstants.S_FALSE; }
public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp) { return VSConstants.S_FALSE; }
public int UnadviseHierarchyEvents(uint dwCookie) { return VSConstants.S_FALSE; }
public int Unused0() { return VSConstants.S_OK; }
public int Unused1() { return VSConstants.S_OK; }
public int Unused2() { return VSConstants.S_OK; }
public int Unused3() { return VSConstants.S_OK; }
public int Unused4() { return VSConstants.S_OK; }
#endregion
#region IVsUIHierarchy Members
public int ExecCommand(uint itemid, ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { return VSConstants.S_OK; }
public int QueryStatusCommand(uint itemid, ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { return VSConstants.S_OK;
} #endregion
#region IVsWindowPane Members
public int ClosePane() { return VSConstants.S_FALSE; }
public int CreatePaneWindow(IntPtr hwndParent, int x, int y, int cx, int cy, out IntPtr hwnd) { hwnd = IntPtr.Zero; return VSConstants.S_FALSE; }
public int GetDefaultSize(SIZE[] pSize) { return VSConstants.S_FALSE; }
public int LoadViewState(IStream pStream) { return VSConstants.S_FALSE; }
public int SaveViewState(IStream pStream) { return VSConstants.S_FALSE; }
public int TranslateAccelerator(MSG[] lpmsg) { return VSConstants.S_FALSE; }
#endregion
#region IOleCommandTarget Members
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { return VSConstants.S_FALSE; }
public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { return VSConstants.S_FALSE; }
#endregion
#region IOleServiceProvider Members
public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject) { ppvObject = IntPtr.Zero; return VSConstants.S_FALSE; }
#endregion
#region ISelectionContainer Members
public int CountObjects(uint dwFlags, out uint pc) { pc = 0; return VSConstants.S_FALSE; }
public int GetObjects(uint dwFlags, uint cObjects, object[] apUnkObjects) { return VSConstants.S_FALSE; }
public int SelectObjects(uint cSelect, object[] apUnkSelect, uint dwFlags) { return VSConstants.S_FALSE; }
#endregion
#region IVsBroadcastMessageEvents Members
public int OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam) { throw new Exception("The method or operation is not implemented."); }
#endregion
#region IVsSolutionEvents Members
public int OnAfterCloseSolution(object pUnkReserved) { throw new Exception("The method or operation is not implemented."); }
public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) { throw new Exception("The method or operation is not implemented."); }
public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) { throw new Exception("The method or operation is not implemented."); }
public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) { throw new Exception("The method or operation is not implemented."); }
public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) { throw new Exception("The method or operation is not implemented."); }
public int OnBeforeCloseSolution(object pUnkReserved) { throw new Exception("The method or operation is not implemented."); }
public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) { throw new Exception("The method or operation is not implemented."); }
public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) { throw new Exception("The method or operation is not implemented."); }
public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) { throw new Exception("The method or operation is not implemented."); }
public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) { throw new Exception("The method or operation is not implemented."); }
#endregion
#region IConnectionPointContainer Members
public void EnumConnectionPoints(out IEnumConnectionPoints ppEnum) { throw new Exception("The method or operation is not implemented."); }
public void FindConnectionPoint(ref Guid riid, out IConnectionPoint ppCP) { throw new Exception("The method or operation is not implemented."); }
#endregion }
DarenYong - 17 Jul 2005 03:50 GMT Hello Ed,
Never mind about question 1 and 3 - I figured it out from carefully examining the SlnExt project.
The trick is to create a ViewHelper class which inherits from IVsWindowFrameNotify and IVsWindowFrameNotify2.
After getting the window frame back from IVsUIShell.CreateDocumentWindow(), I set the view helper into it.
windowFrame.SetProperty((int)__VSFPROPID.VSFPROPID_ViewHelper, viewHelper);
This creates an interceptor, where I can check to see if the buffer is modified and save it if necessary (and forward all other messages to the wrapped code window).
This still leaves me with 2 questions:
1) Can I use IVsUIShell.CreateDocumentWindow() to open an XmlEditor? 2) I still can't get open specific editor to work. I get an InvalidCastException. Is it my poor implementation of the VirtualHierarchy class causing this?
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|