1 //------------------------------------------------------------------------------
2 // <copyright file="WindowPane.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace Microsoft
.VisualStudio
.Shell
{
9 using Microsoft
.VisualStudio
.OLE
.Interop
;
10 using Microsoft
.VisualStudio
.Shell
.Interop
;
11 using Microsoft
.Win32
;
13 using System
.Collections
;
14 using System
.ComponentModel
;
15 using System
.ComponentModel
.Design
;
16 using System
.Diagnostics
;
17 using System
.Diagnostics
.CodeAnalysis
;
19 using System
.Runtime
.InteropServices
;
20 using System
.Windows
.Forms
;
22 using IOleServiceProvider
= Microsoft
.VisualStudio
.OLE
.Interop
.IServiceProvider
;
23 using IServiceProvider
= System
.IServiceProvider
;
25 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane"]' />
27 /// This is a quick way to implement a tool window pane. This class
28 /// implements IVsWindowPane; you must provide an implementation of an
29 /// object that returns an IWin32Window, however. In addition to
30 /// IVsWindowPane this object implements IOleCommandTarget, mapping
31 /// it to IMenuCommandService and IObjectWithSite, mapping the site
32 /// to services that can be querried through its protected GetService
35 [System
.Runtime
.InteropServices
.ComVisible(true)]
36 public abstract class WindowPane
:
40 IVsBroadcastMessageEvents
,
44 private IServiceProvider _parentProvider
;
45 private ServiceProvider _provider
;
46 private IVsShell _vsShell
;
47 private uint _broadcastEventCookie
;
49 private IMenuCommandService _commandService
;
50 private HelpService _helpService
;
52 private bool _zombie
= false;
54 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.WindowPane"]' />
56 /// Creates a new window pane. The window pane can accept a service provider
57 /// to use when resolving services. This provider can be null.
59 protected WindowPane(IServiceProvider provider
) {
60 _parentProvider
= provider
;
63 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.Window"]' />
65 /// Retrieves the window associated with this window pane.
67 public abstract IWin32Window Window { get; }
69 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.Dispose"]' />
71 /// Can be called to dispose this editing window.
73 public void Dispose() {
77 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.Dispose1"]' />
79 /// Called when this window pane is being disposed.
81 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
82 protected virtual void Dispose(bool disposing
) {
86 if (_vsShell
!= null) {
88 // Don't check for return code because here we can't do anything in case of failure.
89 _vsShell
.UnadviseBroadcastMessages(_broadcastEventCookie
);
90 } catch (Exception
) { /* do nothing */ }
92 _broadcastEventCookie
= 0;
95 IWin32Window window
= Window
;
96 if (window
is IDisposable
) {
98 ((IDisposable
)window
).Dispose();
100 Debug
.Fail("Failed to dispose window");
105 if (_commandService
!= null && _commandService
is IDisposable
) {
107 ((IDisposable
)_commandService
).Dispose();
108 } catch (Exception
) {
109 Debug
.Fail("Failed to dispose command service");
112 _commandService
= null;
114 if (_parentProvider
!= null)
115 _parentProvider
= null;
117 if (_helpService
!= null)
120 // Do not clear _provider. SetSite will do it for us.
127 /// This is a separate method so the jitter doesn't see MenuCommandService (from system.design.dll) in
128 /// the GetService call and load the assembly.
130 private void EnsureCommandService() {
131 if (_commandService
== null) {
132 _commandService
= new OleMenuCommandService(this);
136 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.GetService"]' />
138 /// Maps to IServiceProvider for service routing.
140 protected virtual object GetService(Type serviceType
) {
144 Debug
.Fail("GetService called after WindowPane was zombied");
148 if (serviceType
== null) {
149 throw new ArgumentNullException("serviceType");
152 // We provide IMenuCommandService, so we will
153 // demand create it. MenuCommandService also
154 // implements IOleCommandTarget, but unless
155 // someone requested IMenuCommandService no commands
156 // will exist, so we don't demand create for
159 if (serviceType
== typeof(IMenuCommandService
)) {
160 EnsureCommandService();
161 return _commandService
;
163 else if (serviceType
== typeof(IOleCommandTarget
)) {
164 return _commandService
;
166 else if (serviceType
== typeof(IHelpService
)) {
167 if (_helpService
== null) {
168 _helpService
= new HelpService(this);
173 if (_provider
!= null) {
174 object service
= _provider
.GetService(serviceType
);
175 if (service
!= null) {
180 if (_parentProvider
!= null) {
181 return _parentProvider
.GetService(serviceType
);
187 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.Initialize"]' />
189 /// This method is called after the window pane has been sited. Any initialization
190 /// that requires window frame services from VS can be done by overriding this
193 protected virtual void Initialize() {
196 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.OnClose"]/*' />
198 /// The OnClose method is called in response to the ClosePane method on
199 /// IVsWindowPane. The default implementation calls Dispose();
201 protected virtual void OnClose() {
205 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.OnCreate"]/*' />
207 /// The OnCreate method is called during the CreatePaneWindow method of
208 /// IVsWindowPane. This provides a handy hook for knowing when VS wants
209 /// the window. The default implementation does nothing.
211 protected virtual void OnCreate() {
214 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.PreProcessMessage"]' />
216 /// This method will be called to pre-process keyboard
217 /// messages before VS handles them. It is directly
218 /// attached to IVsWindowPane::TranslateAccellerator.
219 /// The default implementation calls the PreProcessMessage
220 /// method on a Windows Forms control. You may override this if your
221 /// window pane is not based on Windows Forms.
222 /// Arguments and return values are the
223 /// same as for Windows Forms: return true if you handled
224 /// the message, false if you want the default processing
227 protected virtual bool PreProcessMessage(ref Message m
) {
228 Control c
= Control
.FromChildHandle(m
.HWnd
);
230 return c
.PreProcessControlMessage(ref m
) == PreProcessControlState
.MessageProcessed
;
237 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IOleCommandTarget.Exec"]/*' />
240 /// This is called by Visual Studio when the user has requested to execute a particular
241 /// command. There is no need to override this method. If you need access to menu
242 /// commands use IMenuCommandService.
244 int IOleCommandTarget
.Exec(ref Guid guidGroup
, uint nCmdId
, uint nCmdExcept
, IntPtr pIn
, IntPtr vOut
) {
246 // Always redirect through GetService for this. That way outside users can replace
249 IOleCommandTarget cmdTarget
= GetService(typeof(IOleCommandTarget
)) as IOleCommandTarget
;
250 if (cmdTarget
!= null) {
251 return cmdTarget
.Exec(ref guidGroup
, nCmdId
, nCmdExcept
, pIn
, vOut
);
254 return NativeMethods
.OLECMDERR_E_NOTSUPPORTED
;
258 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IOleCommandTarget.QueryStatus"]/*' />
261 /// This is called by Visual Studio when it needs the status of our menu commands. There
262 /// is no need to override this method. If you need access to menu commands use
263 /// IMenuCommandService.
265 int IOleCommandTarget
.QueryStatus(ref Guid guidGroup
, uint nCmdId
, OLECMD
[] oleCmd
, IntPtr oleText
) {
267 // Always redirect through GetService for this. That way outside users can replace
270 IOleCommandTarget cmdTarget
= GetService(typeof(IOleCommandTarget
)) as IOleCommandTarget
;
271 if (cmdTarget
!= null) {
272 return cmdTarget
.QueryStatus(ref guidGroup
, nCmdId
, oleCmd
, oleText
);
275 return NativeMethods
.OLECMDERR_E_NOTSUPPORTED
;
279 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IServiceProvider.GetService"]/*' />
282 /// IServiceProvider implementation.
284 object IServiceProvider
.GetService(Type serviceType
) {
285 return GetService(serviceType
);
288 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IVsBroadcastMessageEvents.OnBroadcastMessage"]/*' />
291 /// Receives broadcast messages from the shell
293 int IVsBroadcastMessageEvents
.OnBroadcastMessage(uint msg
, IntPtr wParam
, IntPtr lParam
) {
294 int hr
= NativeMethods
.S_OK
;
295 IntPtr hwnd
= Window
.Handle
;
296 bool result
= UnsafeNativeMethods
.PostMessage(hwnd
, (int)msg
, wParam
, wParam
);
298 hr
= NativeMethods
.E_FAIL
;
302 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IVsWindowPane.ClosePane"]/*' />
305 /// IVsWindowPane implementation.
307 int IVsWindowPane
.ClosePane() {
309 if (_vsShell
!= null) {
310 NativeMethods
.ThrowOnFailure(_vsShell
.UnadviseBroadcastMessages(_broadcastEventCookie
));
312 _broadcastEventCookie
= 0;
316 return NativeMethods
.S_OK
;
319 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IVsWindowPane.CreatePaneWindow"]/*' />
322 /// IVsWindowPane implementation.
324 int IVsWindowPane
.CreatePaneWindow(IntPtr hwndParent
, int x
, int y
, int cx
, int cy
, out IntPtr pane
) {
327 IntPtr hwnd
= Window
.Handle
;
328 int style
= (int)UnsafeNativeMethods
.GetWindowLong(hwnd
, NativeMethods
.GWL_STYLE
);
330 // set up the required styles of an IVsWindowPane
331 style
|= (NativeMethods
.WS_CLIPSIBLINGS
| NativeMethods
.WS_CHILD
| NativeMethods
.WS_VISIBLE
);
332 style
&= ~
(NativeMethods
.WS_POPUP
|
333 NativeMethods
.WS_MINIMIZE
|
334 NativeMethods
.WS_MAXIMIZE
|
335 NativeMethods
.WS_DLGFRAME
|
336 NativeMethods
.WS_SYSMENU
|
337 NativeMethods
.WS_THICKFRAME
|
338 NativeMethods
.WS_MINIMIZEBOX
|
339 NativeMethods
.WS_MAXIMIZEBOX
);
341 UnsafeNativeMethods
.SetWindowLong(hwnd
, NativeMethods
.GWL_STYLE
, (IntPtr
)style
);
343 style
= (int)UnsafeNativeMethods
.GetWindowLong(hwnd
, NativeMethods
.GWL_EXSTYLE
);
345 style
&= ~
(NativeMethods
.WS_EX_DLGMODALFRAME
|
346 NativeMethods
.WS_EX_NOPARENTNOTIFY
|
347 NativeMethods
.WS_EX_TOPMOST
|
348 NativeMethods
.WS_EX_MDICHILD
|
349 NativeMethods
.WS_EX_TOOLWINDOW
|
350 NativeMethods
.WS_EX_CONTEXTHELP
|
351 NativeMethods
.WS_EX_APPWINDOW
);
353 UnsafeNativeMethods
.SetWindowLong(hwnd
, NativeMethods
.GWL_EXSTYLE
, (IntPtr
)style
);
354 UnsafeNativeMethods
.SetParent(hwnd
, (IntPtr
)hwndParent
);
355 UnsafeNativeMethods
.SetWindowPos(hwnd
, IntPtr
.Zero
, x
, y
, cx
, cy
, NativeMethods
.SWP_NOZORDER
| NativeMethods
.SWP_NOACTIVATE
);
356 UnsafeNativeMethods
.ShowWindow(hwnd
, NativeMethods
.SW_SHOWNORMAL
);
358 // Sync broadcast events so we update our UI when colors/fonts change.
360 if (_vsShell
== null) {
361 _vsShell
= (IVsShell
)GetService(typeof(SVsShell
));
362 if (_vsShell
!= null) {
363 NativeMethods
.ThrowOnFailure(_vsShell
.AdviseBroadcastMessages(this, out _broadcastEventCookie
));
368 return NativeMethods
.S_OK
;
371 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IVsWindowPane.GetDefaultSize"]/*' />
374 /// IVsWindowPane implementation.
376 int IVsWindowPane
.GetDefaultSize(SIZE
[] size
) {
377 return NativeMethods
.E_NOTIMPL
;
380 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IVsWindowPane.LoadViewState"]/*' />
383 /// IVsWindowPane implementation.
385 int IVsWindowPane
.LoadViewState(IStream pstream
) {
386 Marshal
.ReleaseComObject(pstream
);
387 return NativeMethods
.E_NOTIMPL
;
390 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IVsWindowPane.SaveViewState"]/*' />
393 /// IVsWindowPane implementation.
395 int IVsWindowPane
.SaveViewState(IStream pstream
) {
396 Marshal
.ReleaseComObject(pstream
);
397 return NativeMethods
.E_NOTIMPL
;
400 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IVsWindowPane.SetSite"]/*' />
403 /// IVsWindowPane implementation.
405 int IVsWindowPane
.SetSite(IOleServiceProvider p
) {
407 // The siting mechanism works as follows: If the
408 // parent provider provides ServiceProviderHierarchy
409 // as a service we will insert our service provider in
410 // the WindowPaneSite slot of the hierarchy.
411 // If, however, it does not provide
412 // this service, we will create a new
413 // ServiceProvider that will be used to resolve
414 // services through this site.
416 if (_provider
!= null) {
421 IObjectWithSite ows
= GetService(typeof(IObjectWithSite
)) as IObjectWithSite
;
422 ServiceProviderHierarchy serviceHierarchy
= GetService(typeof(ServiceProviderHierarchy
)) as ServiceProviderHierarchy
;
423 if (serviceHierarchy
!= null) {
424 ServiceProvider sp
= (p
== null ? null : new ServiceProvider(p
));
425 serviceHierarchy
[ServiceProviderHierarchyOrder
.WindowPaneSite
] = sp
;
427 else if (ows
!= null) {
432 _provider
= new ServiceProvider(p
);
439 return NativeMethods
.S_OK
;
442 /// <include file='doc\WindowPane.uex' path='docs/doc[@for="WindowPane.IVsWindowPane.TranslateAccelerator"]/*' />
445 /// IVsWindowPane implementation.
447 int IVsWindowPane
.TranslateAccelerator(Microsoft
.VisualStudio
.OLE
.Interop
.MSG
[] msg
) {
448 Message m
= Message
.Create(msg
[0].hwnd
, (int)msg
[0].message
, msg
[0].wParam
, msg
[0].lParam
);
449 bool eat
= PreProcessMessage(ref m
);
451 msg
[0].message
= (uint)m
.Msg
;
452 msg
[0].wParam
= m
.WParam
;
453 msg
[0].lParam
= m
.LParam
;
456 return NativeMethods
.S_OK
;
459 return NativeMethods
.E_FAIL
;