Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Visual Studio.NET / Extensibility / September 2005

Tip: Looking for answers? Try searching our database.

Need CodeElement / EditPoint from cursor location changed event

Thread view: 
Enable EMail Alerts  Start New Thread
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.

Rate this thread:







Free Magazines

Get 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 ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.