Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / WinForms / System / WinForms / Design / Behavior / SelectionManager.cs / 1 / SelectionManager.cs
namespace System.Windows.Forms.Design.Behavior {
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Design;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms.Design;
///
///
/// The SelectionBehavior is pushed onto the BehaviorStack in response to a
/// positively hit tested SelectionGlyph. The SelectionBehavior performs
/// two main tasks: 1) forward messages to the related ControlDesigner, and 2)
/// calls upon the SelectionManager to push a potention DragBehavior.
///
internal sealed class SelectionManager : IDisposable {
private Adorner selectionAdorner;//used to provide all selection glyphs
private Adorner bodyAdorner;//used to track all body glyphs for each control
private BehaviorService behaviorService;//ptr back to our BehaviorService
private IServiceProvider serviceProvider;//standard service provider
private Hashtable componentToDesigner;//used for quick look up of designers related to comps
private Control rootComponent;//root component being designed
private ISelectionService selSvc;//we cache the selection service for perf.
private IDesignerHost designerHost;//we cache the designerhost for perf.
private bool needRefresh; // do we need to refresh?
private Rectangle[] prevSelectionBounds;//used to only repaint the changing part of the selection
private object prevPrimarySelection; //used to check if the primary selection changed
private Rectangle[] curSelectionBounds;
private int curCompIndex;
private DesignerActionUI designerActionUI = null; // the "container" for all things related to the designer action (smartags) UI
private bool selectionChanging; //we dont want the OnSelectionChanged to be recursively called.
///
///
/// Constructor. Here we query for necessary services and cache them for perf. reasons.
/// We also hook to Component Added/Removed/Changed notifications so we can keep in sync
/// when the designers' components change. Also, we create our custom Adorner and add
/// it to the BehaviorService.
///
public SelectionManager(IServiceProvider serviceProvider, BehaviorService behaviorService) {
prevSelectionBounds = null;
prevPrimarySelection = null;
this.behaviorService = behaviorService;
this.serviceProvider = serviceProvider;
selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService));
designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost));
if (designerHost == null || selSvc == null) {
Debug.Fail("SelectionManager - Host or SelSvc is null, can't continue");
}
//sync the BehaviorService's begindrag event
behaviorService.BeginDrag += new BehaviorDragDropEventHandler(this.OnBeginDrag);
//sync the BehaviorService's Synchronize event
behaviorService.Synchronize += new EventHandler(this.OnSynchronize);
selSvc.SelectionChanged += new EventHandler(this.OnSelectionChanged);
rootComponent = (Control)designerHost.RootComponent;
//create and add both of our adorners,
//one for selection, one for bodies
selectionAdorner = new Adorner();
bodyAdorner = new Adorner();
behaviorService.Adorners.Add(bodyAdorner);
behaviorService.Adorners.Add(selectionAdorner);//adding this will cause the adorner to get setup with a ptr to the beh.svc.
componentToDesigner = new Hashtable();
IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService));
if (cs != null) {
cs.ComponentAdded += new ComponentEventHandler(this.OnComponentAdded);
cs.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemoved);
cs.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
}
designerHost.TransactionClosed += new DesignerTransactionCloseEventHandler(OnTransactionClosed);
// designeraction UI
DesignerOptionService options = designerHost.GetService(typeof(DesignerOptionService)) as DesignerOptionService;
if (options != null) {
PropertyDescriptor p = options.Options.Properties["UseSmartTags"];
if (p != null && p.PropertyType == typeof(bool) && (bool)p.GetValue(null)) {
designerActionUI = new DesignerActionUI(serviceProvider, selectionAdorner);
behaviorService.DesignerActionUI = designerActionUI;
}
}
}
///
///
/// Returns the Adorner that contains all the
/// BodyGlyphs for the current selection state.
///
internal Adorner BodyGlyphAdorner {
get {
return bodyAdorner;
}
}
///
/// There are certain cases like Adding Item to ToolStrips through InSitu Editor, where there is ParentTransaction that has to be cancelled depending upon the user action
/// When this parent transaction is cancelled, there may be no reason to REFRESH the selectionManager which actually clears all the glyphs and readds them
/// This REFRESH causes a lot of flicker and can be avoided by setting this property to false. Since this property is checked in the TransactionClosed, the SelectionManager
/// won't REFRESH and hence just eat up the refresh thus avoiding unnecessary flicker.
///
internal bool NeedRefresh {
get {
return needRefresh;
}
set {
needRefresh = value;
}
}
///
///
/// Returns the Adorner that contains all the
/// BodyGlyphs for the current selection state.
///
internal Adorner SelectionGlyphAdorner {
get {
return selectionAdorner;
}
}
///
/// This method fist calls the recursive AddControlGlyphs() method.
/// When finished, we add the final glyph(s) to the root comp.
///
private void AddAllControlGlyphs(Control parent, ArrayList selComps, object primarySelection) {
foreach (Control control in parent.Controls) {
AddAllControlGlyphs(control, selComps, primarySelection);
}
GlyphSelectionType selType = GlyphSelectionType.NotSelected;
if (selComps.Contains(parent)) {
if (parent.Equals(primarySelection)) {
selType = GlyphSelectionType.SelectedPrimary;
}
else {
selType = GlyphSelectionType.Selected;
}
}
AddControlGlyphs(parent, selType);
}
///
/// Recursive method that goes through and adds all the glyphs
/// of every child to our global Adorner.
///
private void AddControlGlyphs(Control c, GlyphSelectionType selType) {
ControlDesigner cd = (ControlDesigner)componentToDesigner[c];
if (cd != null) {
ControlBodyGlyph bodyGlyph = cd.GetControlGlyphInternal(selType);
if (bodyGlyph != null) {
bodyAdorner.Glyphs.Add(bodyGlyph);
if (selType == GlyphSelectionType.SelectedPrimary ||
selType == GlyphSelectionType.Selected) {
if (curSelectionBounds[curCompIndex] == Rectangle.Empty) {
curSelectionBounds[curCompIndex] = bodyGlyph.Bounds;
}
else {
curSelectionBounds[curCompIndex] =
Rectangle.Union(curSelectionBounds[curCompIndex], bodyGlyph.Bounds);
}
}
}
GlyphCollection glyphs = cd.GetGlyphs(selType);
if (glyphs != null) {
selectionAdorner.Glyphs.AddRange(glyphs);
if (selType == GlyphSelectionType.SelectedPrimary ||
selType == GlyphSelectionType.Selected) {
foreach (Glyph glyph in glyphs) {
curSelectionBounds[curCompIndex] =
Rectangle.Union(curSelectionBounds[curCompIndex], glyph.Bounds);
}
}
}
}
if (selType == GlyphSelectionType.SelectedPrimary ||
selType == GlyphSelectionType.Selected) {
curCompIndex++;
}
}
///
///
/// Unhook all of our event notifications, clear our adorner and remove it from the Beh.Svc.
///
// We don't need to Dispose rootComponent.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")]
public void Dispose() {
if (designerHost != null) {
designerHost.TransactionClosed -= new DesignerTransactionCloseEventHandler(OnTransactionClosed);
designerHost = null;
}
if (serviceProvider != null) {
IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService));
if (cs != null) {
cs.ComponentAdded -= new ComponentEventHandler(this.OnComponentAdded);
cs.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
cs.ComponentRemoved -= new ComponentEventHandler(this.OnComponentRemoved);
}
if (selSvc != null) {
selSvc.SelectionChanged -= new EventHandler(this.OnSelectionChanged);
selSvc = null;
}
serviceProvider = null;
}
if (behaviorService != null) {
behaviorService.Adorners.Remove(bodyAdorner);
behaviorService.Adorners.Remove(selectionAdorner);
behaviorService.BeginDrag -= new BehaviorDragDropEventHandler(this.OnBeginDrag);
behaviorService.Synchronize -= new EventHandler(this.OnSynchronize);
behaviorService = null;
}
if (selectionAdorner != null)
{
selectionAdorner.Glyphs.Clear();
selectionAdorner = null;
}
if (bodyAdorner != null)
{
bodyAdorner.Glyphs.Clear();
bodyAdorner = null;
}
if (designerActionUI != null) {
designerActionUI.Dispose();
designerActionUI = null;
}
}
///
///
/// Refreshes all selection Glyphs.
///
public void Refresh() {
NeedRefresh = false;
OnSelectionChanged(this, null);
}
///
/// When a component is added, we get the designer and add it
/// to our hashtable for quick lookup.
///
private void OnComponentAdded(object source, ComponentEventArgs ce) {
IComponent component = ce.Component;
IDesigner designer = designerHost.GetDesigner(component);
if (designer is ControlDesigner) {
componentToDesigner.Add(component, designer);
}
}
///
/// Before a drag, remove all glyphs that are involved
/// in the drag operation and any that don't allow drops.
///
private void OnBeginDrag(object source, BehaviorDragDropEventArgs e) {
ArrayList dragComps = new ArrayList(e.DragComponents);
ArrayList glyphsToRemove = new ArrayList();
foreach (ControlBodyGlyph g in bodyAdorner.Glyphs) {
if (g.RelatedComponent is Control)
{
if (dragComps.Contains(g.RelatedComponent) ||
!((Control)g.RelatedComponent).AllowDrop) {
glyphsToRemove.Add(g);
}
}
}
foreach (Glyph g in glyphsToRemove) {
bodyAdorner.Glyphs.Remove(g);
}
}
// Called by the DropSourceBehavior when dragging into a new host
internal void OnBeginDrag(BehaviorDragDropEventArgs e) {
OnBeginDrag(null, e);
}
///
/// When a component is changed - we need to refresh the selection.
///
private void OnComponentChanged(object source, ComponentChangedEventArgs ce) {
if (selSvc.GetComponentSelected(ce.Component)) {
if (!designerHost.InTransaction) {
Refresh();
}
else {
NeedRefresh = true;
}
}
}
///
/// When a component is removed - we remove the key & value from
/// our hashtable.
///
private void OnComponentRemoved(object source, ComponentEventArgs ce) {
if (componentToDesigner.Contains(ce.Component)) {
componentToDesigner.Remove(ce.Component);
}
//remove the associated designeractionpanel
if (designerActionUI != null) {
designerActionUI.RemoveActionGlyph(ce.Component);
}
}
///
/// Computes the region representing the difference between the old
/// selection and the new selection.
///
private Region DetermineRegionToRefresh(object primarySelection) {
Region toRefresh = new Region(Rectangle.Empty);
Rectangle[] larger;
Rectangle[] smaller;
if (curSelectionBounds.Length >= prevSelectionBounds.Length) {
larger = curSelectionBounds;
smaller = prevSelectionBounds;
}
else {
larger = prevSelectionBounds;
smaller = curSelectionBounds;
}
// we need to make sure all of the rects in the smaller array are
// accounted for. Any that don't intersect a rect in the larger
// array need to be included in the region to repaint.
bool[] intersected = new bool[smaller.Length];
for (int i = 0; i < smaller.Length; i++) {
intersected[i] = false;
}
// determine which rects in the larger array need to be
// included in the region to invalidate by intersecting
// with rects in the smaller array.
for(int l = 0; l < larger.Length; l++) {
bool largeIntersected = false;
Rectangle large = larger[l];
for (int s = 0; s < smaller.Length; s++) {
if (large.IntersectsWith(smaller[s])) {
Rectangle small = smaller[s];
largeIntersected = true;
if (large != small) {
toRefresh.Union(large);
toRefresh.Union(small);
}
intersected[s] = true;
break;
}
}
if (!largeIntersected) {
toRefresh.Union(large);
}
}
// now add any rects from the smaller array
// that weren't accounted for
for (int k = 0; k < intersected.Length; k++) {
if (!intersected[k]) {
toRefresh.Union(smaller[k]);
}
}
using (Graphics g = behaviorService.AdornerWindowGraphics) {
//If all that changed was the primary selection, then the refresh region was empty,
//but we do need to update the 2 controls. VSWhidbey #269806
if (toRefresh.IsEmpty(g) && primarySelection != null && !primarySelection.Equals(prevPrimarySelection)) {
for (int i = 0; i < curSelectionBounds.Length; i++) {
toRefresh.Union(curSelectionBounds[i]);
}
}
}
return toRefresh;
}
///
/// Event handler for the behaviorService's Synchronize event
///
private void OnSynchronize(object sender, EventArgs e) {
Refresh();
}
///
/// On every selectionchange, we remove all glyphs, get the newly
/// selected components, and re-add all glyphs back to the Adorner.
///
private void OnSelectionChanged(object sender, EventArgs e) {
// Note: selectionChanging would guard against a re-entrant code...
// Since we dont want to be in messed up state when adding new Glyphs.
if (!selectionChanging)
{
selectionChanging = true;
selectionAdorner.Glyphs.Clear();
bodyAdorner.Glyphs.Clear();
ArrayList selComps = new ArrayList(selSvc.GetSelectedComponents());
object primarySelection = selSvc.PrimarySelection;
//add all control glyphs to all controls on rootComp
curCompIndex = 0;
curSelectionBounds = new Rectangle[selComps.Count];
AddAllControlGlyphs(rootComponent, selComps, primarySelection);
if (prevSelectionBounds != null) {
Region toUpdate = DetermineRegionToRefresh(primarySelection);
using (Graphics g = behaviorService.AdornerWindowGraphics) {
if (!toUpdate.IsEmpty(g))
{
selectionAdorner.Invalidate(toUpdate);
}
}
}
else {
// There was no previous selection, so just invalidate
// the current selection
if (curSelectionBounds.Length > 0) {
Rectangle toUpdate = curSelectionBounds[0];
for (int i = 1; i < curSelectionBounds.Length; i++) {
toUpdate = Rectangle.Union(toUpdate, curSelectionBounds[i]);
}
if (toUpdate != Rectangle.Empty)
{
selectionAdorner.Invalidate(toUpdate);
}
}
else {
selectionAdorner.Invalidate();
}
}
prevPrimarySelection = primarySelection;
if (curSelectionBounds.Length > 0) {
prevSelectionBounds = new Rectangle[curSelectionBounds.Length];
Array.Copy(curSelectionBounds, prevSelectionBounds, curSelectionBounds.Length);
}
else {
prevSelectionBounds = null;
}
selectionChanging = false;
}
}
///
/// When a transaction that involves one of our components closes,
/// refresh to reflect any changes.
///
private void OnTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) {
if (e.LastTransaction && NeedRefresh) {
Refresh();
}
}
}
}
// 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
- CommandHelper.cs
- Renderer.cs
- NativeMethods.cs
- Semaphore.cs
- PrimitiveSchema.cs
- CommandBinding.cs
- PropertyTab.cs
- XdrBuilder.cs
- TextRange.cs
- BindingExpressionUncommonField.cs
- SimplePropertyEntry.cs
- GlobalAllocSafeHandle.cs
- VisualBrush.cs
- Slider.cs
- SelectionRangeConverter.cs
- precedingquery.cs
- DesignerVerbCollection.cs
- GroupedContextMenuStrip.cs
- SystemKeyConverter.cs
- MenuItemCollectionEditor.cs
- PartialTrustVisibleAssembliesSection.cs
- SmtpLoginAuthenticationModule.cs
- ReferenceList.cs
- XmlAttributeAttribute.cs
- TableLayoutPanel.cs
- TextTreeUndo.cs
- ProgressBar.cs
- SqlConnectionHelper.cs
- BaseTreeIterator.cs
- DataTableNewRowEvent.cs
- BindableTemplateBuilder.cs
- FileDialogPermission.cs
- COM2ComponentEditor.cs
- BinaryCommonClasses.cs
- CatalogZone.cs
- GlobalItem.cs
- Misc.cs
- XmlSchemaAppInfo.cs
- RegisteredExpandoAttribute.cs
- AccessText.cs
- Trace.cs
- StringExpressionSet.cs
- BindingSource.cs
- Baml2006ReaderSettings.cs
- Timer.cs
- HttpConfigurationSystem.cs
- SqlExpander.cs
- GlobalDataBindingHandler.cs
- ConnectionManagementElement.cs
- IDReferencePropertyAttribute.cs
- TraceUtility.cs
- ClientConfigurationSystem.cs
- AuthenticationServiceManager.cs
- SqlPersistenceProviderFactory.cs
- CommandBinding.cs
- RoleService.cs
- DynamicControl.cs
- ImageKeyConverter.cs
- GridViewHeaderRowPresenter.cs
- HwndSubclass.cs
- ColumnTypeConverter.cs
- WindowsImpersonationContext.cs
- SqlNodeTypeOperators.cs
- ThemeDictionaryExtension.cs
- PointUtil.cs
- Version.cs
- ActivityTypeDesigner.xaml.cs
- HttpConfigurationSystem.cs
- XmlQueryType.cs
- Relationship.cs
- InstanceData.cs
- MediaEntryAttribute.cs
- SQLInt32.cs
- DBAsyncResult.cs
- FrameworkReadOnlyPropertyMetadata.cs
- SmiContextFactory.cs
- XmlTextAttribute.cs
- UrlAuthorizationModule.cs
- IOThreadTimer.cs
- Timer.cs
- Pen.cs
- PublishLicense.cs
- BuilderElements.cs
- InstallerTypeAttribute.cs
- ReservationNotFoundException.cs
- AssemblyFilter.cs
- TextTreeInsertUndoUnit.cs
- GenericIdentity.cs
- FrameworkElement.cs
- TargetParameterCountException.cs
- DataGridCellInfo.cs
- SystemIcons.cs
- ListViewInsertedEventArgs.cs
- XslVisitor.cs
- GeneralTransform2DTo3DTo2D.cs
- DiffuseMaterial.cs
- MultiBindingExpression.cs
- TextSegment.cs
- TypeInitializationException.cs
- WinEventQueueItem.cs