Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / WinForms / System / WinForms / Design / FlowLayoutPanelDesigner.cs / 2 / FlowLayoutPanelDesigner.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
/*
*/
namespace System.Windows.Forms.Design {
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System;
using System.Design;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Windows.Forms.Design.Behavior;
using Microsoft.Win32;
///
///
/// This class handles all design time behavior for the FlowLayoutPanel control. Basically,
/// this designer carefully watches drag operations. During a drag, we attempt to draw an
/// "I" bar for insertion/feedback purposes. When a control is added to our designer, we check
/// some cached state to see if we believe that it needs to be inserted at a particular index. If
/// so, we re-insert the control appropriately.
///
internal class FlowLayoutPanelDesigner : FlowPanelDesigner {
private struct ChildInfo {
public Rectangle marginBounds;//represents the bounds (incl. margins) of a child - used for hittesting
public Rectangle controlBounds; //bounds of the control -- used for drawing the IBar
public bool inSelectionColl;//is this child in the selection collection?
}
private ChildInfo[] childInfo;
private ArrayList dragControls; //the controls that are actually being dragged -- used for an internal drag
private Control primaryDragControl; //the primary drag control
// commonSizes is used to store the maximum height/width of each row/col. We need to do so by storing
// rather than Height/Width. See VSWhidbey 584477, DevDiv Bugs 71258
private ArrayList commonSizes;//format: Rectangle[] (Top | Left, Bottom | Right, height/width of row/col, index of Last ctrl on Row/Col)
private int insertIndex;//the index which we will re-insert a newly added child
private Point lastMouseLoc;//use for mouse tracking purposes
private Point oldP1, oldP2;//tracks the last rendered I-bar location
private static readonly int InvalidIndex = -1;
private const int iBarHalfSize = 2;
private const int minIBar = 10; //if space for IBar is <= minIBar we draw a simple IBar
private const int iBarHatHeight = 3;
private const int iBarSpace = 2;
private const int iBarLineOffset = (iBarHatHeight + iBarSpace);
private const int iBarHatWidth = 5;
private int maxIBarWidth = Math.Max(iBarHalfSize, (iBarHatWidth - 1)/2); //since we don't always know whic IBar we are going draw we want to invalidate max area
///
///
/// Simple constructor that cretes our array list and clears our Ibar points.
///
public FlowLayoutPanelDesigner() {
commonSizes = new ArrayList();
oldP1 = oldP2 = Point.Empty;
insertIndex = InvalidIndex;
}
///
///
/// This is called to check whether a generic dragbox should be drawn when dragging a toolbox item
/// over the designer's surface.
///
protected override bool AllowGenericDragBox {
get {
return false;
}
}
///
///
/// This is called to check whether the z-order of dragged controls should be maintained when dropped on a
/// ParentControlDesigner. By default it will, but e.g. FlowLayoutPanelDesigner wants to do its own z-ordering.
///
/// If this returns true, then the DropSourceBehavior will attempt to set the index of the controls being
/// dropped to preserve the original order (in the dragSource). If it returns false, the index will not
/// be set.
///
/// If this is set to false, then the DropSourceBehavior will not treat a drag as a local drag even
/// if the dragSource and the dragTarget are the same. This will allow a ParentControlDesigner to hook
/// OnChildControlAdded to set the right child index, since in this case, the control(s) being dragged
/// will be removed from the dragSource and then added to the dragTarget.
///
///
protected internal override bool AllowSetChildIndexOnDrop {
get {
return false;
}
}
///
///
/// Simply returns the designer's control as a FlowLayoutPanel
///
private new FlowLayoutPanel Control {
get {
return base.Control as FlowLayoutPanel;
}
}
// per VSWhidbey #424850 adding this to this class...
protected override InheritanceAttribute InheritanceAttribute {
get {
if ((base.InheritanceAttribute == InheritanceAttribute.Inherited)
|| (base.InheritanceAttribute == InheritanceAttribute.InheritedReadOnly)) {
return InheritanceAttribute.InheritedReadOnly;
}
return base.InheritanceAttribute;
}
}
///
///
/// Shadows the FlowDirection property. We do this so that we can update the areas
/// covered by glyphs correctly. VSWhidbey# 232910.
///
private FlowDirection FlowDirection {
get {
return Control.FlowDirection;
}
set {
if (value != Control.FlowDirection) {
//Since we don't know which control is going to go where,
//we just invalidate the area corresponding to the ClientRectangle
//in the adornerwindow
BehaviorService.Invalidate(BehaviorService.ControlRectInAdornerWindow(Control));
Control.FlowDirection = value;
}
}
}
///
///
/// Returns true if flow direction is right-to-left or left-to-right
///
private bool HorizontalFlow {
get {
return Control.FlowDirection == FlowDirection.RightToLeft || Control.FlowDirection == FlowDirection.LeftToRight;
}
}
private FlowDirection RTLTranslateFlowDirection(FlowDirection direction) {
if (Control.RightToLeft == RightToLeft.No) {
return direction;
}
switch (direction) {
case FlowDirection.LeftToRight:
return FlowDirection.RightToLeft;
case FlowDirection.RightToLeft:
return FlowDirection.LeftToRight;
case FlowDirection.TopDown:
case FlowDirection.BottomUp:
return direction;
default: {
Debug.Fail("Unknown FlowDirection");
return direction;
}
}
}
///
/// Returns a Rectangle representing the margin bounds of the control.
///
private Rectangle GetMarginBounds(Control control) {
//If the FLP is RightToLeft.Yes, then the values of Right and Left margins are swapped, account for that here.
return new Rectangle(control.Bounds.Left - (Control.RightToLeft == RightToLeft.No ? control.Margin.Left : control.Margin.Right),
control.Bounds.Top - control.Margin.Top,
control.Bounds.Width + control.Margin.Horizontal, // width should include both margins
control.Bounds.Height+ control.Margin.Vertical); // so should height
}
///
///
/// Called when we receive a DragEnter notification - here we attempt to cache child position and information
/// intended to be used by drag move & drop messages. Basically we pass through the children twice - first
/// we build up an array of rects representing the children bounds (w/margins) and identify where the row/
/// column changes are. Secondly, we normalize the child rects so that children in each row/column are the
/// same height/width;
///
private void CreateMarginBoundsList() {
commonSizes.Clear();
if (Control.Controls.Count == 0) {
childInfo = new ChildInfo[0];
return;
}
//this will cache all needed info for the children
childInfo = new ChildInfo[Control.Controls.Count];
Point offset = Control.PointToScreen(Point.Empty);
// cache these 2
FlowDirection flowDirection = RTLTranslateFlowDirection(Control.FlowDirection);
bool horizontalFlow = HorizontalFlow;
int currentMinTopLeft = Int32.MaxValue;
int currentMaxBottomRight = -1;
int lastOffset = -1;
if ((horizontalFlow && flowDirection == FlowDirection.RightToLeft) ||
(!horizontalFlow && flowDirection == FlowDirection.BottomUp)) {
lastOffset = Int32.MaxValue;
}
int i = 0;
bool isRTL = (Control.RightToLeft == RightToLeft.Yes);
//pass 1 - store off the original margin rects & identify row/column sizes
for( i = 0; i < Control.Controls.Count; i++) {
Control currentControl = Control.Controls[i];//save time
Rectangle rect = GetMarginBounds(currentControl);
Rectangle bounds = currentControl.Bounds;
//fix up bounds such that the IBar is not drawn right on top of the control
if (horizontalFlow) {
//difference between bounds and rect is that we do not adjust top, bottom, height
//If the FLP is RightToLeft.Yes, then the values of Right and Left margins are swapped, account for that here.
bounds.X -= (!isRTL ? currentControl.Margin.Left : currentControl.Margin.Right);
//to draw correctly in dead areas
bounds.Width += currentControl.Margin.Horizontal;
//we want the IBar to stop at the very edge of the control. Offset height
//by 1 pixel to ensure that. This is the classic - how many pixels to draw when you
//draw from Bounds.Top to Bounds.Bottom.
bounds.Height -= 1;
}
else {
//difference between bounds and rect is that we do not adjust left, right, width
bounds.Y -= currentControl.Margin.Top;
//to draw correctly in dead areas
bounds.Height += currentControl.Margin.Vertical;
//we want the IBar to stop at the very edge of the control. Offset width
//by 1 pixel to ensure that. This is the classic - how many pixels to draw when you
//draw from Bounds.Left to Bounds.Right.
bounds.Width -= 1;
}
rect.Offset(offset.X, offset.Y);
bounds.Offset(offset.X, offset.Y);
childInfo[i].marginBounds = rect;
childInfo[i].controlBounds = bounds;
childInfo[i].inSelectionColl = false;
if (dragControls != null && dragControls.Contains(currentControl)) {
childInfo[i].inSelectionColl = true;
}
if (horizontalFlow) {
//identify a new row
if (flowDirection == FlowDirection.LeftToRight ? rect.X < lastOffset : rect.X > lastOffset) {
Debug.Assert(currentMinTopLeft > 0 && currentMaxBottomRight > 0, "How can we not have a min/max value?");
if (currentMinTopLeft > 0 && currentMaxBottomRight > 0) {
//store off the
// min Top|Left for Row/Col
// max Bottom|Right for Row/Col
// Height/Width of Row/Col
// Control index
commonSizes.Add(new Rectangle(currentMinTopLeft, currentMaxBottomRight, currentMaxBottomRight - currentMinTopLeft, i));
currentMinTopLeft = Int32.MaxValue;
currentMaxBottomRight = -1;
}
}
lastOffset = rect.X;
//be sure to track the largest row size
if (rect.Top < currentMinTopLeft) {
currentMinTopLeft = rect.Top;
}
if (rect.Bottom > currentMaxBottomRight) {
currentMaxBottomRight = rect.Bottom;
}
}
else {
//identify a new column
if (flowDirection == FlowDirection.TopDown ? rect.Y < lastOffset : rect.Y > lastOffset) {
Debug.Assert(currentMinTopLeft > 0 && currentMaxBottomRight > 0, "How can we not have a min/max value?");
if (currentMinTopLeft > 0 && currentMaxBottomRight > 0) {
//store off the
// min Top|Left for Row/Col
// max Bottom|Right for Row/Col
// Height/Width of Row/Col
// Control index
commonSizes.Add(new Rectangle(currentMinTopLeft, currentMaxBottomRight, currentMaxBottomRight - currentMinTopLeft, i));
currentMinTopLeft = Int32.MaxValue;
currentMaxBottomRight = -1;
}
}
lastOffset = rect.Y;
//be sure to track the column size
if (rect.Left < currentMinTopLeft) {
currentMinTopLeft = rect.Left;
}
if (rect.Right > currentMaxBottomRight) {
currentMaxBottomRight = rect.Right;
}
}
}
//add the last row/column to our commonsizes
if (currentMinTopLeft > 0 && currentMaxBottomRight > 0) {
//store off the max size for this row
commonSizes.Add(new Rectangle(currentMinTopLeft, currentMaxBottomRight, currentMaxBottomRight - currentMinTopLeft, i));
}
//pass2 - adjust all controls to max width/height according to their row/col
int controlIndex = 0;
for (i = 0; i < commonSizes.Count; i++) {
while (controlIndex < ((Rectangle)commonSizes[i]).Height) {// Remember that Height is control index
if (horizontalFlow) {
childInfo[controlIndex].marginBounds.Y = ((Rectangle)commonSizes[i]).X; // X is Top
childInfo[controlIndex].marginBounds.Height = ((Rectangle)commonSizes[i]).Width;
}
else {
childInfo[controlIndex].marginBounds.X = ((Rectangle)commonSizes[i]).X; // X is Left
childInfo[controlIndex].marginBounds.Width = ((Rectangle)commonSizes[i]).Width;
}
controlIndex++;
}
}
}
///
///
/// Overrides the base .
///
public override void Initialize(IComponent component) {
base.Initialize(component);
// VSWhidbey #424845. If the FLP is inheritedreadonly, so should all of the children
if (InheritanceAttribute == InheritanceAttribute.InheritedReadOnly) {
for (int i = 0; i < Control.Controls.Count; i++) {
TypeDescriptor.AddAttributes(Control.Controls[i], InheritanceAttribute.InheritedReadOnly);
}
}
}
///
///
/// When a child is added -we check to see if we cached an index
/// representing where this control should be inserted. If so, we
/// re-insert the new child.
///
/// This is only done on an external drag-drop
///
private void OnChildControlAdded(object sender, ControlEventArgs e) {
if (insertIndex != InvalidIndex) { // this will only be true on a drag-drop
IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost;
if (host != null) {
IComponentChangeService cs = GetService(typeof(IComponentChangeService)) as IComponentChangeService;
PropertyDescriptor controlsProp = TypeDescriptor.GetProperties(Control)["Controls"];
if (cs != null && controlsProp != null) {
cs.OnComponentChanging(Control, controlsProp);
//on an external drag/drop, the control will have been inserted at the end, so we can safely
//set the index and increment it, since we are moving the control backwards. Check out
//SetChildIndex and MoveElement.
Control.Controls.SetChildIndex(e.Control, insertIndex);
++insertIndex;
cs.OnComponentChanged(Control, controlsProp, null, null);
}
}
}
}
///
///
/// On a drop, if we have cached a special index where we think a control
/// should be inserted - we check to see if this was a pure-local drag
/// (i.e. we dragged a child control inside ourself). If so, we re-insert the
/// child to the appropriate index. Otherwise, we'll do this in the ChildAdded
/// event.
///
protected override void OnDragDrop(DragEventArgs de) {
bool localDrag = false;
if (dragControls != null && primaryDragControl != null && Control.Controls.Contains(primaryDragControl)) {
localDrag = true;
}
if (!localDrag) {
//if we are not a local drag then just let the base handle it.
if (Control != null) {
Control.ControlAdded += new ControlEventHandler(this.OnChildControlAdded);
}
try {
base.OnDragDrop(de);
}
finally {
if (Control != null) {
Control.ControlAdded -= new ControlEventHandler(this.OnChildControlAdded);
}
}
}
else {
// local drag. We do it ourselves, so that we can set the indices right
IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost;
if (host != null) {
DesignerTransaction trans = null;
string transDesc;
bool performCopy = (de.Effect == DragDropEffects.Copy);
// We use this list when doing a Drag-Copy, so that we can correctly restore state when we are done.
ArrayList originalControls = null;
ISelectionService selSvc = null;
if (dragControls.Count == 1) {
string name = TypeDescriptor.GetComponentName(dragControls[0]);
if (name == null || name.Length == 0) {
name = dragControls[0].GetType().Name;
}
transDesc = SR.GetString(performCopy ? SR.BehaviorServiceCopyControl : SR.BehaviorServiceMoveControl, name);
}
else {
transDesc = SR.GetString(performCopy ? SR.BehaviorServiceCopyControls : SR.BehaviorServiceMoveControls, dragControls.Count);
}
trans = host.CreateTransaction(transDesc) ;
try {
//In order to be able to set the index correctly, we need to create a backwards move.
//We do this by first finding the control foo that corresponds to insertIndex.
//We then remove all the drag controls from the FLP.
//Then we get the new childIndex for the control foo.
//Finally we loop:
// add the ith drag control
// set its child index to (index of control foo) - 1
//On each iteration, the child index of control foo will change.
//
//This ensures that we can move both contiguous and non-contiguous selections.
//Special case when the element we are inserting before is a part of the dragControls
while ((insertIndex < childInfo.Length-1) && (childInfo[insertIndex].inSelectionColl)){
// Find the next control that is not a part of the selection
++insertIndex;
}
IComponentChangeService cs = GetService(typeof(IComponentChangeService)) as IComponentChangeService;
PropertyDescriptor controlsProp = TypeDescriptor.GetProperties(Control)["Controls"];
Control control = null;
if (insertIndex != childInfo.Length) {
control = Control.Controls[insertIndex];
}
else {
//we are inserting past the last control
insertIndex = -1;
}
if (cs != null && controlsProp != null) {
cs.OnComponentChanging(Control, controlsProp);
}
//remove the controls in the dragcollection - don't need to do this if we are copying
if (!performCopy) {
for (int i = 0; i < dragControls.Count; i++) {
Control.Controls.Remove(dragControls[i] as Control);
}
// get the new index -- if we are performing a copy, then the index is the same
if (control != null) {
insertIndex = Control.Controls.GetChildIndex(control, false);
}
}
else {
// We are doing a copy, so let's copy the controls
#if DEBUG
if (control != null) {
Debug.Assert(insertIndex == Control.Controls.GetChildIndex(control, false), "Why are the indices not the same?");
}
#endif
// Get the objects to copy
ArrayList temp = new ArrayList();
for (int i = 0; i < dragControls.Count; i++) {
temp.Add(dragControls[i]);
}
// Create a copy of them
temp = DesignerUtils.CopyDragObjects(temp, Component.Site) as ArrayList;
if (temp == null) {
Debug.Fail("Couldn't create copies of the controls we are dragging.");
return;
}
originalControls = new ArrayList();
// And stick the copied controls back into the dragControls array
for (int j = 0; j < temp.Count; j++) {
// ... but save off the old controls first
originalControls.Add(dragControls[j]);
// remember to set the new primary control
if (primaryDragControl.Equals(dragControls[j] as Control)) {
primaryDragControl = temp[j] as Control;
}
dragControls[j] = temp[j];
}
selSvc = (ISelectionService)GetService(typeof(ISelectionService));
}
if (insertIndex == -1) {
//Either insertIndex was childInfo.Length (inserting past the end) or
//insertIndex was childInfo.Length - 1 and the control at that index was also
//a part of the dragCollection. In either case, the new index is equal to the count
//of existing controls in the controlCollection. Helps to draw this out...
insertIndex = Control.Controls.Count;
}
//do the primary control first
Control.Controls.Add(primaryDragControl);
Control.Controls.SetChildIndex(primaryDragControl, insertIndex);
++insertIndex;
if (selSvc != null) {
Debug.Assert(performCopy, "selSvc should only be non-null when we are doing a local copy");
selSvc.SetSelectedComponents(new object[] {primaryDragControl}, SelectionTypes.Primary | SelectionTypes.Replace);
}
//now do the rest
//
//Note dragControls are in opposite zorder than what FLP uses,
//so add from the end.
for (int i = dragControls.Count - 1; i >= 0; i--) {
if (primaryDragControl.Equals(dragControls[i] as Control)) {
continue;
}
Control.Controls.Add(dragControls[i] as Control);
Control.Controls.SetChildIndex(dragControls[i] as Control, insertIndex);
++insertIndex;
if (selSvc != null) {
selSvc.SetSelectedComponents(new object[] {dragControls[i]}, SelectionTypes.Add);
}
}
if (cs != null && controlsProp != null) {
cs.OnComponentChanged(Control, controlsProp, null, null);
}
// If we did a Copy, then restore the old controls to make sure we set state correctly
if (originalControls != null) {
for (int i = 0; i < originalControls.Count; i++) {
dragControls[i] = originalControls[i];
}
}
base.OnDragComplete(de);
if (trans != null) {
trans.Commit();
trans = null;
}
}
finally {
if (trans != null) {
trans.Cancel();
}
}
}
}
insertIndex = InvalidIndex;
}
///
///
/// Called when a drag-drop operation leaves the control designer view
///
///
protected override void OnDragLeave(EventArgs e) {
EraseIBar();
insertIndex = InvalidIndex;
primaryDragControl = null;
if (dragControls != null) {
dragControls.Clear();
}
base.OnDragLeave(e);
}
///
///
/// When we receive a drag enter notification - we clear our recommended insertion
/// index and mose loc - then call our method to cache all the bounds of the children.
///
protected override void OnDragEnter(DragEventArgs de) {
base.OnDragEnter(de);
insertIndex = InvalidIndex;
lastMouseLoc = Point.Empty;
primaryDragControl = null;
//Get the sorted drag controls. We use these for an internal drag.
DropSourceBehavior.BehaviorDataObject data = de.Data as DropSourceBehavior.BehaviorDataObject;
if (data != null) {
int primaryIndex = -1;
dragControls = data.GetSortedDragControls(ref primaryIndex);
primaryDragControl = dragControls[primaryIndex] as Control;
}
//cache all child bounds and identify rows/cols
CreateMarginBoundsList();
}
///
///
/// During a drag over, if we have successfully cached marign/row/col information
/// we will attempt to render an "I-bar" for the user based on where we think the
/// user is attempting to insert the control at. Note that we also cache off this
/// guessed-index so that if a control is dropped/added we can re-insert it at this
/// spot.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
protected override void OnDragOver(DragEventArgs de) {
base.OnDragOver(de);
Point mouseLoc = System.Windows.Forms.Control.MousePosition;
if (mouseLoc.Equals(lastMouseLoc) || childInfo == null || childInfo.Length == 0 || commonSizes.Count == 0) {
//no layout data to work with
return;
}
Rectangle bounds = Rectangle.Empty;
lastMouseLoc = mouseLoc;
Point controlOffset = Control.PointToScreen(new Point(0,0));
if (Control.RightToLeft == RightToLeft.Yes) {
controlOffset.X += Control.Width;
}
insertIndex = InvalidIndex;
//brute force hit testing to first determine if we're over one
//of our margin bounds
//
int i = 0;
for(i = 0; i < childInfo.Length; i++) {
if (childInfo[i].marginBounds.Contains(mouseLoc)) {
bounds = childInfo[i].controlBounds;
break;
}
}
//If we found the bounds - then we need to draw our "I-Beam"
//If the mouse is over one of the marginbounds, then the dragged control
//will always be inserted before the control the margin area represents. Thus
//we will always draw the I-Beam to the left or above (FlowDirection.LRT | TB) or
//to the right or below (FlowDirection.RTL | BT).
if (!bounds.IsEmpty) {
insertIndex = i;//the insertion index will always be the boxed area (called margin area) we are over
if (childInfo[i].inSelectionColl) {
//If the marginBounds is part of the selection, then don't draw the IBar. But actually
//setting insertIndex, will allows us to correctly drop the control in the right place.
EraseIBar();
}
else {
FlowDirection direction = RTLTranslateFlowDirection(Control.FlowDirection);
if (direction == FlowDirection.LeftToRight) {
ReDrawIBar(new Point(bounds.Left, bounds.Top), new Point( bounds.Left, bounds.Bottom));
}
else if (direction == FlowDirection.RightToLeft) {
ReDrawIBar(new Point(bounds.Right, bounds.Top), new Point( bounds.Right, bounds.Bottom));
}
else if (direction == FlowDirection.TopDown) {
ReDrawIBar(new Point(bounds.Left, bounds.Top), new Point(bounds.Right, bounds.Top));
}
else if (direction == FlowDirection.BottomUp) {
ReDrawIBar(new Point(bounds.Left, bounds.Bottom), new Point(bounds.Right, bounds.Bottom));
}
else {
Debug.Fail("Unknown FlowDirection");
}
}
}
else {
//here, we're in a dead area - see what row / column we're in for a
//best-guess at the insertion index
int offset = HorizontalFlow ? controlOffset.Y : controlOffset.X;
bool isRTL = (Control.RightToLeft == RightToLeft.Yes);
for ( i =0; i < commonSizes.Count; i++) {
if (isRTL) {
offset -= ((Rectangle)commonSizes[i]).Width; // Width is height/width of row/col
}
else {
offset += ((Rectangle)commonSizes[i]).Width;
}
// Just introducing match for readability.
bool match = false;
if (!isRTL) {
match = (HorizontalFlow ? (mouseLoc.Y) : (mouseLoc.X)) <= offset;
}
else {
match = (HorizontalFlow && mouseLoc.Y <= offset) || (!HorizontalFlow && mouseLoc.X >= offset);
}
if (match) {
insertIndex = ((Rectangle)commonSizes[i]).Height; // Height is index of last control
bounds = childInfo[insertIndex - 1].controlBounds;
if (childInfo[insertIndex - 1].inSelectionColl) {
EraseIBar();
}
else {
FlowDirection direction = RTLTranslateFlowDirection(Control.FlowDirection);
if (direction == FlowDirection.LeftToRight) {
ReDrawIBar(new Point(bounds.Right, bounds.Top), new Point(bounds.Right, bounds.Bottom));
}
else if (direction == FlowDirection.RightToLeft) {
ReDrawIBar(new Point(bounds.Left, bounds.Top), new Point(bounds.Left, bounds.Bottom));
}
else if (direction == FlowDirection.TopDown) {
ReDrawIBar(new Point(bounds.Left, bounds.Bottom), new Point(bounds.Right, bounds.Bottom));
}
else if (direction == FlowDirection.BottomUp) {
ReDrawIBar(new Point(bounds.Left, bounds.Top), new Point(bounds.Right, bounds.Top));
}
else {
Debug.Fail("Unknown FlowDirection");
}
}
break;
}
}
}
if (insertIndex == InvalidIndex) {
//here, we're at the 'end' of the flowlayoutpanel - not over
//any controls and not in a row/column.
insertIndex = Control.Controls.Count;
EraseIBar();
}
}
private void EraseIBar() {
ReDrawIBar(Point.Empty, Point.Empty);
}
///
///
/// Given two points, we'll draw an 'Ibar". Note that we only erase at our
/// old points if they are different from the new ones. Also note that if
/// the points are empty - we will simply erase and not draw.
///
private void ReDrawIBar(Point p1, Point p2) {
//offset the points to adorner coords
Point offset = BehaviorService.AdornerWindowToScreen();
Pen pen = SystemPens.ControlText;
if (Control.BackColor != Color.Empty && Control.BackColor.GetBrightness() < .5) {
pen = SystemPens.ControlLight;
}
//don't offset if p1 is empty. Empty really just means that we want to erase the IBar.
if (p1 != Point.Empty) {
p1.Offset(-offset.X, -offset.Y);
p2.Offset(-offset.X, -offset.Y);
}
//only erase the ibar if the points are different from last time
//Only invalidate if there's something to invalidate
if (p1 != oldP1 && p2 != oldP2 && oldP1 != Point.Empty) {
Rectangle invalidRect = new Rectangle(oldP1.X, oldP1.Y, oldP2.X - oldP1.X + 1, oldP2.Y - oldP1.Y + 1);
invalidRect.Inflate(maxIBarWidth, maxIBarWidth);//akways invalidate max area
BehaviorService.Invalidate(invalidRect);
}
//cache this for next time around -- but do so before changing p1 and p2 below.
oldP1 = p1;
oldP2 = p2;
//if we have valid new points - redraw our ibar
//we always want to redraw the line. This is because part of it could have been erased when
//the dragimage (see DropSourceBehavior) is being moved over the IBar.
if (p1 != Point.Empty) {
using (Graphics g = BehaviorService.AdornerWindowGraphics) {
if (HorizontalFlow) {
if (Math.Abs(p1.Y - p2.Y) <= minIBar) {
//draw the smaller, simpler IBar
g.DrawLine(pen, p1, p2);//vertical line
g.DrawLine(pen, p1.X -iBarHalfSize, p1.Y, p1.X + iBarHalfSize, p1.Y);//top hat
g.DrawLine(pen, p2.X -iBarHalfSize, p2.Y, p2.X + iBarHalfSize, p2.Y);//bottom hat
}
else {
//top and bottom hat
for (int i = 0; i < iBarHatHeight - 1; i++) { // stop 1 pixel before, since we can't draw a 1 pixel line
// reducing the width of the hat with 2 pixel on each iteration
g.DrawLine(pen, p1.X - (iBarHatWidth - 1 - (i*2))/2, p1.Y+i, p1.X + (iBarHatWidth - 1 - (i*2))/2, p1.Y+i);//top hat
g.DrawLine(pen, p2.X - (iBarHatWidth - 1 - (i*2))/2, p2.Y-i, p2.X + (iBarHatWidth - 1 - (i*2))/2, p2.Y-i);//bottom hat
}
//can't draw a 1 pixel line, so draw a vertical line
g.DrawLine(pen, p1.X, p1.Y, p1.X, p1.Y + iBarHatHeight - 1); // top hat
g.DrawLine(pen, p2.X, p2.Y, p2.X, p2.Y - iBarHatHeight + 1); // bottom hat
// vertical line
g.DrawLine(pen, p1.X, p1.Y + iBarLineOffset, p2.X, p2.Y - iBarLineOffset);//vertical line
}
}
else {
if (Math.Abs(p1.X - p2.X) <= minIBar) {
//draw the smaller, simpler IBar
g.DrawLine(pen, p1, p2);//horizontal line
g.DrawLine(pen, p1.X, p1.Y -iBarHalfSize, p1.X, p1.Y+ iBarHalfSize);//top hat
g.DrawLine(pen, p2.X, p2.Y -iBarHalfSize, p2.X, p2.Y + iBarHalfSize);//bottom hat
}
else {
//left and right hat
for (int i = 0; i < iBarHatHeight - 1; i++) { // stop 1 pixel before, since we can't draw a 1 pixel line
// reducing the width of the hat with 2 pixel on each iteration
g.DrawLine(pen, p1.X+i, p1.Y - (iBarHatWidth - 1 - (i*2))/2, p1.X+i, p1.Y + (iBarHatWidth - 1 - (i*2))/2);//left hat
g.DrawLine(pen, p2.X-i, p2.Y - (iBarHatWidth - 1 - (i*2))/2, p2.X-i, p2.Y + (iBarHatWidth - 1 - (i*2))/2);//right hat
}
//can't draw a 1 pixel line, so draw a horizontal line
g.DrawLine(pen, p1.X, p1.Y, p1.X + iBarHatHeight - 1, p1.Y); // left hat
g.DrawLine(pen, p2.X, p2.Y, p2.X - iBarHatHeight + 1, p2.Y); // right hat
// horizontal line
g.DrawLine(pen, p1.X + iBarLineOffset, p1.Y, p2.X - iBarLineOffset, p2.Y);//horizontal line
}
}
}
}
}
///
///
/// Allows a designer to filter the set of properties
/// the component it is designing will expose through the
/// TypeDescriptor object. This method is called
/// immediately before its corresponding "Post" method.
/// If you are overriding this method you should call
/// the base implementation before you perform your own
/// filtering.
///
protected override void PreFilterProperties(IDictionary properties) {
base.PreFilterProperties(properties);
PropertyDescriptor prop;
// Handle shadowed properties
//
string[] shadowProps = new string[] {
"FlowDirection"
};
Attribute[] empty = new Attribute[0];
for (int i = 0; i < shadowProps.Length; i++) {
prop = (PropertyDescriptor)properties[shadowProps[i]];
if (prop != null) {
properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(FlowLayoutPanelDesigner), prop, empty);
}
}
}
}
}
// 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
- SpecularMaterial.cs
- webbrowsersite.cs
- hebrewshape.cs
- ContentFileHelper.cs
- EllipseGeometry.cs
- FolderBrowserDialog.cs
- DesignTimeTemplateParser.cs
- WhitespaceSignificantCollectionAttribute.cs
- DCSafeHandle.cs
- EditorAttributeInfo.cs
- DrawListViewSubItemEventArgs.cs
- EditorBrowsableAttribute.cs
- DataGridViewSelectedCellCollection.cs
- Vector3D.cs
- CompiledIdentityConstraint.cs
- TextContainerHelper.cs
- MeasurementDCInfo.cs
- ScriptBehaviorDescriptor.cs
- PermissionAttributes.cs
- DataGridViewCheckBoxColumn.cs
- TextTabProperties.cs
- ProfileSection.cs
- TransactedReceiveData.cs
- ThreadInterruptedException.cs
- PrimitiveCodeDomSerializer.cs
- Token.cs
- SafeFileMappingHandle.cs
- LogWriteRestartAreaAsyncResult.cs
- EntitySetDataBindingList.cs
- coordinatorscratchpad.cs
- ObjectPersistData.cs
- DesignTimeResourceProviderFactoryAttribute.cs
- ToolBarOverflowPanel.cs
- CompilerState.cs
- ParameterReplacerVisitor.cs
- NativeMethodsOther.cs
- ControlIdConverter.cs
- DataRowChangeEvent.cs
- Int16Storage.cs
- DefaultValueAttribute.cs
- ListItem.cs
- PersistenceException.cs
- MultilineStringConverter.cs
- MemberInfoSerializationHolder.cs
- SafeLocalAllocation.cs
- TransformPattern.cs
- Color.cs
- Soap.cs
- GeneralTransform3DTo2DTo3D.cs
- TextBoxLine.cs
- StatusBarPanelClickEvent.cs
- ExpandSegmentCollection.cs
- CfgParser.cs
- PrintController.cs
- ProjectionPathSegment.cs
- TagElement.cs
- XmlHierarchicalEnumerable.cs
- Itemizer.cs
- Soap12ServerProtocol.cs
- FlowPanelDesigner.cs
- ExpressionContext.cs
- CommonObjectSecurity.cs
- VideoDrawing.cs
- QueueProcessor.cs
- SvcFileManager.cs
- BufferedGraphicsContext.cs
- CodeAttachEventStatement.cs
- Timer.cs
- HostedElements.cs
- EdmSchemaAttribute.cs
- XmlExtensionFunction.cs
- MonitorWrapper.cs
- ProtocolViolationException.cs
- Touch.cs
- DropDownHolder.cs
- ToolStrip.cs
- ClonableStack.cs
- CommandField.cs
- SessionChannels.cs
- DSACryptoServiceProvider.cs
- GenericAuthenticationEventArgs.cs
- LabelTarget.cs
- DaylightTime.cs
- TextChange.cs
- X509CertificateInitiatorServiceCredential.cs
- VisualCollection.cs
- ArcSegment.cs
- SchemaNamespaceManager.cs
- ValueTable.cs
- FramingEncoders.cs
- BitmapEffectState.cs
- FindCriteria.cs
- LinearKeyFrames.cs
- GacUtil.cs
- RectangleGeometry.cs
- Repeater.cs
- KoreanCalendar.cs
- BuildProvidersCompiler.cs
- IdlingCommunicationPool.cs
- MSAAWinEventWrap.cs