Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / WinForms / System / WinForms / Design / SelectionUIService.cs / 1 / SelectionUIService.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
/*
*/
namespace System.Windows.Forms.Design {
using System;
using System.Design;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using Microsoft.Win32;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Design;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters;
using System.Windows.Forms;
///
///
/// The selection manager handles selection within a form. There is one selection
/// manager for each form or top level designer.
///
/// A selection consists of an array of components. One component is designated
/// the "primary" selection and is displayed with different grab handles.
///
/// An individual selection may or may not have UI associated with it. If the
/// selection manager can find a suitable designer that is representing the
/// selection, it will highlight the designer's border. If the merged property
/// set has a location property, the selection's rules will allow movement. Also,
/// if the property set has a size property, the selection's rules will allow
/// for sizing. Grab handles may be drawn around the designer and user
/// interactions involving the selection frame and grab handles are initiated
/// here, but the actual movement of the objects is done in a designer object
/// that implements the ISelectionHandler interface.
/// @author [....]
///
internal sealed class SelectionUIService : Control, ISelectionUIService {
private static readonly Point InvalidPoint = new Point(int.MinValue, int.MinValue);
private const int HITTEST_CONTAINER_SELECTOR = 0x0001;
private const int HITTEST_NORMAL_SELECTION = 0x0002;
private const int HITTEST_DEFAULT = HITTEST_CONTAINER_SELECTOR | HITTEST_NORMAL_SELECTION;
// These are used during a drag operation, either through our own handle drag or through
// ISelectionUIService
//
private ISelectionUIHandler dragHandler; // the current drag handler
private object [] dragComponents; // the controls being dragged
private SelectionRules dragRules; // movement constraints for the drag
private bool dragMoved = false;
private object containerDrag; // object being dragged during a container drag
// These are used during a drag of a selection grab handle
//
private bool ignoreCaptureChanged = false;
private bool mouseDown; // is our mouse button actually down right now?
private int mouseDragHitTest; // where the hit occurred that caused the drag
private Point mouseDragAnchor = InvalidPoint; // anchor point of the drag
private Rectangle mouseDragOffset = Rectangle.Empty; // current drag offset
private Point lastMoveScreenCoord = Point.Empty;
private bool ctrlSelect = false; // was the CTRL key down when the drag began
private bool mouseDragging = false; // Are we actually doing a drag?
private ContainerSelectorActiveEventHandler containerSelectorActive; // the event we fire when user interacts with container selector
private Hashtable selectionItems;
private Hashtable selectionHandlers; // Component UI handlers
private bool savedVisible; // we stash this when we mess with visibility ourselves.
private bool batchMode;
private bool batchChanged;
private bool batchSync;
private ISelectionService selSvc;
private IDesignerHost host;
private DesignerTransaction dragTransaction;
///
///
/// Creates a new selection manager object. The selection manager manages all
/// selection of all designers under the current form file.
///
[SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")]
public SelectionUIService(IDesignerHost host)
: base() {
this.SetStyle(ControlStyles.StandardClick
| ControlStyles.Opaque
| ControlStyles.OptimizedDoubleBuffer, true);
this.host = host;
this.dragHandler = null;
this.dragComponents = null;
this.selectionItems = new Hashtable();
this.selectionHandlers = new Hashtable();
this.AllowDrop = true;
// Not really any reason for this, except that it can be handy when
// using Spy++
//
Text = "SelectionUIOverlay";
this.selSvc = (ISelectionService)host.GetService(typeof(ISelectionService));
if (selSvc != null) {
selSvc.SelectionChanged += new EventHandler(this.OnSelectionChanged);
}
// And configure the events we want to listen to.
//
host.TransactionOpened += new EventHandler(this.OnTransactionOpened);
host.TransactionClosed += new DesignerTransactionCloseEventHandler(this.OnTransactionClosed);
if (host.InTransaction) {
OnTransactionOpened(host, EventArgs.Empty);
}
IComponentChangeService cs = (IComponentChangeService)host.GetService(typeof(IComponentChangeService));
if (cs != null) {
cs.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemove);
cs.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
}
// Listen to the SystemEvents so that we can resync selection based on display settings etc.
SystemEvents.DisplaySettingsChanged += new EventHandler(this.OnSystemSettingChanged);
SystemEvents.InstalledFontsChanged += new EventHandler(this.OnSystemSettingChanged);
SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged);
}
///
///
/// override of control.
///
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.Style &= ~(NativeMethods.WS_CLIPSIBLINGS | NativeMethods.WS_CLIPCHILDREN);
return cp;
}
}
///
///
/// Called to initiate a mouse drag on the selection overlay. We cache some
/// state here.
///
private void BeginMouseDrag(Point anchor, int hitTest) {
Capture = true;
ignoreCaptureChanged = false;
mouseDragAnchor = anchor;
mouseDragging = true;
mouseDragHitTest = hitTest;
mouseDragOffset = new Rectangle();
savedVisible = Visible;
}
///
///
/// Displays the given exception to the user.
///
private void DisplayError(Exception e) {
IUIService uis = (IUIService)host.GetService(typeof(IUIService));
if (uis != null) {
uis.ShowError(e);
}
else {
string message = e.Message;
if (message == null || message.Length == 0) {
message = e.ToString();
}
RTLAwareMessageBox.Show(null, message, null, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1, 0);
}
}
///
///
/// Disposes the entire selection UI manager.
///
protected override void Dispose(bool disposing) {
if (disposing) {
if (selSvc != null) {
selSvc.SelectionChanged -= new EventHandler(this.OnSelectionChanged);
}
if (host != null) {
host.TransactionOpened -= new EventHandler(this.OnTransactionOpened);
host.TransactionClosed -= new DesignerTransactionCloseEventHandler(this.OnTransactionClosed);
if (host.InTransaction) {
OnTransactionClosed(host, new DesignerTransactionCloseEventArgs(true, true));
}
IComponentChangeService cs = (IComponentChangeService)host.GetService(typeof(IComponentChangeService));
if (cs != null) {
cs.ComponentRemoved -= new ComponentEventHandler(this.OnComponentRemove);
cs.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
foreach(SelectionUIItem s in selectionItems.Values) {
s.Dispose();
}
selectionHandlers.Clear();
selectionItems.Clear();
// Listen to the SystemEvents so that we can resync selection based on display settings etc.
SystemEvents.DisplaySettingsChanged -= new EventHandler(this.OnSystemSettingChanged);
SystemEvents.InstalledFontsChanged -= new EventHandler(this.OnSystemSettingChanged);
SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged);
}
base.Dispose(disposing);
}
///
///
/// Called when we want to finish a mouse drag and clean up our variables. We call this
/// from multiple places, depending on the state of the finish. This does NOT end
/// the drag -- for that must call EndDrag. This just cleans up the state of the
/// mouse.
///
private void EndMouseDrag(Point position) {
// it's possible for us to be destroyed in a drag --
// e.g. if this is the tray's selectionuiservice
// and the last item is dragged out, so
// check diposed first
if (this.IsDisposed) {
return;
}
ignoreCaptureChanged = true;
Capture = false;
mouseDragAnchor = InvalidPoint;
mouseDragOffset = Rectangle.Empty;
mouseDragHitTest = 0;
dragMoved = false;
SetSelectionCursor(position);
mouseDragging = ctrlSelect = false;
}
///
///
/// Determines the selection hit test at the given point. The point should be in screen
/// coordinates.
///
private HitTestInfo GetHitTest(Point value, int flags) {
Point pt = PointToClient(value);
foreach(SelectionUIItem item in selectionItems.Values) {
if ((flags & HITTEST_CONTAINER_SELECTOR) != 0) {
if (item is ContainerSelectionUIItem && (item.GetRules() & SelectionRules.Visible) != SelectionRules.None) {
int hitTest = item.GetHitTest(pt);
if ((hitTest & SelectionUIItem.CONTAINER_SELECTOR) != 0) {
return new HitTestInfo(hitTest, item, true);
}
}
}
if ((flags & HITTEST_NORMAL_SELECTION) != 0) {
if (!(item is ContainerSelectionUIItem) && (item.GetRules() & SelectionRules.Visible) != SelectionRules.None) {
int hitTest = item.GetHitTest(pt);
if (hitTest != SelectionUIItem.NOHIT) {
if (hitTest != 0) {
return new HitTestInfo(hitTest, item);
}
else {
return new HitTestInfo(SelectionUIItem.NOHIT, item);
}
}
}
}
}
return new HitTestInfo(SelectionUIItem.NOHIT, null);
}
private ISelectionUIHandler GetHandler(object component) {
return (ISelectionUIHandler)selectionHandlers[component];
}
///
///
/// This method returns a well-formed name for a drag transaction based on
/// the rules it is given.
///
public static string GetTransactionName(SelectionRules rules, object[] objects) {
// Determine a nice name for the drag operation
//
string transactionName;
if ((int)(rules & SelectionRules.Moveable) != 0) {
if (objects.Length > 1) {
transactionName = SR.GetString(SR.DragDropMoveComponents, objects.Length);
}
else {
string name = string.Empty;
if (objects.Length > 0) {
IComponent comp = objects[0] as IComponent;
if (comp != null && comp.Site != null) {
name = comp.Site.Name;
}
else {
name = objects[0].GetType().Name;
}
}
transactionName = SR.GetString(SR.DragDropMoveComponent, name);
}
}
else if ((int)(rules & SelectionRules.AllSizeable) != 0) {
if (objects.Length > 1) {
transactionName = SR.GetString(SR.DragDropSizeComponents, objects.Length);
}
else {
string name = string.Empty;
if (objects.Length > 0) {
IComponent comp = objects[0] as IComponent;
if (comp != null && comp.Site != null) {
name = comp.Site.Name;
}
else {
name = objects[0].GetType().Name;
}
}
transactionName = SR.GetString(SR.DragDropSizeComponent, name);
}
}
else {
transactionName = SR.GetString(SR.DragDropDragComponents, objects.Length);
}
return transactionName;
}
///
///
/// Called by the designer host when it is entering or leaving a batch
/// operation. Here we queue up selection notification and we turn off
/// our UI.
///
private void OnTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) {
if (e.LastTransaction) {
batchMode = false;
if (batchChanged) {
batchChanged = false;
((ISelectionUIService)this).SyncSelection();
}
if (batchSync) {
batchSync = false;
((ISelectionUIService)this).SyncComponent(null);
}
}
}
///
///
/// Called by the designer host when it is entering or leaving a batch
/// operation. Here we queue up selection notification and we turn off
/// our UI.
///
private void OnTransactionOpened(object sender, EventArgs e) {
batchMode = true;
}
///
///
/// update our window region on first create. We shouldn't do this before the handle
/// is created or else we will force creation.
///
protected override void OnHandleCreated(EventArgs e) {
Debug.Assert(!RecreatingHandle, "Perf hit: we are recreating the docwin handle");
base.OnHandleCreated(e);
// Default the shape of the control to be empty, so that
// if nothing is initially selected that our window surface doesn't
// interfere.
//
UpdateWindowRegion();
}
///
///
/// Called whenever a component changes. Here we update our selection information
/// so that the selection rectangles are all up to date.
///
private void OnComponentChanged(object sender, ComponentChangedEventArgs ccevent) {
if (!batchMode) {
((ISelectionUIService)this).SyncSelection();
}
else {
batchChanged = true;
}
}
///
///
/// called by the formcore when someone has removed a component. This will
/// remove any selection on the component without disturbing the rest of
/// the selection
///
private void OnComponentRemove(object sender, ComponentEventArgs ce) {
selectionHandlers.Remove(ce.Component);
selectionItems.Remove(ce.Component);
((ISelectionUIService)this).SyncComponent(ce.Component);
}
///
///
/// Called to invoke the container active event, if a designer
/// has bound to it.
///
private void OnContainerSelectorActive(ContainerSelectorActiveEventArgs e) {
if (containerSelectorActive != null) {
containerSelectorActive(this, e);
}
}
///
///
/// Called when the selection changes. We sync up the UI with
/// the selection at this point.
///
private void OnSelectionChanged(object sender, EventArgs e) {
ICollection selection = selSvc.GetSelectedComponents();
Hashtable newSelection = new Hashtable(selection.Count);
bool shapeChanged = false;
foreach(object comp in selection ) {
object existingItem = selectionItems[comp];
bool create = true;
if (existingItem != null) {
ContainerSelectionUIItem item = existingItem as ContainerSelectionUIItem;
if (item != null) {
item.Dispose();
shapeChanged = true;
}
else {
newSelection[comp] = existingItem;
create = false;
}
}
if (create) {
shapeChanged = true;
newSelection[comp] = new SelectionUIItem(this, comp);
}
}
if (!shapeChanged) {
shapeChanged = selectionItems.Keys.Count != newSelection.Keys.Count;
}
selectionItems = newSelection;
if (shapeChanged) {
UpdateWindowRegion();
}
Invalidate();
Update();
}
///
///
/// User setting requires that we repaint.
///
private void OnSystemSettingChanged(object sender, EventArgs e) {
Invalidate();
}
///
///
/// User setting requires that we repaint.
///
private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) {
Invalidate();
}
///
///
/// Inheriting classes should override this method to handle this event.
/// Call super.onDragEnter to send this event to any registered event listeners.
///
protected override void OnDragEnter(DragEventArgs devent) {
base.OnDragEnter(devent);
if (dragHandler != null) {
dragHandler.OleDragEnter(devent);
}
}
///
///
/// Inheriting classes should override this method to handle this event.
/// Call super.onDragOver to send this event to any registered event listeners.
///
protected override void OnDragOver(DragEventArgs devent) {
base.OnDragOver(devent);
if (dragHandler != null) {
dragHandler.OleDragOver(devent);
}
}
///
///
/// Inheriting classes should override this method to handle this event.
/// Call super.onDragLeave to send this event to any registered event listeners.
///
protected override void OnDragLeave(EventArgs e) {
base.OnDragLeave(e);
if (dragHandler != null) {
dragHandler.OleDragLeave();
}
}
///
///
/// Inheriting classes should override this method to handle this event.
/// Call super.onDragDrop to send this event to any registered event listeners.
///
protected override void OnDragDrop(DragEventArgs devent) {
base.OnDragDrop(devent);
if (dragHandler != null) {
dragHandler.OleDragDrop(devent);
}
}
///
///
/// Inheriting classes should override this method to handle this event.
/// Call base.OnDoiubleClick to send this event to any registered event listeners.
///
protected override void OnDoubleClick(EventArgs devent) {
base.OnDoubleClick(devent);
if (selSvc != null) {
object selComp = selSvc.PrimarySelection;
Debug.Assert(selComp != null, "Illegal selection on double-click");
if (selComp != null) {
ISelectionUIHandler handler = GetHandler(selComp);
if (handler != null) {
handler.OnSelectionDoubleClick((IComponent)selComp);
}
}
}
}
///
///
/// Overrides Control to handle our selection grab handles.
///
// Standard 'catch all - rethrow critical' exception pattern
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
[SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
protected override void OnMouseDown(MouseEventArgs me) {
if (dragHandler == null && selSvc != null) {
try {
mouseDown = true;
// First, did the user step on anything?
//
Point anchor = PointToScreen(new Point(me.X, me.Y));
HitTestInfo hti = GetHitTest(anchor, HITTEST_DEFAULT);
int hitTest = hti.hitTest;
if ((hitTest & SelectionUIItem.CONTAINER_SELECTOR) != 0) {
selSvc.SetSelectedComponents(new object[] {hti.selectionUIHit.component}, SelectionTypes.Auto);
// Then do a drag...
//
SelectionRules rules = SelectionRules.Moveable;
if (((ISelectionUIService)this).BeginDrag(rules, anchor.X, anchor.Y)) {
Visible = false;
containerDrag = hti.selectionUIHit.component;
BeginMouseDrag(anchor, hitTest);
}
}
else if (hitTest != SelectionUIItem.NOHIT && me.Button == MouseButtons.Left) {
SelectionRules rules = SelectionRules.None;
// If the CTRL key isn't down, select this component,
// otherwise, we wait until the mouse up
//
// Make sure the component is selected
//
ctrlSelect = (Control.ModifierKeys & Keys.Control) != Keys.None;
if (!ctrlSelect) {
selSvc.SetSelectedComponents(new object[] {hti.selectionUIHit.component}, SelectionTypes.Primary);
}
if ((hitTest & SelectionUIItem.MOVE_MASK) != 0) {
rules |= SelectionRules.Moveable;
}
if ((hitTest & SelectionUIItem.SIZE_MASK) != 0) {
if ((hitTest & (SelectionUIItem.SIZE_X | SelectionUIItem.POS_RIGHT)) == (SelectionUIItem.SIZE_X | SelectionUIItem.POS_RIGHT)) {
rules |= SelectionRules.RightSizeable;
}
if ((hitTest & (SelectionUIItem.SIZE_X | SelectionUIItem.POS_LEFT)) == (SelectionUIItem.SIZE_X | SelectionUIItem.POS_LEFT)) {
rules |= SelectionRules.LeftSizeable;
}
if ((hitTest & (SelectionUIItem.SIZE_Y | SelectionUIItem.POS_TOP)) == (SelectionUIItem.SIZE_Y | SelectionUIItem.POS_TOP)) {
rules |= SelectionRules.TopSizeable;
}
if ((hitTest & (SelectionUIItem.SIZE_Y | SelectionUIItem.POS_BOTTOM)) == (SelectionUIItem.SIZE_Y | SelectionUIItem.POS_BOTTOM)) {
rules |= SelectionRules.BottomSizeable;
}
if (((ISelectionUIService)this).BeginDrag(rules, anchor.X, anchor.Y)) {
BeginMouseDrag(anchor, hitTest);
}
}
else {
// Our mouse is in drag mode. We defer the actual move until the user moves the
// mouse.
//
dragRules = rules;
BeginMouseDrag(anchor, hitTest);
}
}
else if (hitTest == SelectionUIItem.NOHIT) {
dragRules = SelectionRules.None;
mouseDragAnchor = InvalidPoint;
return;
}
}
catch(Exception e) {
if (ClientUtils.IsCriticalException(e)) {
throw;
}
else if (e != CheckoutException.Canceled) {
DisplayError(e);
}
}
}
}
///
///
/// Overrides Control to handle our selection grab handles.
///
protected override void OnMouseMove(MouseEventArgs me) {
base.OnMouseMove(me);
Point screenCoord = PointToScreen(new Point(me.X, me.Y));
HitTestInfo hti = GetHitTest(screenCoord, HITTEST_CONTAINER_SELECTOR);
int hitTest = hti.hitTest;
if (hitTest != SelectionUIItem.CONTAINER_SELECTOR && hti.selectionUIHit != null) {
OnContainerSelectorActive(new ContainerSelectorActiveEventArgs(hti.selectionUIHit.component));
}
if (lastMoveScreenCoord == screenCoord) {
return;
}
// If we're not dragging then set the cursor correctly.
//
if (!mouseDragging) {
SetSelectionCursor(screenCoord);
}
else {
// we have to make sure the mouse moved farther than
// the minimum drag distance before we actually start
// the drag
//
if (!((ISelectionUIService)this).Dragging && (mouseDragHitTest & SelectionUIItem.MOVE_MASK) != 0) {
Size minDragSize = SystemInformation.DragSize;
if (
Math.Abs(screenCoord.X - mouseDragAnchor.X) < minDragSize.Width &&
Math.Abs(screenCoord.Y - mouseDragAnchor.Y) < minDragSize.Height) {
return;
}
else {
ignoreCaptureChanged = true;
if (((ISelectionUIService)this).BeginDrag(dragRules, mouseDragAnchor.X, mouseDragAnchor.Y)) {
// we're moving, so we
// don't care about the ctrl key any more
ctrlSelect = false;
}
else {
EndMouseDrag(MousePosition);
return;
}
}
}
Rectangle old = mouseDragOffset;
if ((mouseDragHitTest & SelectionUIItem.MOVE_X) != 0) {
mouseDragOffset.X = screenCoord.X - mouseDragAnchor.X;
}
if ((mouseDragHitTest & SelectionUIItem.MOVE_Y) != 0) {
mouseDragOffset.Y = screenCoord.Y - mouseDragAnchor.Y;
}
if ((mouseDragHitTest & SelectionUIItem.SIZE_X) != 0) {
if ((mouseDragHitTest & SelectionUIItem.POS_LEFT) != 0) {
mouseDragOffset.X = screenCoord.X - mouseDragAnchor.X;
mouseDragOffset.Width = mouseDragAnchor.X - screenCoord.X;
}
else {
mouseDragOffset.Width = screenCoord.X - mouseDragAnchor.X;
}
}
if ((mouseDragHitTest & SelectionUIItem.SIZE_Y) != 0) {
if ((mouseDragHitTest & SelectionUIItem.POS_TOP) != 0) {
mouseDragOffset.Y = screenCoord.Y - mouseDragAnchor.Y;
mouseDragOffset.Height = mouseDragAnchor.Y - screenCoord.Y;
}
else {
mouseDragOffset.Height = screenCoord.Y - mouseDragAnchor.Y;
}
}
if (!old.Equals(mouseDragOffset)) {
Rectangle delta = mouseDragOffset;
delta.X -= old.X;
delta.Y -= old.Y;
delta.Width -= old.Width;
delta.Height -= old.Height;
if (delta.X != 0 || delta.Y != 0 || delta.Width != 0 || delta.Height != 0) {
// Go to default cursor for moves...
//
if ((mouseDragHitTest & SelectionUIItem.MOVE_X) != 0
|| (mouseDragHitTest & SelectionUIItem.MOVE_Y) != 0) {
Cursor = Cursors.Default;
}
((ISelectionUIService)this).DragMoved(delta);
}
}
}
}
///
///
/// Overrides Control to handle our selection grab handles.
///
// Standard 'catch all - rethrow critical' exception pattern
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
[SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
protected override void OnMouseUp(MouseEventArgs me) {
try {
bool wasDown = mouseDown;
mouseDown = false;
Point screenCoord = PointToScreen(new Point(me.X, me.Y));
if (ctrlSelect && !mouseDragging && selSvc != null) {
HitTestInfo hti = GetHitTest(screenCoord, HITTEST_DEFAULT);
selSvc.SetSelectedComponents(new object[] {hti.selectionUIHit.component}, SelectionTypes.Primary);
}
if (mouseDragging) {
object oldContainerDrag = containerDrag;
bool oldDragMoved = dragMoved;
EndMouseDrag(screenCoord);
if (((ISelectionUIService)this).Dragging) {
((ISelectionUIService)this).EndDrag(false);
}
if (me.Button == MouseButtons.Right && oldContainerDrag != null && !oldDragMoved) {
OnContainerSelectorActive(new ContainerSelectorActiveEventArgs(oldContainerDrag,
ContainerSelectorActiveEventArgsType.Contextmenu));
}
}
}
catch(Exception e) {
if (ClientUtils.IsCriticalException(e)) {
throw;
}
else if (e != CheckoutException.Canceled) {
DisplayError(e);
}
}
}
///
///
/// If the selection manager move, this indicates that the form has autoscolling
/// enabled and has been scrolled. We have to invalidate here because we may
/// get moved before the rest of the components so we may draw the selection in
/// the wrong spot.
///
protected override void OnMove(EventArgs e) {
base.OnMove(e);
Invalidate();
}
///
///
/// overrides control.onPaint. here we paint the selection handles. The window's
/// region was setup earlier.
///
protected override void OnPaint(PaintEventArgs e) {
// Paint the regular selection items first, and then the
// container selectors last so they draw over the
// top.
//
foreach(SelectionUIItem item in selectionItems.Values) {
if (item is ContainerSelectionUIItem) {
continue;
}
item.DoPaint(e.Graphics);
}
foreach(SelectionUIItem item in selectionItems.Values) {
if (item is ContainerSelectionUIItem) {
item.DoPaint(e.Graphics);
}
}
}
///
///
/// Sets the appropriate selection cursor at the given point.
///
private void SetSelectionCursor(Point pt) {
Point clientCoords = PointToClient(pt);
// We render the cursor in the same order we paint.
//
foreach(SelectionUIItem item in selectionItems.Values) {
if (item is ContainerSelectionUIItem) {
continue;
}
Cursor cursor = item.GetCursorAtPoint(clientCoords);
if (cursor != null) {
if (cursor == Cursors.Default) {
Cursor = null;
}
else {
Cursor = cursor;
}
return;
}
}
foreach(SelectionUIItem item in selectionItems.Values) {
if (item is ContainerSelectionUIItem) {
Cursor cursor = item.GetCursorAtPoint(clientCoords);
if (cursor != null) {
if (cursor == Cursors.Default) {
Cursor = null;
}
else {
Cursor = cursor;
}
return;
}
}
}
// Don't know what to set; just use the default.
//
Cursor = null;
}
///
///
/// called when the overlay region is invalid and should be updated
///
private void UpdateWindowRegion() {
Region region = new Region(new Rectangle(0, 0, 0, 0));
foreach(SelectionUIItem item in selectionItems.Values) {
region.Union(item.GetRegion());
}
Region = region;
}
///
///
/// Override of our control's WNDPROC. We diddle with capture a bit,
/// and it's important to turn this off if the capture changes.
///
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case NativeMethods.WM_LBUTTONUP:
case NativeMethods.WM_RBUTTONUP:
if (mouseDragAnchor != InvalidPoint) {
ignoreCaptureChanged = true;
}
break;
case NativeMethods.WM_CAPTURECHANGED:
if (!ignoreCaptureChanged && mouseDragAnchor != InvalidPoint) {
EndMouseDrag(MousePosition);
if (((ISelectionUIService)this).Dragging) {
((ISelectionUIService)this).EndDrag(true);
}
}
ignoreCaptureChanged = false;
break;
}
base.WndProc(ref m);
}
///
///
/// This can be used to determine if the user is in the middle of a drag operation.
///
bool ISelectionUIService.Dragging {
get {
return dragHandler != null;
}
}
///
///
/// Determines if the selection UI is shown or not.
///
///
bool ISelectionUIService.Visible {
get {
return Visible;
}
set {
Visible = value;
}
}
///
///
/// Adds an event handler to the ContainerSelectorActive event.
/// This event is fired whenever the user interacts with the container
/// selector in a manor that would indicate that the selector should
/// continued to be displayed. Since the container selector normally
/// will vanish after a timeout, designers should listen to this event
/// and reset the timeout when this event occurs.
///
event ContainerSelectorActiveEventHandler ISelectionUIService.ContainerSelectorActive {
add {
containerSelectorActive += value;
}
remove {
containerSelectorActive -= value;
}
}
///
///
/// Assigns a selection UI handler to a given component. The handler will be
/// called when the UI service needs information about the component. A single
/// selection UI handler can be assigned to multiple components.
///
/// When multiple components are dragged, only a single handler may control the
/// drag. Because of this, only components that are assigned the same handler
/// as the primary selection are included in drag operations.
///
/// A selection UI handler is automatically unassigned when the component is removed
/// from the container or disposed.
///
void ISelectionUIService.AssignSelectionUIHandler(object component, ISelectionUIHandler handler) {
ISelectionUIHandler oldHandler = (ISelectionUIHandler)selectionHandlers[component];
if (oldHandler != null) {
// ASURT #44582: The collection editors do not dispose objects from the
// collection before setting a new collection. This causes items that are
// common to the old and new collections to come through this code path
// again, causing the exception to fire. So, we check to see if the SelectionUIHandler
// is same, and bail out in that case.
//
if (handler == oldHandler) {
return;
}
Debug.Fail("A component may have only one selection UI handler.");
throw new InvalidOperationException();
}
selectionHandlers[component] = handler;
// If this component is selected, create a new UI handler for it.
//
if (selSvc != null && selSvc.GetComponentSelected(component)) {
SelectionUIItem item = new SelectionUIItem(this, component);
selectionItems[component] = item;
UpdateWindowRegion();
item.Invalidate();
}
}
void ISelectionUIService.ClearSelectionUIHandler(object component, ISelectionUIHandler handler) {
ISelectionUIHandler oldHandler = (ISelectionUIHandler)selectionHandlers[component];
if (oldHandler == handler) {
selectionHandlers[component] = null;
}
}
///
///
/// This can be called by an outside party to begin a drag of the currently selected
/// set of components.
///
// Standard 'catch all - rethrow critical' exception pattern
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
[SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
bool ISelectionUIService.BeginDrag(SelectionRules rules, int initialX, int initialY) {
if (dragHandler != null) {
Debug.Fail("Caller is starting a drag, but there is already one in progress -- we cannot nest these!");
return false;
}
if (rules == SelectionRules.None) {
Debug.Fail("Caller is starting requesting a drag with no drag rules.");
return false;
}
if (selSvc == null) {
return false;
}
savedVisible = Visible;
// First, get the list of controls
//
ICollection col = selSvc.GetSelectedComponents();
object[] objects = new object[col.Count];
col.CopyTo(objects, 0);
objects = ((ISelectionUIService)this).FilterSelection(objects, rules);
if (objects.Length == 0) {
return false; // nothing selected
}
// We allow all components with the same UI handler as the primary selection
// to participate in the drag.
//
ISelectionUIHandler primaryHandler = null;
object primary = selSvc.PrimarySelection;
if (primary != null) {
primaryHandler = GetHandler(primary);
}
if (primaryHandler == null) {
return false; // no UI handler for selection
}
// Now within the given selection, add those items that have the same
// UI handler and that have the proper rule constraints.
//
ArrayList list = new ArrayList();
for (int i = 0; i < objects.Length; i++) {
if (GetHandler(objects[i]) == primaryHandler) {
SelectionRules compRules = primaryHandler.GetComponentRules(objects[i]);
if ((compRules & rules) == rules) {
list.Add(objects[i]);
}
}
}
if (list.Count == 0) {
return false; // nothing matching the given constraints
}
objects = list.ToArray();
bool dragging = false;
// We must setup state before calling QueryBeginDrag. It is possible
// that QueryBeginDrag will cancel a drag (if it places a modal dialog, for
// example), so we must have the drag data all setup before it cancels. Then,
// we will check again after QueryBeginDrag to see if a cancel happened.
//
dragComponents = objects;
dragRules = rules;
dragHandler = primaryHandler;
string transactionName = GetTransactionName(rules, objects);
dragTransaction = host.CreateTransaction(transactionName);
try {
if (primaryHandler.QueryBeginDrag(objects, rules, initialX, initialY)) {
if (dragHandler != null) {
try {
dragging = primaryHandler.BeginDrag(objects, rules, initialX, initialY);
}
catch (Exception e) {
Debug.Fail("Drag handler threw during BeginDrag -- bad handler!", e.ToString());
dragging = false;
}
}
}
}
finally {
if (!dragging) {
dragComponents = null;
dragRules = 0;
dragHandler = null;
// Always commit this -- BeginDrag returns false for our drags because it is a
// complete operation.
if (dragTransaction != null) {
dragTransaction.Commit();
dragTransaction = null;
}
}
}
return dragging;
}
///
///
/// Called by an outside party to update drag information. This can only be called
/// after a successful call to beginDrag.
///
void ISelectionUIService.DragMoved(Rectangle offset) {
Rectangle newOffset = Rectangle.Empty;
if (dragHandler == null) {
throw new Exception(SR.GetString(SR.DesignerBeginDragNotCalled));
}
Debug.Assert(dragComponents != null, "We should have a set of drag controls here");
if ((dragRules & SelectionRules.Moveable) == SelectionRules.None
&& (dragRules & (SelectionRules.TopSizeable | SelectionRules.LeftSizeable)) == SelectionRules.None) {
newOffset = new Rectangle(0, 0, offset.Width, offset.Height);
}
if ((dragRules & SelectionRules.AllSizeable) == SelectionRules.None) {
if (newOffset.IsEmpty) {
newOffset = new Rectangle(offset.X, offset.Y, 0, 0);
}
else {
newOffset.Width = newOffset.Height = 0;
}
}
if (!newOffset.IsEmpty) {
offset = newOffset;
}
Visible = false;
dragMoved = true;
dragHandler.DragMoved(dragComponents, offset);
}
///
///
/// Called by an outside party to finish a drag operation. This can only be called
/// after a successful call to beginDrag.
///
// Standard 'catch all - rethrow critical' exception pattern
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
[SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
void ISelectionUIService.EndDrag(bool cancel) {
containerDrag = null;
ISelectionUIHandler handler = dragHandler;
object[] components = dragComponents;
// Clean these out so that even if we throw an exception we don't die.
//
dragHandler = null;
dragComponents = null;
dragRules = SelectionRules.None;
if (handler == null) {
throw new InvalidOperationException();
}
// Typically, the handler will be changing a bunch of component properties here.
// Optimize this by enclosing it within a batch call.
//
DesignerTransaction trans = null;
try {
IComponent comp = components[0] as IComponent;
if (components.Length > 1 || (components.Length == 1 && comp != null && comp.Site == null)) {
trans = host.CreateTransaction(SR.GetString(SR.DragDropMoveComponents, components.Length));
}
else if (components.Length == 1) {
if (comp != null) {
trans = host.CreateTransaction(SR.GetString(SR.DragDropMoveComponent, comp.Site.Name));
}
}
try {
handler.EndDrag(components, cancel);
}
catch (Exception e) {
Debug.Fail(e.ToString());
}
}
finally {
if (trans != null)
trans.Commit();
// Reset the selection. This will re-display our selection.
//
Visible = savedVisible;
((ISelectionUIService)this).SyncSelection();
if (dragTransaction != null) {
dragTransaction.Commit();
dragTransaction = null;
}
// In case this drag was initiated by us, ensure that our mouse state is correct
//
EndMouseDrag(MousePosition);
}
}
///
///
/// Filters the set of selected components. The selection service will retrieve all
/// components that are currently selected. This method allows you to filter this
/// set down to components that match your criteria. The selectionRules parameter
/// must contain one or more flags from the SelectionRules class. These flags
/// allow you to constrain the set of selected objects to visible, movable,
/// sizeable or all objects.
///
object[] ISelectionUIService.FilterSelection(object [] components, SelectionRules selectionRules) {
object[] selection = null;
if (components == null) return new object[0];
// Mask off any selection object that doesn't adhere to the given ruleset.
// We can ignore this if the ruleset is zero, as all components would be accepted.
//
if (selectionRules != SelectionRules.None) {
ArrayList list = new ArrayList();
foreach(object comp in components) {
SelectionUIItem item = (SelectionUIItem)selectionItems[comp];
if (item != null && !(item is ContainerSelectionUIItem)) {
if ((item.GetRules() & selectionRules) == selectionRules) {
list.Add(comp);
}
}
}
selection = (object[])list.ToArray();
}
return selection == null ? new object[0] : selection;
}
///
///
/// Retrieves the width and height of a selection border grab handle.
/// Designers may need this to properly position their user interfaces.
///
Size ISelectionUIService.GetAdornmentDimensions(AdornmentType adornmentType) {
switch (adornmentType) {
case AdornmentType.GrabHandle:
return new Size(SelectionUIItem.GRABHANDLE_WIDTH, SelectionUIItem.GRABHANDLE_HEIGHT);
case AdornmentType.ContainerSelector:
case AdornmentType.Maximum:
return new Size(ContainerSelectionUIItem.CONTAINER_WIDTH, ContainerSelectionUIItem.CONTAINER_HEIGHT);
}
return new Size(0, 0);
}
///
///
/// Tests to determine if the given screen coordinate is over an adornment
/// for the specified component. This will only return true if the
/// adornment, and selection UI, is visible.
///
bool ISelectionUIService.GetAdornmentHitTest(object component, Point value) {
if (GetHitTest(value, HITTEST_DEFAULT).hitTest != SelectionUIItem.NOHIT) {
return true;
}
return false;
}
///
///
/// Determines if the component is currently "container" selected. Container
/// selection is a visual aid for selecting containers. It doesn't affect
/// the normal "component" selection.
///
bool ISelectionUIService.GetContainerSelected(object component) {
return (component != null && selectionItems[component] is ContainerSelectionUIItem);
}
///
///
/// Retrieves a set of flags that define rules for the selection. Selection
/// rules indicate if the given component can be moved or sized, for example.
///
SelectionRules ISelectionUIService.GetSelectionRules(object component) {
SelectionUIItem sel = (SelectionUIItem)selectionItems[component];
if (sel == null) {
Debug.Fail("The component is not currently selected.");
throw new InvalidOperationException();
}
return sel.GetRules();
}
///
///
/// Allows you to configure the style of the selection frame that a
/// component uses. This is useful if your component supports different
/// modes of operation (such as an in-place editing mode and a static
/// design mode). Where possible, you should leave the selection style
/// as is and use the design-time hit testing feature of the IDesigner
/// interface to provide features at design time. The value of style
/// must be one of the SelectionStyle enum values.
///
/// The selection style is only valid for the duration that the component is
/// selected.
///
SelectionStyles ISelectionUIService.GetSelectionStyle(object component) {
SelectionUIItem s = (SelectionUIItem)selectionItems[component];
if (s == null) {
return SelectionStyles.None;
}
return s.Style;
}
///
///
/// Changes the container selection status of the given component.
/// Container selection is a visual aid for selecting containers. It
/// doesn't affect the normal "component" selection.
///
void ISelectionUIService.SetContainerSelected(object component, bool selected) {
if (selected) {
SelectionUIItem existingItem = (SelectionUIItem)selectionItems[component];
if (!(existingItem is ContainerSelectionUIItem)) {
if (existingItem != null) {
existingItem.Dispose();
}
SelectionUIItem item = new ContainerSelectionUIItem(this, component);
selectionItems[component] = item;
// Now update our region and invalidate
//
UpdateWindowRegion();
if (existingItem != null) {
existingItem.Invalidate();
}
item.Invalidate();
}
}
else {
SelectionUIItem existingItem = (SelectionUIItem)selectionItems[component];
if (existingItem == null || existingItem is ContainerSelectionUIItem) {
selectionItems.Remove(component);
if (existingItem != null) {
existingItem.Dispose();
}
UpdateWindowRegion();
existingItem.Invalidate();
}
}
}
///
///
/// Allows you to configure the style of the selection frame that a
/// component uses. This is useful if your component supports different
/// modes of operation (such as an in-place editing mode and a static
/// design mode). Where possible, you should leave the selection style
/// as is and use the design-time hit testing feature of the IDesigner
/// interface to provide features at design time. The value of style
/// must be one of the SelectionStyle enum values.
///
/// The selection style is only valid for the duration that the component is
/// selected.
///
void ISelectionUIService.SetSelectionStyle(object component, SelectionStyles style) {
SelectionUIItem selUI = (SelectionUIItem)selectionItems[component];
if (selSvc != null && selSvc.GetComponentSelected(component)) {
selUI = new SelectionUIItem(this, component);
selectionItems[component] = selUI;
}
if (selUI != null) {
selUI.Style = style;
UpdateWindowRegion();
selUI.Invalidate();
}
}
///
///
/// This should be called when a component has been moved, sized or re-parented,
/// but the change was not the result of a property change. All property
/// changes are monitored by the selection UI service, so this is automatic most
/// of the time. There are times, however, when a component may be moved without
/// a property change notification occurring. Scrolling an auto scroll Win32
/// form is an example of this.
///
/// This method simply re-queries all currently selected components for their
/// bounds and udpates the selection handles for any that have changed.
///
void ISelectionUIService.SyncSelection() {
if (batchMode) {
batchChanged = true;
}
else {
if (IsHandleCreated) {
bool updateRegion = false;
foreach(SelectionUIItem item in selectionItems.Values) {
updateRegion |= item.UpdateSize();
item.UpdateRules();
}
if (updateRegion) {
UpdateWindowRegion();
Update();
}
}
}
}
///
///
/// This should be called when a component's property changed, that the designer
/// thinks should result in a selection UI change.
///
/// This method simply re-queries all currently selected components for their
/// bounds and udpates the selection handles for any that have changed.
///
void ISelectionUIService.SyncComponent(object component) {
if (batchMode) {
batchSync = true;
}
else {
if (IsHandleCreated) {
foreach(SelectionUIItem item in selectionItems.Values) {
item.UpdateRules();
item.Dispose();
}
UpdateWindowRegion();
Invalidate();
Update();
}
}
}
///
///
/// This class represents a single selected object.
///
private class SelectionUIItem {
// Flags describing how a given selection point may be sized
//
public const int SIZE_X = 0x0001;
public const int SIZE_Y = 0x0002;
public const int SIZE_MASK = 0x0003;
// Flags describing how a given selection point may be moved
//
public const int MOVE_X = 0x0004;
public const int MOVE_Y = 0x0008;
public const int MOVE_MASK = 0x000C;
// Flags describing where a given selection point is located on an object
//
public const int POS_LEFT = 0x0010;
public const int POS_TOP = 0x0020;
public const int POS_RIGHT = 0x0040;
public const int POS_BOTTOM = 0x0080;
public const int POS_MASK = 0x00F0;
// This is returned if the given selection point is not within
// the selection
//
public const int NOHIT = 0x0100;
// This is returned if the given selection point on the "container selector"
//
public const int CONTAINER_SELECTOR = 0x0200;
public const int GRABHANDLE_WIDTH = 7;
public const int GRABHANDLE_HEIGHT = 7;
// tables we use to determine how things can move and size
//
internal static readonly int[] activeSizeArray = new int[] {
SIZE_X | SIZE_Y | POS_LEFT | POS_TOP, SIZE_Y | POS_TOP, SIZE_X | SIZE_Y | POS_TOP | POS_RIGHT,
SIZE_X | POS_LEFT, SIZE_X | POS_RIGHT,
SIZE_X | SIZE_Y | POS_LEFT | POS_BOTTOM, SIZE_Y | POS_BOTTOM, SIZE_X | SIZE_Y | POS_RIGHT | POS_BOTTOM
};
internal static readonly Cursor[] activeCursorArrays = new Cursor[] {
Cursors.SizeNWSE, Cursors.SizeNS, Cursors.SizeNESW,
Cursors.SizeWE, Cursors.SizeWE,
Cursors.SizeNESW, Cursors.SizeNS, Cursors.SizeNWSE
};
internal static readonly int[] inactiveSizeArray = new int[] {0, 0, 0, 0, 0, 0, 0, 0};
internal static readonly Cursor[] inactiveCursorArray = new Cursor[] {
Cursors.Arrow, Cursors.Arrow, Cursors.Arrow,
Cursors.Arrow, Cursors.Arrow,
Cursors.Arrow, Cursors.Arrow, Cursors.Arrow
};
internal int[] sizes; // array of sizing rules for this selection
internal Cursor[] cursors; // array of cursors for each grab location
internal SelectionUIService selUIsvc;
internal Rectangle innerRect = Rectangle.Empty; // inner part of selection (== control bounds)
internal Rectangle outerRect = Rectangle.Empty; // outer part of selection (inner + border size)
internal Region region; // region object that defines the shape
internal object component; // the component we're rendering
private Control control;
private SelectionStyles selectionStyle; // how do we draw this thing?
private SelectionRules selectionRules;
private ISelectionUIHandler handler; // the components selection UI handler (can be null)
///
///
/// constructor
///
/// Its ok to call virtual method as this is a private class.
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public SelectionUIItem(SelectionUIService selUIsvc, object component) {
this.selUIsvc = selUIsvc;
this.component = component;
selectionStyle = SelectionStyles.Selected;
// By default, a component isn't visible. We must establish what
// it can do through it's UI handler.
//
handler = selUIsvc.GetHandler(component);
sizes = inactiveSizeArray;
cursors = inactiveCursorArray;
IComponent comp = component as IComponent;
if (comp != null) {
ControlDesigner cd = selUIsvc.host.GetDesigner(comp) as ControlDesigner;
if (cd != null) {
control = cd.Control;
}
}
UpdateRules();
UpdateGrabSettings();
UpdateSize();
}
///
///
/// Retrieves the style of the selection frame for this selection.
///
public virtual SelectionStyles Style {
get { return selectionStyle;}
set {
if (value != selectionStyle) {
selectionStyle = value;
if (region != null) {
region.Dispose();
region = null;
}
}
}
}
///
///
/// paints the selection
///
public virtual void DoPaint(Graphics gr) {
// If we're not visible, then there's nothing to do...
//
if ((GetRules() & SelectionRules.Visible) == SelectionRules.None) return;
bool fActive = false;
if (selUIsvc.selSvc != null) {
fActive = component == selUIsvc.selSvc.PrimarySelection;
// Office rules: If this is a multi-select, reverse the colors for active / inactive.
//
fActive = (fActive == (selUIsvc.selSvc.SelectionCount <= 1));
}
Rectangle r = new Rectangle(outerRect.X, outerRect.Y,
GRABHANDLE_WIDTH, GRABHANDLE_HEIGHT);
Rectangle inner = innerRect;
Rectangle outer = outerRect;
Region oldClip = gr.Clip;
Color borderColor = SystemColors.Control;
if (control != null && control.Parent != null) {
Control parent = control.Parent;
borderColor = parent.BackColor;
}
Brush brush = new SolidBrush(borderColor);
gr.ExcludeClip(inner);
gr.FillRectangle(brush, outer);
gr.Clip = oldClip;
ControlPaint.DrawSelectionFrame( gr, false, outer, inner, borderColor );
//if it's not locked & it is sizeable...
if (((GetRules() & SelectionRules.Locked) == SelectionRules.None) && (GetRules() & SelectionRules.AllSizeable) != SelectionRules.None) {
// upper left
ControlPaint.DrawGrabHandle(gr, r, fActive, (sizes[0] != 0));
// upper right
r.X = inner.X + inner.Width;
ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[2] != 0);
// lower right
r.Y = inner.Y + inner.Height;
ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[7] != 0);
// lower left
r.X = outer.X;
ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[5] != 0);
// lower middle
r.X += (outer.Width - GRABHANDLE_WIDTH) / 2;
ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[6] != 0);
// upper middle
r.Y = outer.Y;
ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[1] != 0);
// left middle
r.X = outer.X;
r.Y = inner.Y + (inner.Height - GRABHANDLE_HEIGHT) / 2;
ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[3] != 0);
// right middle
r.X = inner.X + inner.Width;
ControlPaint.DrawGrabHandle(gr, r, fActive, sizes[4] != 0);
}
else {
ControlPaint.DrawLockedFrame(gr, outer, fActive);
}
}
///
///
/// Retrieves an appropriate cursor at the given point. If there is no appropriate
/// cursor here (ie, the point lies outside the selection rectangle), then this
/// will return null.
///
public virtual Cursor GetCursorAtPoint(Point pt) {
Cursor cursor = null;
if (PointWithinSelection(pt)) {
int nOffset = -1;
if ((GetRules() & SelectionRules.AllSizeable) != SelectionRules.None) {
nOffset = GetHandleIndexOfPoint(pt);
}
if (-1 == nOffset) {
if ((GetRules() & SelectionRules.Moveable) == SelectionRules.None) {
cursor = Cursors.Default;
}
else {
cursor = Cursors.SizeAll;
}
}
else {
cursor = cursors[nOffset];
}
}
return cursor;
}
///
///
/// returns the hit test code of the given point. This may be one of:
///
public virtual int GetHitTest(Point pt) {
// Is it within our rects?
//
if (!PointWithinSelection(pt)) {
return NOHIT;
}
// Which index in the array is this?
//
int nOffset = GetHandleIndexOfPoint(pt);
// If no index, the user has picked on the hatch
//
if (-1 == nOffset || sizes[nOffset] == 0) {
return((GetRules() & SelectionRules.Moveable) == SelectionRules.None ? 0 : MOVE_X | MOVE_Y);
}
return sizes[nOffset];
}
///
///
/// gets the array offset of the handle at the given point
///
private int GetHandleIndexOfPoint(Point pt) {
if (pt.X >= outerRect.X && pt.X <= innerRect.X) {
// Something on the left side.
if (pt.Y >= outerRect.Y && pt.Y <= innerRect.Y)
return 0; // top left
if (pt.Y >= innerRect.Y + innerRect.Height && pt.Y <= outerRect.Y + outerRect.Height)
return 5; // bottom left
if (pt.Y >= outerRect.Y + (outerRect.Height - GRABHANDLE_HEIGHT) / 2
&& pt.Y <= outerRect.Y + (outerRect.Height + GRABHANDLE_HEIGHT) / 2)
return 3; // middle left
return -1; // unknown hit
}
if (pt.Y >= outerRect.Y && pt.Y <= innerRect.Y) {
// something on the top
Debug.Assert(!(pt.X >= outerRect.X && pt.X <= innerRect.X),
"Should be handled by left top check");
if (pt.X >= innerRect.X + innerRect.Width && pt.X <= outerRect.X + outerRect.Width)
return 2; // top right
if (pt.X >= outerRect.X + (outerRect.Width - GRABHANDLE_WIDTH) / 2
&& pt.X <= outerRect.X + (outerRect.Width + GRABHANDLE_WIDTH) / 2)
return 1; // top middle
return -1; // unknown hit
}
if (pt.X >= innerRect.X + innerRect.Width && pt.X <= outerRect.X + outerRect.Width) {
// something on the right side
Debug.Assert(!(pt.Y >= outerRect.Y && pt.Y <= innerRect.Y),
"Should be handled by top right check");
if (pt.Y >= innerRect.Y + innerRect.Height && pt.Y <= outerRect.Y + outerRect.Height)
return 7; // bottom right
if (pt.Y >= outerRect.Y + (outerRect.Height - GRABHANDLE_HEIGHT) / 2
&& pt.Y <= outerRect.Y + (outerRect.Height + GRABHANDLE_HEIGHT) / 2)
return 4; // middle right
return -1; // unknown hit
}
if (pt.Y >= innerRect.Y + innerRect.Height && pt.Y <= outerRect.Y + outerRect.Height) {
// something on the bottom
Debug.Assert(!(pt.X >= outerRect.X && pt.X <= innerRect.X),
"Should be handled by left bottom check");
Debug.Assert(!(pt.X >= innerRect.X + innerRect.Width && pt.X <= outerRect.X + outerRect.Width),
"Should be handled by right bottom check");
if (pt.X >= outerRect.X + (outerRect.Width - GRABHANDLE_WIDTH) / 2
&& pt.X <= outerRect.X + (outerRect.Width + GRABHANDLE_WIDTH) / 2)
return 6; // bottom middle
return -1; // unknown hit
}
return -1; // unknown hit
}
///
///
/// returns a region handle that defines this selection. This is used to piece
/// together a paint region for the surface that we draw our selection handles on
///
public virtual Region GetRegion() {
if (region == null) {
if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !outerRect.IsEmpty) {
region = new Region(outerRect);
region.Exclude(innerRect);
}
else {
region = new Region(new Rectangle(0, 0, 0, 0));
}
if (handler != null) {
Rectangle handlerClip = handler.GetSelectionClipRect(component);
if (!handlerClip.IsEmpty) {
region.Intersect(selUIsvc.RectangleToClient(handlerClip));
}
}
}
return region;
}
///
///
/// Retrieves the rules associated with this selection.
///
public SelectionRules GetRules() {
return selectionRules;
}
public void Dispose() {
if (region != null) {
region.Dispose();
region = null;
}
}
///
///
/// Invalidates the region for this selection glyph.
///
public void Invalidate() {
if (!outerRect.IsEmpty && !selUIsvc.Disposing) {
selUIsvc.Invalidate(outerRect);
}
}
///
///
/// Part of our hit testing logic; determines if the point is somewhere
/// within our selection.
///
protected bool PointWithinSelection(Point pt) {
// This is only supported for visible selections
//
if ((GetRules() & SelectionRules.Visible) == SelectionRules.None || outerRect.IsEmpty || innerRect.IsEmpty) {
return false;
}
if (pt.X < outerRect.X || pt.X > outerRect.X + outerRect.Width) {
return false;
}
if (pt.Y < outerRect.Y || pt.Y > outerRect.Y + outerRect.Height) {
return false;
}
if (pt.X > innerRect.X
&& pt.X < innerRect.X + innerRect.Width
&& pt.Y > innerRect.Y
&& pt.Y < innerRect.Y + innerRect.Height) {
return false;
}
return true;
}
///
///
/// Updates the available grab handle settings based on the current rules.
///
private void UpdateGrabSettings() {
SelectionRules rules = GetRules();
if ((rules & SelectionRules.AllSizeable) == SelectionRules.None) {
sizes = inactiveSizeArray;
cursors = inactiveCursorArray;
}
else {
sizes = new int[8];
cursors = new Cursor[8];
Array.Copy(activeCursorArrays, cursors, cursors.Length);
Array.Copy(activeSizeArray, sizes, sizes.Length);
if ((rules & SelectionRules.TopSizeable) != SelectionRules.TopSizeable) {
sizes[0] = 0;
sizes[1] = 0;
sizes[2] = 0;
cursors[0] = Cursors.Arrow;
cursors[1] = Cursors.Arrow;
cursors[2] = Cursors.Arrow;
}
if ((rules & SelectionRules.LeftSizeable) != SelectionRules.LeftSizeable) {
sizes[0] = 0;
sizes[3] = 0;
sizes[5] = 0;
cursors[0] = Cursors.Arrow;
cursors[3] = Cursors.Arrow;
cursors[5] = Cursors.Arrow;
}
if ((rules & SelectionRules.BottomSizeable) != SelectionRules.BottomSizeable) {
sizes[5] = 0;
sizes[6] = 0;
sizes[7] = 0;
cursors[5] = Cursors.Arrow;
cursors[6] = Cursors.Arrow;
cursors[7] = Cursors.Arrow;
}
if ((rules & SelectionRules.RightSizeable) != SelectionRules.RightSizeable) {
sizes[2] = 0;
sizes[4] = 0;
sizes[7] = 0;
cursors[2] = Cursors.Arrow;
cursors[4] = Cursors.Arrow;
cursors[7] = Cursors.Arrow;
}
}
}
///
///
/// Updates our cached selection rules based on current
/// handler values.
///
public void UpdateRules() {
if (handler == null) {
selectionRules = SelectionRules.None;
}
else {
SelectionRules oldRules = selectionRules;
selectionRules = handler.GetComponentRules(component);
if (selectionRules != oldRules) {
UpdateGrabSettings();
Invalidate();
}
}
}
///
///
/// rebuilds the inner and outer rectangles based on the current
/// selItem.component dimensions. We could calcuate this every time, but that
/// would be expensive for functions like getHitTest that are called a lot
/// (like on every mouse move)
///
public virtual bool UpdateSize() {
bool sizeChanged = false;
// Short circuit common cases
//
if (handler == null) return false;
if ((GetRules() & SelectionRules.Visible) ==SelectionRules.None) return false;
innerRect = handler.GetComponentBounds(component);
if (!innerRect.IsEmpty) {
innerRect = selUIsvc.RectangleToClient(innerRect);
Rectangle rcOuterNew = new Rectangle(
innerRect.X - GRABHANDLE_WIDTH,
innerRect.Y - GRABHANDLE_HEIGHT,
innerRect.Width + 2 * GRABHANDLE_WIDTH,
innerRect.Height + 2 * GRABHANDLE_HEIGHT);
if (outerRect.IsEmpty || !outerRect.Equals(rcOuterNew)) {
if (!outerRect.IsEmpty)
Invalidate();
outerRect = rcOuterNew;
Invalidate();
if (region != null) {
region.Dispose();
region = null;
}
sizeChanged = true;
}
}
else {
Rectangle rcNew = new Rectangle(0, 0, 0, 0);
sizeChanged = outerRect.IsEmpty || !outerRect.Equals(rcNew);
innerRect = outerRect = rcNew;
}
return sizeChanged;
}
}
private class ContainerSelectionUIItem : SelectionUIItem {
public const int CONTAINER_WIDTH = 13;
public const int CONTAINER_HEIGHT = 13;
///
///
/// constructor
///
public ContainerSelectionUIItem(SelectionUIService selUIsvc, object component) : base(selUIsvc, component) {
}
public override Cursor GetCursorAtPoint(Point pt) {
if ((GetHitTest(pt) & CONTAINER_SELECTOR) != 0 && (GetRules() & SelectionRules.Moveable) != SelectionRules.None) {
return Cursors.SizeAll;
}
else {
return null;
}
}
public override int GetHitTest(Point pt) {
int ht = NOHIT;
if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !outerRect.IsEmpty) {
Rectangle r = new Rectangle(outerRect.X, outerRect.Y,
CONTAINER_WIDTH, CONTAINER_HEIGHT);
if (r.Contains(pt)) {
ht = CONTAINER_SELECTOR;
if ((GetRules() & SelectionRules.Moveable) != SelectionRules.None) {
ht |= MOVE_X | MOVE_Y;
}
}
}
return ht;
}
public override void DoPaint(Graphics gr) {
// If we're not visible, then there's nothing to do...
//
if ((GetRules() & SelectionRules.Visible) == SelectionRules.None) return;
Rectangle glyphBounds = new Rectangle(outerRect.X, outerRect.Y,
CONTAINER_WIDTH, CONTAINER_HEIGHT);
ControlPaint.DrawContainerGrabHandle(gr, glyphBounds);
}
public override Region GetRegion() {
if (region == null) {
if ((GetRules() & SelectionRules.Visible) != SelectionRules.None && !outerRect.IsEmpty) {
Rectangle r = new Rectangle(outerRect.X, outerRect.Y,
CONTAINER_WIDTH, CONTAINER_HEIGHT);
region = new Region(r);
}
else {
region = new Region(new Rectangle(0, 0, 0, 0));
}
}
return region;
}
}
private struct HitTestInfo {
public readonly int hitTest;
public readonly SelectionUIItem selectionUIHit;
public readonly bool containerSelector;
public HitTestInfo(int hitTest, SelectionUIItem selectionUIHit) {
this.hitTest = hitTest;
this.selectionUIHit = selectionUIHit;
this.containerSelector = false;
}
public HitTestInfo(int hitTest, SelectionUIItem selectionUIHit, bool containerSelector) {
this.hitTest = hitTest;
this.selectionUIHit = selectionUIHit;
this.containerSelector = containerSelector;
}
// Standard 'catch all - rethrow critical' exception pattern
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
[SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
public override bool Equals(object obj) {
try {
HitTestInfo hi = (HitTestInfo)obj;
return hitTest == hi.hitTest && selectionUIHit == hi.selectionUIHit && containerSelector == hi.containerSelector;
}
catch (Exception ex) {
if (ClientUtils.IsCriticalException(ex)) {
throw;
}
}
return false;
}
public static bool operator ==(HitTestInfo left, HitTestInfo right) {
return (left.hitTest == right.hitTest
&& left.selectionUIHit == right.selectionUIHit
&& left.containerSelector == right.containerSelector);
}
public static bool operator !=(HitTestInfo left, HitTestInfo right) {
return !(left == right);
}
public override int GetHashCode() {
int hash = hitTest | selectionUIHit.GetHashCode();
if (containerSelector) {
hash |= 0x10000;
}
return hash;
}
}
}
}
// 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
- DataGridTable.cs
- SimpleMailWebEventProvider.cs
- NameService.cs
- GC.cs
- RegexTree.cs
- ContractUtils.cs
- ZipIOEndOfCentralDirectoryBlock.cs
- DropShadowBitmapEffect.cs
- WindowsListViewGroupHelper.cs
- AjaxFrameworkAssemblyAttribute.cs
- Errors.cs
- PKCS1MaskGenerationMethod.cs
- TypedLocationWrapper.cs
- DatagridviewDisplayedBandsData.cs
- BasePattern.cs
- SynchronizationLockException.cs
- SafeEventHandle.cs
- GroupItem.cs
- DSASignatureDeformatter.cs
- PropertyGrid.cs
- Stackframe.cs
- AsymmetricAlgorithm.cs
- TableHeaderCell.cs
- GenericIdentity.cs
- SqlComparer.cs
- LoadMessageLogger.cs
- ConfigurationManager.cs
- ConfigurationElementCollection.cs
- ConsoleKeyInfo.cs
- AnimationClock.cs
- FixedBufferAttribute.cs
- GlyphRunDrawing.cs
- MessagePropertyVariants.cs
- SerializationAttributes.cs
- UserNamePasswordValidator.cs
- ParenthesizePropertyNameAttribute.cs
- Memoizer.cs
- DecimalStorage.cs
- XmlSignificantWhitespace.cs
- FontFamily.cs
- RefType.cs
- StrokeNodeData.cs
- TrackingConditionCollection.cs
- ControlBuilderAttribute.cs
- Int16AnimationBase.cs
- ControlBuilderAttribute.cs
- XsltCompileContext.cs
- UnsafeNativeMethods.cs
- ADConnectionHelper.cs
- LogRestartAreaEnumerator.cs
- AdCreatedEventArgs.cs
- XPathEmptyIterator.cs
- GridView.cs
- ECDiffieHellmanCngPublicKey.cs
- HttpProfileBase.cs
- InternalResources.cs
- Baml6Assembly.cs
- ExtendedProtectionPolicy.cs
- CheckedListBox.cs
- PerfCounters.cs
- Invariant.cs
- SqlSelectStatement.cs
- CapabilitiesUse.cs
- IssuedSecurityTokenProvider.cs
- WebHttpSecurity.cs
- DecoderFallbackWithFailureFlag.cs
- MLangCodePageEncoding.cs
- SqlRemoveConstantOrderBy.cs
- ObfuscateAssemblyAttribute.cs
- BindingManagerDataErrorEventArgs.cs
- HeaderedContentControl.cs
- HtmlButton.cs
- ArgumentElement.cs
- DelayedRegex.cs
- RegistryExceptionHelper.cs
- DeviceOverridableAttribute.cs
- SemaphoreFullException.cs
- SortExpressionBuilder.cs
- Matrix.cs
- FullTextBreakpoint.cs
- AnnotationResource.cs
- FontDifferentiator.cs
- _ShellExpression.cs
- DispatcherProcessingDisabled.cs
- StreamGeometryContext.cs
- webeventbuffer.cs
- SystemKeyConverter.cs
- FindCriteriaCD1.cs
- ProviderSettingsCollection.cs
- IItemContainerGenerator.cs
- CaseCqlBlock.cs
- DeclarativeCatalogPart.cs
- HttpRuntimeSection.cs
- ZipIOExtraFieldZip64Element.cs
- EncoderReplacementFallback.cs
- SerializationEventsCache.cs
- QueryableFilterRepeater.cs
- SyncMethodInvoker.cs
- SystemWebCachingSectionGroup.cs
- XmlChildEnumerator.cs