Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / WinForms / System / WinForms / Design / DesignerActionUI.cs / 2 / DesignerActionUI.cs
namespace System.Windows.Forms.Design { using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design; using System.Design; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Windows.Forms.Design.Behavior; using System.Text; ////// /// The DesignerActionUI is the designer/UI-specific implementation of the //// DesignerActions feature. This class instantiates the DesignerActionService /// and hooks to its DesignerActionsChanged event. Responding to this single /// event will enable the DesignerActionUI to perform all neceessary UI-related /// operations. /// Note that the DesignerActionUI uses the BehaviorService to manage all UI /// interaction. For every component containing a DesignerAction (determined /// by the DesignerActionsChagned event) there will be an associated /// DesignerActionGlyph and DesignerActionBehavior. /// Finally, the DesignerActionUI is also responsible for showing and managing /// the Action's context menus. Note that every DesignerAction context menu has /// an item that will bring up the DesignerActions option pane in the options /// dialog. /// internal class DesignerActionUI : IDisposable { private static TraceSwitch DesigneActionPanelTraceSwitch = new TraceSwitch("DesigneActionPanelTrace", "DesignerActionPanel tracing"); private Adorner designerActionAdorner;//used to add designeraction-related glyphs private IServiceProvider serviceProvider;//standard service provider private ISelectionService selSvc;//used to determine if comps have selection or not private DesignerActionService designerActionService;//this is how all designeractions will be managed private DesignerActionUIService designerActionUIService;//this is how all designeractions UI elements will be managed private BehaviorService behaviorService;//this is how all of our UI is implemented (glyphs, behaviors, etc...) private IMenuCommandService menuCommandService; private DesignerActionKeyboardBehavior dapkb; //out keyboard behavior private Hashtable componentToGlyph;//used for quick reference between compoments and our glyphs private Control marshalingControl;//used to invoke events on our main gui thread private IComponent lastPanelComponent; private IUIService uiService; private IWin32Window mainParentWindow; internal DesignerActionToolStripDropDown designerActionHost; private MenuCommand cmdShowDesignerActions;//used to respond to the Alt+Shft+F10 command private bool inTransaction = false; private IComponent relatedComponentTransaction; private DesignerActionGlyph relatedGlyphTransaction; private bool disposeActionService; private bool disposeActionUIService; private delegate void ActionChangedEventHandler(object sender, DesignerActionListsChangedEventArgs e); #if DEBUG internal static readonly TraceSwitch DropDownVisibilityDebug = new TraceSwitch("DropDownVisibilityDebug", "Debug ToolStrip Selection code"); #else internal static readonly TraceSwitch DropDownVisibilityDebug; #endif ////// /// Constructor that takes a service provider. This is needed to establish /// references to the BehaviorService and SelecteionService, as well as /// spin-up the DesignerActionService. /// public DesignerActionUI(IServiceProvider serviceProvider, Adorner containerAdorner) { this.serviceProvider = serviceProvider; this.designerActionAdorner = containerAdorner; behaviorService = (BehaviorService)serviceProvider.GetService(typeof(BehaviorService)); menuCommandService = (IMenuCommandService)serviceProvider.GetService(typeof(IMenuCommandService)); selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); if (behaviorService == null || selSvc == null) { Debug.Fail("Either BehaviorService or ISelectionService is null, cannot continue."); return; } //query for our DesignerActionService designerActionService = (DesignerActionService)serviceProvider.GetService(typeof(DesignerActionService)); if (designerActionService == null) { //start the service designerActionService = new DesignerActionService(serviceProvider); disposeActionService = true; } designerActionUIService = (DesignerActionUIService)serviceProvider.GetService(typeof(DesignerActionUIService)); if (designerActionUIService == null) { designerActionUIService = new DesignerActionUIService(serviceProvider); disposeActionUIService = true; } designerActionUIService.DesignerActionUIStateChange += new DesignerActionUIStateChangeEventHandler(this.OnDesignerActionUIStateChange); designerActionService.DesignerActionListsChanged += new DesignerActionListsChangedEventHandler(this.OnDesignerActionsChanged); lastPanelComponent = null; IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService)); if (cs != null) { cs.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged); } if (menuCommandService != null) { cmdShowDesignerActions = new MenuCommand(new EventHandler(OnKeyShowDesignerActions), MenuCommands.KeyInvokeSmartTag); menuCommandService.AddCommand(cmdShowDesignerActions); } uiService = (IUIService)serviceProvider.GetService(typeof(IUIService)); if(uiService != null) mainParentWindow = uiService.GetDialogOwnerWindow(); componentToGlyph = new Hashtable(); marshalingControl = new Control(); marshalingControl.CreateControl(); } ////// /// Disposes all UI-related objects and unhooks services. /// // Don't need to dispose of designerActionUIService. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] public void Dispose() { if (marshalingControl != null) { marshalingControl.Dispose(); marshalingControl = null; } if (serviceProvider != null) { IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService)); if (cs != null) { cs.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged); } if (cmdShowDesignerActions != null) { IMenuCommandService mcs = (IMenuCommandService)serviceProvider.GetService(typeof(IMenuCommandService)); if (mcs != null) { mcs.RemoveCommand(cmdShowDesignerActions); } } } serviceProvider = null; behaviorService = null; selSvc = null; if (designerActionService != null) { designerActionService.DesignerActionListsChanged -= new DesignerActionListsChangedEventHandler(this.OnDesignerActionsChanged); if (disposeActionService) { designerActionService.Dispose(); } } designerActionService = null; if (designerActionUIService != null) { designerActionUIService.DesignerActionUIStateChange -= new DesignerActionUIStateChangeEventHandler(this.OnDesignerActionUIStateChange); if (disposeActionUIService) { designerActionUIService.Dispose(); } } designerActionUIService = null; designerActionAdorner = null; } public DesignerActionGlyph GetDesignerActionGlyph(IComponent comp) { return GetDesignerActionGlyph(comp, null); } internal DesignerActionGlyph GetDesignerActionGlyph(IComponent comp, DesignerActionListCollection dalColl) { // check this component origin, this class or is it readyonly because inherited... InheritanceAttribute attribute = (InheritanceAttribute)TypeDescriptor.GetAttributes(comp)[typeof(InheritanceAttribute)]; if(attribute == InheritanceAttribute.InheritedReadOnly) { // only do it if we can change the control... return null; } // we didnt get on, fetch it if(dalColl == null) { dalColl = designerActionService.GetComponentActions(comp); } if(dalColl!= null && dalColl.Count > 0) { DesignerActionGlyph dag = null; if(componentToGlyph[comp] == null) { DesignerActionBehavior dab = new DesignerActionBehavior(serviceProvider, comp, dalColl, this); //if comp is a component then try to find a traycontrol associated with it... // this should really be in ComponentTray but there is no behaviorService for the CT if (!(comp is Control) || comp is ToolStripDropDown) { //Here, we'll try to get the traycontrol associated with //the comp and supply the glyph with an alternative bounds ComponentTray compTray = serviceProvider.GetService(typeof(ComponentTray)) as ComponentTray; if (compTray != null) { ComponentTray.TrayControl trayControl = compTray.GetTrayControlFromComponent(comp); if (trayControl != null) { Rectangle trayBounds = trayControl.Bounds; dag = new DesignerActionGlyph(dab, trayBounds, compTray); } } } //either comp is a control or we failed to find a traycontrol (which could be the case //for toolstripitem components) - in this case just create a standard glyoh. if (dag == null) { //if the related comp is a control, then this shortcut will just hang off its bounds dag = new DesignerActionGlyph(dab, designerActionAdorner); } if (dag != null) { //store off this relationship componentToGlyph.Add(comp, dag); } } else { dag = componentToGlyph[comp] as DesignerActionGlyph; if (dag != null) { DesignerActionBehavior behavior = dag.Behavior as DesignerActionBehavior; if (behavior != null) { behavior.ActionLists = dalColl; } dag.Invalidate(); // need to invalidate here too, someone could have called refresh too soon, //causing the glyph to get created in the wrong place } } return dag; } else { // the list is now empty... remove the panel and glyph for this control RemoveActionGlyph(comp); return null; } } ////// /// We monitor this event so we can update smart tag locations when /// controls move. /// private void OnComponentChanged(object source, ComponentChangedEventArgs ce) { //validate event args if (ce.Component == null || ce.Member == null || !IsDesignerActionPanelVisible) { return; } // VSWhidbey 497545. // If the smart tag is showing, we only move the smart tag if the changing // component is the component for the currently showing smart tag. if (lastPanelComponent != null && !lastPanelComponent.Equals(ce.Component)) { return; } //if something changed on a component we have actions associated with //then invalidate all (repaint & reposition) DesignerActionGlyph glyph = componentToGlyph[ce.Component] as DesignerActionGlyph; if (glyph != null) { glyph.Invalidate(); if(ce.Member.Name.Equals("Dock")) { // this is the only case were we don't require an explicit refresh RecreatePanel(ce.Component as IComponent); // because 99% of the time the action is name "dock in parent container" and get replaced by "undock" } if (ce.Member.Name.Equals("Location") || ce.Member.Name.Equals("Width") || ce.Member.Name.Equals("Height")) { // we don't need to regen, we just need to update location // calculate the position of the form hosting the panel UpdateDAPLocation(ce.Component as IComponent, glyph); } } } private void RecreatePanel(IComponent comp) { if(inTransaction || comp != selSvc.PrimarySelection) { //we only ever need to do that when the comp is the primary selection return; } // we check wether or not we're in a transaction, if we are, we only the refresh at the // end of the transaction to avoid flicker. IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; if(host!=null) { bool hostIsClosingTransaction = false; IDesignerHostTransactionState hostTransactionState = host as IDesignerHostTransactionState; if (hostTransactionState != null) { hostIsClosingTransaction = hostTransactionState.IsClosingTransaction; } if (host.InTransaction && !hostIsClosingTransaction) { //Debug.WriteLine("In transaction, bail, but first hookup to the end of the transaction..."); host.TransactionClosed += new DesignerTransactionCloseEventHandler(this.DesignerTransactionClosed); inTransaction = true; relatedComponentTransaction = comp; return; } } RecreateInternal(comp); } private void DesignerTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) { if(e.LastTransaction && relatedComponentTransaction != null) { // surprise surprise we can get multiple even with e.LastTransaction set to true, even though we unhook here // this is because the list on which we enumerate (the event handler list) is copied before it's enumerated on // which means that if the undo engine for example creates and commit a transaction during the OnCancel of another // completed transaction we will get this twice. So we have to check also for relatedComponentTransaction != null inTransaction = false; //Debug.WriteLine("End of the transaction, refresh..."); IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; host.TransactionClosed -= new DesignerTransactionCloseEventHandler(this.DesignerTransactionClosed); //Debug.WriteLine("End of the transaction, refresh... on component"); RecreateInternal(relatedComponentTransaction); relatedComponentTransaction = null; } } private void RecreateInternal(IComponent comp) { //Debug.WriteLine("not in a transaction, do it now!"); DesignerActionGlyph glyph = GetDesignerActionGlyph(comp); if (glyph != null) { //Debug.WriteLine("Recreating panel for component " + comp.Site.Name); VerifyGlyphIsInAdorner(glyph); // this could happen when a verb change state or suddendly a control gets // a new action in the panel and we are the primary selection // in that case there would not be a glyph active in the adorner to be shown // because we update that on selection change. We have to do that here too. Sad really... RecreatePanel(glyph); // recreate the DAP itself UpdateDAPLocation(comp, glyph); // reposition the thing } } private void RecreatePanel(Glyph glyphWithPanelToRegen) { // we don't want to do anything if the panel is not visible if(!IsDesignerActionPanelVisible) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.RecreatePanel] panel is not visible, bail"); return; } //recreate a designeraction panel if(glyphWithPanelToRegen != null) { DesignerActionBehavior behaviorWithPanelToRegen = glyphWithPanelToRegen.Behavior as DesignerActionBehavior; if(behaviorWithPanelToRegen!= null) { //DesignerActionPanel dap = behaviorWithPanelToRegen.CreateDesignerActionPanel(behaviorWithPanelToRegen.RelatedComponent); //designerActionHost.SetDesignerActionPanel(dap, glyphWithPanelToRegen); Debug.Assert(behaviorWithPanelToRegen.RelatedComponent != null, "could not find related component for this refresh"); DesignerActionPanel dap = designerActionHost.CurrentPanel; // WE DO NOT RECREATE THE WHOLE THING / WE UPDATE THE TASKS - should flicker less dap.UpdateTasks(behaviorWithPanelToRegen.ActionLists, new DesignerActionListCollection(), SR.GetString(SR.DesignerActionPanel_DefaultPanelTitle, behaviorWithPanelToRegen.RelatedComponent.GetType().Name), null); designerActionHost.UpdateContainerSize(); } } } private void VerifyGlyphIsInAdorner(DesignerActionGlyph glyph) { if (glyph.IsInComponentTray) { ComponentTray compTray = serviceProvider.GetService(typeof(ComponentTray)) as ComponentTray; if(compTray.SelectionGlyphs != null && !compTray.SelectionGlyphs.Contains(glyph)) { compTray.SelectionGlyphs.Insert(0, glyph); } } else { if(designerActionAdorner != null && designerActionAdorner.Glyphs != null && !designerActionAdorner.Glyphs.Contains(glyph)) { designerActionAdorner.Glyphs.Insert(0,glyph); } } glyph.InvalidateOwnerLocation(); } ////// This event is fired by the DesignerActionService in response /// to a DesignerActionCollection changing. The event args contains /// information about the related object, the type of change (added /// or removed) and the remaining DesignerActionCollection for the /// object. /// Note that when new DesignerActions are added, if the related control /// is not yet parented - we add these actions to a "delay" list and they /// are later created when the control is finally parented. /// private void OnDesignerActionsChanged(object sender, DesignerActionListsChangedEventArgs e) { // We need to invoke this async because the designer action service will // raise this event from the thread pool. if (marshalingControl != null && marshalingControl.IsHandleCreated) { marshalingControl.BeginInvoke(new ActionChangedEventHandler(OnInvokedDesignerActionChanged), new object[] {sender, e}); } } private void OnDesignerActionUIStateChange(object sender, DesignerActionUIStateChangeEventArgs e) { IComponent comp = e.RelatedObject as IComponent; Debug.Assert(comp!=null || e.ChangeType == DesignerActionUIStateChangeType.Hide, "related object is not an IComponent, something is wrong here..."); if(comp!=null) { DesignerActionGlyph relatedGlyph = GetDesignerActionGlyph(comp); if(relatedGlyph != null) { if(e.ChangeType == DesignerActionUIStateChangeType.Show) { DesignerActionBehavior behavior = relatedGlyph.Behavior as DesignerActionBehavior; if(behavior != null) { behavior.ShowUI(relatedGlyph); } } else if (e.ChangeType == DesignerActionUIStateChangeType.Hide){ DesignerActionBehavior behavior = relatedGlyph.Behavior as DesignerActionBehavior; if(behavior != null) { behavior.HideUI(); } } else if (e.ChangeType == DesignerActionUIStateChangeType.Refresh) { relatedGlyph.Invalidate(); RecreatePanel((IComponent)e.RelatedObject); /*BehaviorService.MenuCommandHandler mch = menuCommandService as BehaviorService.MenuCommandHandler; if(mch != null && mch.MenuService != null) { MenuCommandService mcs = mch.MenuService as MenuCommandService; if(mcs != null) { mcs.InvalidateVerbsCollection(); } else { Debug.Fail("Could not find our way to the real MenuCommandService"); } } else { Debug.Fail("Could not find our way to the real MenuCommandService"); }*/ } } } else { if (e.ChangeType == DesignerActionUIStateChangeType.Hide){ HideDesignerActionPanel(); } } } ////// This is the same as DesignerActionChanged, but it is invoked on our control's thread /// private void OnInvokedDesignerActionChanged(object sender, DesignerActionListsChangedEventArgs e) { IComponent relatedComponent = e.RelatedObject as IComponent; DesignerActionGlyph g = null; if (e.ChangeType == DesignerActionListsChangedType.ActionListsAdded) { if (relatedComponent == null) { Debug.Fail("How can we add a DesignerAction glyphs when it's related object is not an IComponent?"); return; } IComponent primSel = selSvc.PrimarySelection as IComponent; if (primSel == e.RelatedObject) { g = GetDesignerActionGlyph(relatedComponent , e.ActionLists); if(g != null) { VerifyGlyphIsInAdorner(g); } else { RemoveActionGlyph(e.RelatedObject); } } } if (e.ChangeType == DesignerActionListsChangedType.ActionListsRemoved && e.ActionLists.Count == 0) { //only remove our glyph if there are no more DesignerActions //associated with it. RemoveActionGlyph(e.RelatedObject); } else if(g!=null) { // we need to recreate the panel here, since it's content has changed... RecreatePanel(relatedComponent); } } ////// Called when our KeyShowDesignerActions menu command is fired /// (a.k.a. Alt+Shift+F10) - we will find the primary selection, /// see if it has designer actions, and if so - show the menu. /// private void OnKeyShowDesignerActions(object sender, EventArgs e) { ShowDesignerActionPanelForPrimarySelection(); } // we cannot attach several menu command to the same command id, we need // a single entry point, we put it in designershortcutui. but we need a way to call the show ui on the related behavior // hence this internal function to hack it together //we return false if we have nothing to display, we hide it and return true if we're already displaying internal bool ShowDesignerActionPanelForPrimarySelection() { //can't do anythign w/o selection service if (selSvc == null) { return false; } object primarySelection = selSvc.PrimarySelection; //verfiy that we have obtained a valid component with designer actions if (primarySelection == null || !componentToGlyph.Contains(primarySelection)) { return false; } DesignerActionGlyph glyph = (DesignerActionGlyph)componentToGlyph[primarySelection]; if (glyph != null && glyph.Behavior is DesignerActionBehavior) { // show the menu DesignerActionBehavior behavior = glyph.Behavior as DesignerActionBehavior; if(behavior != null) { if(!IsDesignerActionPanelVisible) { behavior.ShowUI(glyph); return true; } else { behavior.HideUI(); return false; } } } return false; } ////// When all the DesignerActions have been removed for a particular /// object, we remove any UI (glyphs) that we may have been managing. /// internal void RemoveActionGlyph(object relatedObject) { if (relatedObject == null) { return; } if(IsDesignerActionPanelVisible && relatedObject == lastPanelComponent) { HideDesignerActionPanel(); } DesignerActionGlyph glyph = (DesignerActionGlyph)componentToGlyph[relatedObject]; if (glyph != null) { // Check ComponentTray first ComponentTray compTray = serviceProvider.GetService(typeof(ComponentTray)) as ComponentTray; if(compTray != null && compTray.SelectionGlyphs != null) { if (compTray != null && compTray.SelectionGlyphs.Contains(glyph)) { compTray.SelectionGlyphs.Remove(glyph); } } if(designerActionAdorner.Glyphs.Contains(glyph)) { designerActionAdorner.Glyphs.Remove(glyph); } componentToGlyph.Remove(relatedObject); // we only do this when we're in a transaction, see bug VSWHIDBEY 418709. This is for compat reason - infragistic. if we're not in a transaction, too bad, we don't update the screen IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; if(host!=null && host.InTransaction) { host.TransactionClosed += new DesignerTransactionCloseEventHandler(this.InvalidateGlyphOnLastTransaction); relatedGlyphTransaction = glyph; } } } private void InvalidateGlyphOnLastTransaction(object sender, DesignerTransactionCloseEventArgs e) { if(e.LastTransaction) { IDesignerHost host = (serviceProvider != null) ? serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost : null; if (host != null) { host.TransactionClosed -= new DesignerTransactionCloseEventHandler(this.InvalidateGlyphOnLastTransaction); } if(relatedGlyphTransaction != null) { relatedGlyphTransaction.InvalidateOwnerLocation(); } relatedGlyphTransaction = null; } } internal void HideDesignerActionPanel() { if(IsDesignerActionPanelVisible) { designerActionHost.Close(); } } internal bool IsDesignerActionPanelVisible { get { return (designerActionHost != null && designerActionHost.Visible); } } internal IComponent LastPanelComponent { get { return (IsDesignerActionPanelVisible ? this.lastPanelComponent : null); } } private void toolStripDropDown_Closing(object sender, ToolStripDropDownClosingEventArgs e) { if (cancelClose || e.Cancel) { e.Cancel = true; Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] cancelClose true, bail"); return; } if (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked) { e.Cancel = true; Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] ItemClicked: e.Cancel set to: " + e.Cancel.ToString()); } if(e.CloseReason == ToolStripDropDownCloseReason.Keyboard) { e.Cancel = false; Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] Keyboard: e.Cancel set to: " + e.Cancel.ToString()); } if(e.Cancel == false) { // we WILL disappear Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI.toolStripDropDown_Closing] Closing..."); Debug.Assert(lastPanelComponent!=null, "last panel component should not be null here... "+ "(except if you're currently debugging VS where deactivation messages in the middle of the pump can mess up everything...)"); if(lastPanelComponent == null) return; // if we're actually closing // get the coordinate of the last message, the one causing us to close, is it within the glyph coordinate // if it is that mean that someone just clicked back from the panel, on VS, but ON THE GLYPH, that means that he // actually wants to close it. The activation change is going to do that for us but we should NOT reopen right away // because he clicked on the glyph... this code is here to prevent this... Point point = DesignerUtils.LastCursorPoint; DesignerActionGlyph currentGlyph = componentToGlyph[lastPanelComponent] as DesignerActionGlyph; if(currentGlyph != null) { Point glyphCoord = GetGlyphLocationScreenCoord(lastPanelComponent, currentGlyph); if((new Rectangle(glyphCoord, new Size(currentGlyph.Bounds.Width, currentGlyph.Bounds.Height))).Contains(point)) { DesignerActionBehavior behavior = currentGlyph.Behavior as DesignerActionBehavior; behavior.IgnoreNextMouseUp = true; } currentGlyph.InvalidateOwnerLocation(); } // unset the ownership relationship /* UnsafeNativeMethods.SetWindowLong(new HandleRef(designerActionHost, designerActionHost.Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(null, IntPtr.Zero)); */ lastPanelComponent = null; // panel is going away, pop the behavior that's on the stack... Debug.Assert(dapkb != null, "why is dapkb null?"); System.Windows.Forms.Design.Behavior.Behavior popBehavior = behaviorService.PopBehavior(dapkb); Debug.Assert(popBehavior is DesignerActionKeyboardBehavior, "behavior returned is of the wrong kind?"); } } internal Point UpdateDAPLocation(IComponent component, DesignerActionGlyph glyph) { if(component == null) { // in case of a resize... component = lastPanelComponent; } if(designerActionHost == null) { return Point.Empty; } if (component == null || glyph == null ) { return designerActionHost.Location; } // check that the glyph is still visible in the adorner window if(behaviorService != null && behaviorService.AdornerWindowGraphics != null && !behaviorService.AdornerWindowControl.DisplayRectangle.IntersectsWith(glyph.Bounds)) { HideDesignerActionPanel(); return designerActionHost.Location; } Point glyphLocationScreenCoord = GetGlyphLocationScreenCoord(component, glyph); Rectangle rectGlyph = new Rectangle(glyphLocationScreenCoord, glyph.Bounds.Size); DockStyle edgeToDock; Point pt = DesignerActionPanel.ComputePreferredDesktopLocation(rectGlyph, designerActionHost.Size, out edgeToDock); glyph.DockEdge = edgeToDock; designerActionHost.Location = pt; return pt; } private Point GetGlyphLocationScreenCoord(IComponent relatedComponent, Glyph glyph) { Point glyphLocationScreenCoord = new Point(0,0); if(relatedComponent is Control && !(relatedComponent is ToolStripDropDown)) { Control relatedControl = relatedComponent as Control; glyphLocationScreenCoord =behaviorService.AdornerWindowPointToScreen(glyph.Bounds.Location); } //ISSUE: we can't have this special cased here - we should find a more //generic approach to solving this problem else if (relatedComponent is ToolStripItem) { ToolStripItem item = relatedComponent as ToolStripItem; if (item != null && item.Owner != null) { glyphLocationScreenCoord = behaviorService.AdornerWindowPointToScreen(glyph.Bounds.Location); } } else if (relatedComponent is IComponent) { ComponentTray compTray = serviceProvider.GetService(typeof(ComponentTray)) as ComponentTray; if (compTray != null) { glyphLocationScreenCoord = compTray.PointToScreen(glyph.Bounds.Location); } } return glyphLocationScreenCoord; } bool cancelClose = false; ////// This shows the actual chrome paenl that is created by the /// DesignerActionBehavior object. /// internal void ShowDesignerActionPanel(IComponent relatedComponent, DesignerActionPanel panel, DesignerActionGlyph glyph) { if(designerActionHost ==null) { designerActionHost = new DesignerActionToolStripDropDown(this, mainParentWindow); designerActionHost.AutoSize = false; designerActionHost.Padding = Padding.Empty; designerActionHost.Renderer = new NoBorderRenderer(); designerActionHost.Text = "DesignerActionTopLevelForm"; designerActionHost.Closing +=new ToolStripDropDownClosingEventHandler(toolStripDropDown_Closing); } // set the accessible name of the panel to the same title as the panel header. do that every time designerActionHost.AccessibleName = SR.GetString(SR.DesignerActionPanel_DefaultPanelTitle, relatedComponent.GetType().Name); panel.AccessibleName = SR.GetString(SR.DesignerActionPanel_DefaultPanelTitle, relatedComponent.GetType().Name); //GetDesignerActionGlyph(relatedComponent); // only here to update the ActionList collection on the behavior designerActionHost.SetDesignerActionPanel(panel, glyph); Point location = UpdateDAPLocation(relatedComponent, glyph); // check that the panel will have at least it's parent glyph visible on the adorner window if(behaviorService != null && behaviorService.AdornerWindowGraphics != null && behaviorService.AdornerWindowControl.DisplayRectangle.IntersectsWith(glyph.Bounds)) { //behaviorService.AdornerWindowGraphics.IsVisible(glyph.Bounds)) { if (mainParentWindow != null && mainParentWindow.Handle != IntPtr.Zero) { Debug.WriteLineIf(DesigneActionPanelTraceSwitch.TraceVerbose, "Assigning owner to mainParentWindow"); Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "Assigning owner to mainParentWindow"); UnsafeNativeMethods.SetWindowLong(new HandleRef(designerActionHost, designerActionHost.Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(mainParentWindow, mainParentWindow.Handle)); } // designerActionHost.AutoClose = false; cancelClose = true; designerActionHost.Show(location); designerActionHost.Focus(); // when a control is drag and dropped and authoshow is set to true // the vs designer is going to get activated as soon as the control is dropped // we don't want to close the panel then, so we post a message (using the trick to // call begin invoke) and once everything is settled re-activate the autoclose logic designerActionHost.BeginInvoke(new EventHandler(OnShowComplete)); // invalidate the glyph to have it point the other way glyph.InvalidateOwnerLocation(); lastPanelComponent = relatedComponent; // push new behavior for keyboard handling on the behavior stack dapkb = new DesignerActionKeyboardBehavior(designerActionHost.CurrentPanel, serviceProvider, behaviorService); behaviorService.PushBehavior(dapkb); } } private void OnShowComplete(object sender, EventArgs e) { // designerActionHost.AutoClose = true; cancelClose = false; // force the panel to be the active window - for some reason someone else could have forced VS to become // active for real while we were ignoring close. This might be bad cause we'd be in a bad state. if(designerActionHost != null && designerActionHost.Handle != IntPtr.Zero && designerActionHost.Visible) { UnsafeNativeMethods.SetActiveWindow(new HandleRef(this, designerActionHost.Handle)); designerActionHost.CheckFocusIsRight(); } } } internal class DesignerActionToolStripDropDown : ToolStripDropDown { private IWin32Window _mainParentWindow; private ToolStripControlHost _panel; private DesignerActionUI _designerActionUI; private bool _cancelClose = false; private Glyph relatedGlyph; public DesignerActionToolStripDropDown(DesignerActionUI designerActionUI, IWin32Window mainParentWindow) { _mainParentWindow = mainParentWindow; _designerActionUI = designerActionUI; } public DesignerActionPanel CurrentPanel { get { if(this._panel != null) { return _panel.Control as DesignerActionPanel; } else { return null; } } } // we're not topmost because we can show modal editors above us. protected override bool TopMost { get { return false; } } public void UpdateContainerSize() { if (CurrentPanel != null) { Size panelSize = CurrentPanel.GetPreferredSize(new Size(150, Int32.MaxValue)); if (CurrentPanel.Size == panelSize) { // If the panel size didn't actually change, we still have to force // a call to PerformLayout to make sure that controls get repositioned // properly within the panel. The issue arises because we did a // measure-only Layout that determined some sizes, and then we end up // painting with those values even though there wasn't an actual Layout // performed. CurrentPanel.PerformLayout(); } else { CurrentPanel.Size = panelSize; } ClientSize = panelSize; } } public void CheckFocusIsRight() { // hack to get the focus to NOT stay on ContainerControl Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "Checking focus..."); IntPtr focusedControl = UnsafeNativeMethods.GetFocus(); if(focusedControl == this.Handle) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, " putting focus on the panel..."); _panel.Focus(); } focusedControl = UnsafeNativeMethods.GetFocus(); if(CurrentPanel != null && CurrentPanel.Handle == focusedControl) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, " selecting next available control on the panel..."); CurrentPanel.SelectNextControl(null, true, true, true, true); } focusedControl = UnsafeNativeMethods.GetFocus(); } protected override void OnLayout(LayoutEventArgs levent) { base.OnLayout(levent); UpdateContainerSize(); } protected override void OnClosing(ToolStripDropDownClosingEventArgs e) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "_____________________________Begin OnClose " + e.CloseReason.ToString()); Debug.Indent(); if (e.CloseReason == ToolStripDropDownCloseReason.AppFocusChange && _cancelClose) { _cancelClose = false; e.Cancel = true; Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "cancel close prepopulated"); } // when we get closing event as a result of an activation change, // pre-populate e.Cancel based on why we're exiting. // // - if it's a modal window that's owned by VS dont exit // - if it's a window that's owned by the toolstrip dropdown dont exit else if (e.CloseReason == ToolStripDropDownCloseReason.AppFocusChange || e.CloseReason == ToolStripDropDownCloseReason.AppClicked) { IntPtr hwndActivating = UnsafeNativeMethods.GetActiveWindow(); if (this.Handle == hwndActivating && e.CloseReason == ToolStripDropDownCloseReason.AppClicked) { e.Cancel = false; Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] activation hasnt changed, but we've certainly clicked somewhere else."); } else if(WindowOwnsWindow(this.Handle, hwndActivating)) { // we're being de-activated for someone owned by the panel e.Cancel = true; Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] Cancel close - the window activating is owned by this window"); } else if(_mainParentWindow != null && !WindowOwnsWindow(_mainParentWindow.Handle, hwndActivating)) { if (IsWindowEnabled(_mainParentWindow.Handle)) { // the activated windows is not a child/owned windows of the main top level windows // let toolstripdropdown handle this e.Cancel = false; Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] Call close: the activated windows is not a child/owned windows of the main top level windows "); } else { e.Cancel = true; Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] we're being deactivated by a foreign window, but the main window is not enabled - we should stay up"); } base.OnClosing(e); Debug.Unindent(); Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "_____________________________End OnClose e.Cancel: " + e.Cancel.ToString() ); return; } else { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] since the designer action panel dropdown doesnt own the activating window " + hwndActivating.ToString("x") + ", calling close. "); } // what's the owner of the windows being activated? IntPtr parent = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, hwndActivating), NativeMethods.GWL_HWNDPARENT); // is it currently disabled (ie, the activating windows is in modal mode) if(!IsWindowEnabled(parent)) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] modal window activated - cancelling close"); // we are in a modal case e.Cancel = true; } } else { } Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.OnClosing] calling base.OnClosing with e.Cancel: " + e.Cancel.ToString()); base.OnClosing(e); Debug.Unindent(); Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "_____________________________End OnClose e.Cancel: " + e.Cancel.ToString()); } public void SetDesignerActionPanel(DesignerActionPanel panel, Glyph relatedGlyph) { if(_panel != null && panel == (DesignerActionPanel)_panel.Control) return; Debug.Assert(relatedGlyph != null, "related glyph cannot be null"); this.relatedGlyph = relatedGlyph; panel.SizeChanged += new EventHandler(PanelResized); // hook up the event if( _panel != null) { Items.Remove(_panel); _panel.Dispose(); _panel = null; } _panel = new ToolStripControlHost(panel); // we don't want no margin _panel.Margin = Padding.Empty; _panel.Size = panel.Size; this.SuspendLayout(); this.Size = panel.Size; this.Items.Add(_panel); this.ResumeLayout(); if(this.Visible) { CheckFocusIsRight(); } } private void PanelResized(object sender, System.EventArgs e) { Control ctrl = sender as Control; if(this.Size.Width != ctrl.Size.Width || this.Size.Height != ctrl.Size.Height) { this.SuspendLayout(); this.Size = ctrl.Size; if(_panel != null) { _panel.Size = ctrl.Size; } _designerActionUI.UpdateDAPLocation(null, relatedGlyph as DesignerActionGlyph); this.ResumeLayout(); } } protected override void SetVisibleCore(bool visible) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionToolStripDropDown.SetVisibleCore] setting dropdown visible=" + visible.ToString()); base.SetVisibleCore(visible); if(visible) { CheckFocusIsRight(); } } ////// General purpose method, based on Control.Contains()... /// /// Determines whether a given window (specified using native window handle) /// is a descendant of this control. This catches both contained descendants /// and 'owned' windows such as modal dialogs. Using window handles rather /// than Control objects allows it to catch un-managed windows as well. /// private static bool WindowOwnsWindow(IntPtr hWndOwner, IntPtr hWndDescendant) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[WindowOwnsWindow] Testing if " + hWndOwner.ToString("x")+ " is a owned by " + hWndDescendant.ToString("x") + "... "); #if DEBUG if (DesignerActionUI.DropDownVisibilityDebug.TraceVerbose) { Debug.WriteLine("\t\tOWNER: " + GetControlInformation(hWndOwner)); Debug.WriteLine("\t\tOWNEE: " + GetControlInformation(hWndDescendant)); IntPtr claimedOwnerHwnd = UnsafeNativeMethods.GetWindowLong(new HandleRef(null, hWndDescendant), NativeMethods.GWL_HWNDPARENT); Debug.WriteLine("OWNEE's CLAIMED OWNER: "+ GetControlInformation(claimedOwnerHwnd)); } #endif if (hWndDescendant == hWndOwner) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "they match, YES."); return true; } while (hWndDescendant != IntPtr.Zero) { hWndDescendant = UnsafeNativeMethods.GetWindowLong(new HandleRef(null, hWndDescendant), NativeMethods.GWL_HWNDPARENT); if (hWndDescendant == IntPtr.Zero) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "NOPE."); return false; } if (hWndDescendant == hWndOwner) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "YES."); return true; } } Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "NO."); return false; } // helper function for generating infomation about a particular control // use AssertControlInformation if sticking in an assert - then the work // to figure out the control info will only be done when the assertion is false. internal static string GetControlInformation(IntPtr hwnd) { if (hwnd == IntPtr.Zero) { return "Handle is IntPtr.Zero"; } #if DEBUG if (!DesignerActionUI.DropDownVisibilityDebug.TraceVerbose) { return String.Empty; } int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(null, hwnd)); StringBuilder sb = new StringBuilder(textLen+1); UnsafeNativeMethods.GetWindowText(new HandleRef(null, hwnd), sb, sb.Capacity); string typeOfControl = "Unknown"; string nameOfControl = ""; Control c = Control.FromHandle(hwnd); if (c != null) { typeOfControl = c.GetType().Name; if (!string.IsNullOrEmpty(c.Name)) { nameOfControl += c.Name; } else { nameOfControl += "Unknown"; ToolStripDropDown dd = c as ToolStripDropDown; // some extra debug info for toolstripdropdowns... if (dd != null) { if (dd.OwnerItem != null) { nameOfControl += "OwnerItem: [" + dd.OwnerItem.ToString()+ "]"; } } } } return sb.ToString() + "\r\n\t\t\tType: [" + typeOfControl + "] Name: [" + nameOfControl + "]"; #else return String.Empty; #endif } private bool IsWindowEnabled(IntPtr handle) { int style = (int) UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_STYLE); return (style & NativeMethods.WS_DISABLED) == 0; } private void WmActivate(ref Message m) { if((int)m.WParam == NativeMethods.WA_INACTIVE) { IntPtr hwndActivating = m.LParam; if(WindowOwnsWindow(this.Handle, hwndActivating)) { Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI WmActivate] setting cancel close true because WindowsOwnWindow"); Debug.WriteLineIf(DesignerActionUI.DropDownVisibilityDebug.TraceVerbose, "[DesignerActionUI WmActivate] checking the focus... " + GetControlInformation(UnsafeNativeMethods.GetFocus())); _cancelClose = true; } else { _cancelClose = false; } } else { _cancelClose = false; } base.WndProc(ref m); } protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_ACTIVATE: WmActivate(ref m); return; } base.WndProc(ref m); } protected override bool ProcessDialogKey(Keys keyData) { // since we're not hosted in a form we need to do the same logic // as Form.cs. If we get an enter key we need to find the current focused control // if it's a button, we click it and return that we handled the message if(keyData == Keys.Enter) { IntPtr focusedControlPtr = UnsafeNativeMethods.GetFocus(); Control focusedControl = Control.FromChildHandle(focusedControlPtr); IButtonControl button = focusedControl as IButtonControl; if (button != null && button is Control) { button.PerformClick(); return true; } } /* should not need that anymore... *//* if ( keyData == (Keys.Menu | Keys.Alt) || keyData == Keys.F4 || keyData == (Keys.Alt | Keys.Down) || keyData == (Keys.Alt | Keys.Up)) { //HACK HACK HACK DesignerActionPanel should handle message routing properly // I don't think that's the case now. checking this in to get the suite to pass. Here we prevent VS from getting the F4 IntPtr focusedControlPtr = UnsafeNativeMethods.GetFocus(); if(WindowOwnsWindow(this.Handle, focusedControlPtr)) { // we don't want VS to even get the message, but we want to // make sure it'll cause an OnKeyDown on the panel (who has focus) return false; } }*/ return base.ProcessDialogKey(keyData); } } internal class NoBorderRenderer : ToolStripProfessionalRenderer { protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DataSourceHelper.cs
- DataServiceKeyAttribute.cs
- ACE.cs
- _NativeSSPI.cs
- ClientRuntimeConfig.cs
- SQLStringStorage.cs
- PathFigure.cs
- CompilationUnit.cs
- ComponentEditorForm.cs
- ChangeDirector.cs
- TdsParserHelperClasses.cs
- MultiDataTrigger.cs
- Base64Stream.cs
- ContainerActivationHelper.cs
- QueryStringParameter.cs
- SettingsPropertyCollection.cs
- NamespaceInfo.cs
- FocusManager.cs
- OrderedEnumerableRowCollection.cs
- __FastResourceComparer.cs
- PropertyToken.cs
- DispatchOperationRuntime.cs
- DefaultCommandExtensionCallback.cs
- EntityDataSourceState.cs
- StsCommunicationException.cs
- TranslateTransform3D.cs
- TableChangeProcessor.cs
- ProxyDataContractResolver.cs
- GuidelineCollection.cs
- AlignmentYValidation.cs
- GridErrorDlg.cs
- FocusTracker.cs
- ColorConverter.cs
- CategoryNameCollection.cs
- DummyDataSource.cs
- MembershipUser.cs
- ToolStripSystemRenderer.cs
- StatusStrip.cs
- QuadraticBezierSegment.cs
- Line.cs
- TemplatedControlDesigner.cs
- XpsException.cs
- RelativeSource.cs
- XslCompiledTransform.cs
- RequestQueue.cs
- TableLayoutStyle.cs
- SortKey.cs
- BypassElement.cs
- Hash.cs
- AuthenticationService.cs
- InstanceKeyCompleteException.cs
- Material.cs
- QilUnary.cs
- SctClaimSerializer.cs
- ObjectQuery_EntitySqlExtensions.cs
- ComponentManagerBroker.cs
- XmlName.cs
- SchemaConstraints.cs
- TextBoxBaseDesigner.cs
- ZipPackagePart.cs
- ScrollChrome.cs
- MemberInitExpression.cs
- ToolStripItemCollection.cs
- SharedPersonalizationStateInfo.cs
- TextParagraphView.cs
- LockedAssemblyCache.cs
- And.cs
- DataSetMappper.cs
- Bold.cs
- KeyedCollection.cs
- Int64AnimationBase.cs
- DbConnectionPool.cs
- MimeBasePart.cs
- EdmSchemaAttribute.cs
- HelpEvent.cs
- AddInToken.cs
- BitmapEffectGroup.cs
- PerformanceCounterPermission.cs
- InkCollectionBehavior.cs
- Main.cs
- FtpWebResponse.cs
- NavigationWindow.cs
- CollectionChangeEventArgs.cs
- ZipIOFileItemStream.cs
- NamespaceMapping.cs
- ItemAutomationPeer.cs
- PerfCounterSection.cs
- ErrorFormatter.cs
- UnsafePeerToPeerMethods.cs
- SQLBytes.cs
- DiscoveryClientOutputChannel.cs
- WindowShowOrOpenTracker.cs
- DataGridViewTextBoxCell.cs
- WebConfigurationHost.cs
- DialogBaseForm.cs
- FormViewRow.cs
- ACE.cs
- MetabaseServerConfig.cs
- dataobject.cs
- DebugView.cs