.NET Forum / Visual Studio.NET / Extensibility / September 2005
Need CodeElement / EditPoint from cursor location changed event
|
|
Thread rating:  |
Oren Novotny - 02 May 2005 15:40 GMT My goal is to try and make an AddIn that keeps the selected class view item in sync with where the cursor is in the code window.
What would be the best way to track the cursor's location and then obtain the namespace, class, function, property, field or event that it's on? I'd be happy to constrain things to VS 2005 to take advantage of any new interfaces.
I've looked into the ActiveWindow, LineChanged and SelectionChanged events, but those all have shortcomings. If I go to a different part of a file, either with arrow keys, mouse, or some other way, I want to get an event showing me where I am. The same problem exists for LineChanged; it only fires after text changes, not upon the insertion point changing.
Can this be done with Automation? If not, can it be done in a mostly language-neutral way as a VSPackage?
I've installed the b2 VSIP, but the documentation is still very incomplete.
Thanks, --Oren
Yan-Hong Huang[MSFT] - 03 May 2005 09:38 GMT Hi Oren,
I have forwarded your question to our VSIP support team to see whether there is any new update on it. If there is any information, I will post back here as soon as possible.
Thanks very much.
Best regards, Yanhong Huang Microsoft Community Support
Get Secure! ¨C www.microsoft.com/security Register to Access MSDN Managed Newsgroups! -http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.as p&SD=msdn
This posting is provided "AS IS" with no warranties, and confers no rights.
Gary Chang[MSFT] - 05 May 2005 12:00 GMT Hi Oren,
>My goal is to try and make an AddIn that keeps the selected class view item >in sync with where the cursor is in the code window. [quoted text clipped - 4 lines] >showing me where I am. The same problem exists for LineChanged; it only >fires after text changes, not upon the insertion point changing. Based on my understanding, you want to trace your caret position and find its related code elements to sync the item in the class view, please let me know if I have misunderstood anything.
Currently, I will perform some research on this issue and update you when we get any results.
Thanks for your patience!
Best regards,
Gary Chang Microsoft Community Support -------------------- Get Secure! ¡§C www.microsoft.com/security Register to Access MSDN Managed Newsgroups! http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.asp &SD=msdn
This posting is provided "AS IS" with no warranties, and confers no rights.
Oren Novotny - 05 May 2005 12:20 GMT [snip]
> Based on my understanding, you want to trace your caret position and find > its related code elements to sync the item in the class view, please let > me > know if I have misunderstood anything. Yes, that is correct. For an initial cut, I can (try to) put a button/command on the class view to call a manual sync and have that based on the active code window, but what I really want is to have it happen automatically.
There is an option in the VS 05 ide called "Track Active Item in Solution Explorer." I want to do the same for Class View.
Thanks again, --Oren
Gary Chang[MSFT] - 07 May 2005 09:13 GMT Hi Oren,
>There is an option in the VS 05 ide called "Track Active Item in Solution >Explorer." I want to do the same for Class View. I have consulted our VSIP guys on your issue, it appears there is no prefabricated function for doing this in the code editor, but maybe you have interests on the following suggestion:
The EnvDTE.TextPoint.AbsoluteCharOffset returns the x,y character location of the insertion point (not the cursor, though), or you can use the EnvDTE80.CodeElement2.GetStartPoint / EnvDTE80.CodeElement2.GetEndPoint methods to get the beginning/end of a code element's position in the document. The EnvDTE80.CodeElement2.IsCodeType property returns the type of code element the TextPoint is in as a vsCMElement / vsCMElement2 enum value as does EnvDTE80.CodeElement2.Kind. (In fact, there are several "vsCMxxxx" enums that return different parts of a code element.)
However your options for doing this in Class View are severely limited because the Class View window doesn't really have any automation associated with it (in core automation). All you can do is affect the toolwindow itself (size, location, etc.) or query its contents via UIHierarchy. VSIP may provide fuller access to this toolwindow.
Hope this helps!
Best regards,
Gary Chang Microsoft Community Support -------------------- Get Secure! ¡§C www.microsoft.com/security Register to Access MSDN Managed Newsgroups! http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.asp &SD=msdn
This posting is provided "AS IS" with no warranties, and confers no rights.
Oren Novotny - 07 May 2005 15:22 GMT > Hi Oren, > [quoted text clipped - 16 lines] > "vsCMxxxx" > enums that return different parts of a code element.) Is there any way to hook into get this notification by using a VSPackage and connecting to any services? What about hooking into the Navigation Bar since in most casees the Navigation Bar shows the same information that's in the class view; there has to be some way to get this information since some of the functionality is already happening -- the navigation bar updates somehow...
> However your options for doing this in Class View are severely limited > because the Class View window doesn't really have any automation > associated > with it (in core automation). All you can do is affect the toolwindow > itself (size, location, etc.) or query its contents via UIHierarchy. VSIP > may provide fuller access to this toolwindow. I don't know if it's a "good idea" or not, but I've found that it's possible to get access to the main IServiceProvider interface just by casting the applicationObject. With that, I can access the class view services with GetService.
Thanks, --Oren
Gary Chang[MSFT] - 10 May 2005 11:12 GMT Hi Oren,
>Is there any way to hook into get this notification by using a VSPackage and >connecting to any services? What about hooking into the Navigation Bar >since in most casees the Navigation Bar shows the same information that's in >the class view; there has to be some way to get this information since some >of the functionality is already happening -- the navigation bar updates >somehow... I am not very clear about which notification you mean here, the cursor location changed event?
For the issue about hooking into the Navigation Bar, it appears the Navigation Bar only synchronizes with the cursor location when a line-changed event occurs...
Thanks!
Best regards,
Gary Chang Microsoft Community Support -------------------- Get Secure! ¡§C www.microsoft.com/security Register to Access MSDN Managed Newsgroups! http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.asp &SD=msdn
This posting is provided "AS IS" with no warranties, and confers no rights.
Oren Novotny - 11 May 2005 00:53 GMT Well, where there is a will, there is a way. I resorted to some good ole' fashioned subclassing to listen to the keydown and mousedown events, but I got it working. I'm not using any DTE2 interfaces or any .net 2.0 features, so this should work with VS 2003 as well.
Note one hackish section is that at least in VS 2005, the Window object's HWnd was always 0. I've bugged this in the product feedback center, but none-the-less, I worked around with Spy++ and FindWindowEx.
Hopefully the code below can be of help to someone; maybe it can be used as part of a sample in the sdk?
Thanks for your help, --Oren
using System; using System.Collections; using System.Collections.Specialized; using System.Text; using System.Reflection; using System.Runtime.InteropServices;
using EnvDTE;
using System.Windows.Forms;
namespace ClassSync {
internal class CaretWatcher : IDisposable { DTE applicationObject;
WindowEvents windowEvents;
HybridDictionary windowHandles = new HybridDictionary();
HybridDictionary subclassedWindows = new HybridDictionary();
IntPtr mainWindowHandle;
public CaretWatcher(DTE application) { applicationObject = application;
windowEvents = applicationObject.Events.get_WindowEvents(null);
windowEvents.WindowCreated += new _dispWindowEvents_WindowCreatedEventHandler(OnWindowCreated); windowEvents.WindowClosing += new _dispWindowEvents_WindowClosingEventHandler(OnWindowClosing);
KeyboardMouseWatcher mainWindow = new KeyboardMouseWatcher();
// get main window handle of devenv mainWindowHandle = (IntPtr)application.MainWindow.HWnd;
mainWindow.AssignHandle(mainWindowHandle);
mainWindow.InputDetected += new EventHandler(OnInput);
subclassedWindows.Add(mainWindowHandle, mainWindow); }
void OnWindowClosing(Window Window) { TextWindow wnd = Window.Object as TextWindow; if (wnd != null) { // remove the subclass IntPtr hwnd = (IntPtr)windowHandles[wnd.Parent]; windowHandles.Remove(wnd.Parent);
// Get the subclassed window KeyboardMouseWatcher watcher = (KeyboardMouseWatcher)subclassedWindows[hwnd];
subclassedWindows.Remove(hwnd); watcher.InputDetected -= new EventHandler(OnInput); watcher.ReleaseHandle(); } }
void OnWindowCreated(Window Window) { TextWindow wnd = Window.Object as TextWindow; if (wnd != null) { string caption = wnd.Parent.Caption;
// top level that we can id IntPtr hwndParent = FindWindowEx(mainWindowHandle, IntPtr.Zero, null, caption);
// This is hackish since we have no easier way to do this hwndParent = FindWindowEx(hwndParent, IntPtr.Zero, null, null);
// an intermediate window hwndParent = FindWindowEx(hwndParent, IntPtr.Zero, null, null);
// This is the VsTextEditPane item IntPtr pane = FindWindowEx(hwndParent, IntPtr.Zero, null, null);
windowHandles[wnd.Parent] = pane;
KeyboardMouseWatcher watcher = new KeyboardMouseWatcher(); watcher.AssignHandle(pane); watcher.InputDetected += new EventHandler(OnInput); subclassedWindows.Add(pane, watcher); } }
[DllImport("User32.dll", SetLastError=true)] private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string className, string windowName);
private void OnInput(object sender, EventArgs e) { TryCaratChanged(); }
private TextWindow oldWindow; private VirtualPoint oldPoint;
private int oldLine; private int oldCol;
public event CaretChangedEventHandler CaretChanged;
private bool TryCaratChanged() { bool retval = false; try { TextWindow newWindow = applicationObject.ActiveWindow.Object as TextWindow;
// first check to see if the window is the same if (newWindow == null) { oldWindow = null; } else if (oldWindow != newWindow) { retval = true; oldWindow = newWindow; }
if (oldWindow == null) { retval = false; oldPoint = null; oldLine = 0; oldCol = 0; } else if (newWindow.Selection.ActivePoint.Line != oldLine || newWindow.Selection.ActivePoint.VirtualDisplayColumn != oldCol) { retval = true; oldPoint = newWindow.Selection.ActivePoint; oldLine = newWindow.Selection.ActivePoint.Line; oldCol = newWindow.Selection.ActivePoint.VirtualDisplayColumn; }
if (retval) { CaretChangedEventHandler evt = CaretChanged; if (evt != null) evt(this, new CaretChangedEventArgs(newWindow.Selection.ActivePoint)); }
return retval; } catch (COMException) { // If we get any exceptions, return false return false; } }
#region IDisposable Members
public void Dispose() {
if (windowEvents != null) { windowEvents.WindowCreated -= new _dispWindowEvents_WindowCreatedEventHandler(OnWindowCreated); windowEvents.WindowClosing -= new _dispWindowEvents_WindowClosingEventHandler(OnWindowClosing); }
foreach (KeyboardMouseWatcher watcher in subclassedWindows) { watcher.ReleaseHandle(); } }
#endregion }
internal class CaretChangedEventArgs : EventArgs { private VirtualPoint currentPoint; public CaretChangedEventArgs(VirtualPoint CaratPoint) { currentPoint = CaratPoint; }
public VirtualPoint CurrentPoint { get { return currentPoint; } } }
internal delegate void CaretChangedEventHandler(object sender, CaretChangedEventArgs e);
internal class KeyboardMouseWatcher : NativeWindow { private const int WM_KEYDOWN = 0x0100; private const int WM_KEYUP = 0x101; private const int WM_LBUTTONUP = 0x202; private const int WM_RBUTTONUP = 0x205; private const int WM_MBUTTONUP = 0x208; private const int WM_XBUTTONUP = 0x20C; private const int WM_LBUTTONDOWN = 0x201; private const int WM_RBUTTONDOWN = 0x204; private const int WM_MBUTTONDOWN = 0x207; private const int WM_XBUTTONDOWN = 0x20B; private const int WM_PARENTNOTIFY = 0x0210;
public event EventHandler InputDetected;
protected override void WndProc(ref Message m) { bool keymousedown = false;
// Get the mouse args switch (m.Msg) {
case WM_KEYUP: case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_XBUTTONDOWN: keymousedown = true; break; }
// let the base have it base.WndProc(ref m);
//now trigger our event if (keymousedown) { EventHandler evt = InputDetected; if (evt != null) evt(this, EventArgs.Empty); } } } }
Gary Chang[MSFT] - 11 May 2005 04:23 GMT >Well, where there is a will, there is a way. I resorted to some good >ole' fashioned subclassing to listen to the keydown and mousedown >events, but I got it working. It's great, Oren!
Thanks for sharing this valuable code sample with us, for the issue about the Window object's HWnd value, I will forward your feedback to our product team directly.
Good Luck!
Best regards,
Gary Chang Microsoft Community Support -------------------- Get Secure! ¡§C www.microsoft.com/security Register to Access MSDN Managed Newsgroups! http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.asp &SD=msdn
This posting is provided "AS IS" with no warranties, and confers no rights.
Dave Lopez - 13 May 2005 22:10 GMT Oren, your code example is exactly where I need to be going with my addin. Thanks.
I'm a bit rusty in windows programming (been 5 years or so), so I'm not exactly sure where to go from here though.
The one thing I've noticed is that in WndProc, if I add WM_KEYDOWN to the cases, WndProc doesn't seem to get that event. It gets WM_KEYUP, but not WM_KEYDOWN except for what seems to be the shift key.
It doesn't look like I can use PreProcessMessage, and it I think I've read some documentation where some messages will goto the parent instead of the subclass.
At this point I'm looking at using SetWindowHookEx to hook into WM_KEYDOWN, but any help is appreciated.
Dave Lopez - 14 May 2005 03:11 GMT Just to follow up on my previous message. The WM_KEYDOWN message is getting sent for some keystrokes, but not all of them. I really need to be able to intercept VK_ESCAPE if possible.
KCS - 28 Sep 2005 14:11 GMT Hi Oren
It looks like you may be able to help me.
Can you figure out a way to programmatically activate the current web form designer (assuming it does not have focus) so that you can then invoke the standard command 'Paste' method to insert a control from the clipboard??
I have tried sending mouse messages to the (what I think is) the correct window object. But I cannot achieve the effect that happens by manually clicking on the web form, which is to activate the paste button, which can then be clicked to insert a control.
Thanks for any advice you can give.
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 ...
|
|
|