Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / WinForms / System / WinForms / Design / TableLayoutPanelDesigner.cs / 5 / TableLayoutPanelDesigner.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Windows.Forms.Design { using System.CodeDom; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.ComponentModel.Design.Serialization; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System; using System.Design; using System.Drawing; using System.Drawing.Design; using System.Drawing.Drawing2D; using System.Windows.Forms; using System.Windows.Forms.Design; using System.Windows.Forms.Design.Behavior; using Microsoft.Win32; ////// /// This class handles all design time behavior for the TableLayoutPanel class. This class /// is in charge of: making sure all rows/columns are correctly rendered, all drag/move /// gestures correctly add/insert controls into the table, and new controls are added to the /// appropriate cell(s). /// internal class TableLayoutPanelDesigner : FlowPanelDesigner { private TableLayoutPanelBehavior tlpBehavior;//every resize col/row glyph is associated with this instance of behavior private Point droppedCellPosition = InvalidPoint;//used to insert new children // NEVER USE undoing DIRECTLY. ALWAYS USE THE PROPERTY private bool undoing = false; private UndoEngine undoEngine = null; private Control localDragControl;//only valid if we're currently dragging a child control of the table private ArrayList dragComps; //the components we are dragging private DesignerVerbCollection verbs;//add col/row and remove col/row tab verbs private DesignerTableLayoutControlCollection controls; private DesignerVerb removeRowVerb; private DesignerVerb removeColVerb; DesignerActionListCollection actionLists = null;//action list for the Smart Tag private BaseContextMenuStrip designerContextMenuStrip = null; private int curRow = -1; //row cursor was over when context menu was dropped private int curCol= -1; //col cursor was over when context menu was dropped IComponentChangeService compSvc = null; private PropertyDescriptor rowStyleProp; private PropertyDescriptor colStyleProp; // Only used when adding controls via the toolbox private int rowCountBeforeAdd = 0; // What's the row count before a control is added private int colCountBeforeAdd = 0; // Ditto for column private int ensureSuspendCount = 0; ////// /// The instance to our Behavior that every glyph shares. This is /// demand created only once. /// private TableLayoutPanelBehavior Behavior { get { if (tlpBehavior == null) { tlpBehavior = new TableLayoutPanelBehavior(Table, this, Component.Site); } return tlpBehavior; } } // SPECIAL HANDLING - VSWhidbey 483915 // // The only reason we shadow ColumnStyles and RowStyles is to control // how they are serialized out. If the form is not localizable, then we // want to serialize to code. If the form is localized, then we do not // want to serialize to code. This is because the TLP has special logic // that adds these properties to the resx file in this case. It needs this // special code, since the properties are not marked as localizable. // If we also added these to the code, we would actually end up with double // the number of styles in the collections. // // TLP has no setter private TableLayoutColumnStyleCollection ColumnStyles { get { return Table.ColumnStyles; } } // TLP has no setter private TableLayoutRowStyleCollection RowStyles { get { return Table.RowStyles; } } public int RowCount { get { return Table.RowCount; } set { if (value <= 0) { throw new ArgumentException(SR.GetString(SR.TableLayoutPanelDesignerInvalidColumnRowCount, "RowCount")); } else { Table.RowCount = value; } } } public int ColumnCount { get { return Table.ColumnCount; } set { if (value <= 0) { throw new ArgumentException(SR.GetString(SR.TableLayoutPanelDesignerInvalidColumnRowCount, "ColumnCount")); } else { Table.ColumnCount = value; } } } [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] private bool IsLocalizable() { IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null) { PropertyDescriptor prop = TypeDescriptor.GetProperties(host.RootComponent)["Localizable"]; if (prop != null && prop.PropertyType == typeof(bool)) { return (bool) prop.GetValue(host.RootComponent); } } return false; } private bool ShouldSerializeColumnStyles() { return !IsLocalizable(); } private bool ShouldSerializeRowStyles() { return !IsLocalizable(); } // END SPECIAL HANDLING [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] private DesignerTableLayoutControlCollection Controls { get { if (controls == null) { controls = new DesignerTableLayoutControlCollection((TableLayoutPanel)Control); } return controls; } } private ContextMenuStrip DesignerContextMenuStrip { get { if (designerContextMenuStrip == null) { designerContextMenuStrip = new BaseContextMenuStrip(Component.Site, Table); // Remove all the verbs -- except the Edit Rows and Columns ContextMenuStripGroup group = designerContextMenuStrip.Groups[StandardGroups.Verbs]; foreach (DesignerVerb verb in Verbs) { if (verb.Text.Equals(SR.GetString(SR.TableLayoutPanelDesignerEditRowAndCol))) { continue; } foreach (ToolStripItem item in group.Items) { if (item.Text.Equals(verb.Text)) { group.Items.Remove(item); break; } } } // Now build the new menus ToolStripDropDownMenu rowMenu = BuildMenu(true); ToolStripDropDownMenu colMenu = BuildMenu(false); ToolStripMenuItem row = new ToolStripMenuItem(); row.DropDown = rowMenu; row.Text = SR.GetString(SR.TableLayoutPanelDesignerRowMenu); ToolStripMenuItem col = new ToolStripMenuItem(); col.DropDown = colMenu; col.Text = SR.GetString(SR.TableLayoutPanelDesignerColMenu); group.Items.Insert(0, col); group.Items.Insert(0, row); group = designerContextMenuStrip.Groups[StandardGroups.Edit]; foreach (ToolStripItem item in group.Items) { if (item.Text.Equals(SR.GetString(SR.ContextMenuCut))) { item.Text = SR.GetString(SR.TableLayoutPanelDesignerContextMenuCut); } else if (item.Text.Equals(SR.GetString(SR.ContextMenuCopy))) { item.Text = SR.GetString(SR.TableLayoutPanelDesignerContextMenuCopy); } else if (item.Text.Equals(SR.GetString(SR.ContextMenuDelete))) { item.Text = SR.GetString(SR.TableLayoutPanelDesignerContextMenuDelete); } } } return designerContextMenuStrip; } } ////// /// Returns true if the host is loading. /// private bool IsLoading { get { IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null) { return host.Loading; } return false; } } ////// /// Returns our runtime TableLayoutPanel instance. /// internal TableLayoutPanel Table { get { return Component as TableLayoutPanel; } } private bool Undoing { get { if (undoEngine == null) { undoEngine = GetService(typeof(UndoEngine)) as UndoEngine; if (undoEngine != null) { undoEngine.Undoing += new EventHandler(this.OnUndoing); if (undoEngine.UndoInProgress) { undoing = true; undoEngine.Undone += new EventHandler(this.OnUndone); } } } return undoing; } set { undoing = value; } } ////// /// The designer verbs we'll surface for this table. /// // We need to add separate verbs (and not use the ones from the DesignerActionPanel), since // we need to be able to disable/enable the Remove items based on the number of rows/cols, and // you cannot do that via the DesignerAction stuff. // These verbs would be promoted to the DAP, but we need a DAP since we want it to AutoShow. public override DesignerVerbCollection Verbs { get { if (verbs == null) { removeColVerb = new DesignerVerb(SR.GetString(SR.TableLayoutPanelDesignerRemoveColumn), new EventHandler(this.OnVerbRemove)); removeRowVerb = new DesignerVerb(SR.GetString(SR.TableLayoutPanelDesignerRemoveRow), new EventHandler(this.OnVerbRemove)); verbs = new DesignerVerbCollection(); verbs.Add(new DesignerVerb(SR.GetString(SR.TableLayoutPanelDesignerAddColumn), new EventHandler(this.OnVerbAdd))); verbs.Add(new DesignerVerb(SR.GetString(SR.TableLayoutPanelDesignerAddRow), new EventHandler(this.OnVerbAdd))); verbs.Add(removeColVerb); verbs.Add(removeRowVerb); verbs.Add(new DesignerVerb(SR.GetString(SR.TableLayoutPanelDesignerEditRowAndCol), new EventHandler(this.OnVerbEdit))); CheckVerbStatus(); } return verbs; } } private void RefreshSmartTag() { DesignerActionUIService actionUIService = (DesignerActionUIService)GetService(typeof(DesignerActionUIService)); if (actionUIService != null) { actionUIService.Refresh(Component); } } private void CheckVerbStatus() { if (Table != null) { if (removeColVerb != null) { bool colState = Table.ColumnCount > 1; if (removeColVerb.Enabled != colState) { removeColVerb.Enabled = colState; } } if (removeRowVerb != null) { bool rowState = Table.RowCount > 1; if (removeRowVerb.Enabled != rowState) { removeRowVerb.Enabled = rowState; } } RefreshSmartTag(); } } ////// /// public override DesignerActionListCollection ActionLists { get { if (actionLists == null) { BuildActionLists(); } return actionLists; } } private ToolStripDropDownMenu BuildMenu(bool isRow) { ToolStripMenuItem add = new ToolStripMenuItem(); ToolStripMenuItem insert = new ToolStripMenuItem(); ToolStripMenuItem delete = new ToolStripMenuItem(); ToolStripSeparator separator = new ToolStripSeparator(); ToolStripLabel label = new ToolStripLabel(); ToolStripMenuItem absolute = new ToolStripMenuItem(); ToolStripMenuItem percent = new ToolStripMenuItem(); ToolStripMenuItem autosize = new ToolStripMenuItem(); add.Text = SR.GetString(SR.TableLayoutPanelDesignerAddMenu); add.Tag = isRow; add.Name = "add"; add.Click += new System.EventHandler(this.OnAddClick); insert.Text = SR.GetString(SR.TableLayoutPanelDesignerInsertMenu); insert.Tag = isRow; insert.Name = "insert"; insert.Click += new System.EventHandler(this.OnInsertClick); delete.Text = SR.GetString(SR.TableLayoutPanelDesignerDeleteMenu); delete.Tag = isRow; delete.Name = "delete"; delete.Click += new System.EventHandler(this.OnDeleteClick); label.Text = SR.GetString(SR.TableLayoutPanelDesignerLabelMenu); if (SR.GetString(SR.TableLayoutPanelDesignerDontBoldLabel) == "0") { label.Font = new Font(label.Font, FontStyle.Bold); } label.Name = "sizemode"; absolute.Text = SR.GetString(SR.TableLayoutPanelDesignerAbsoluteMenu); absolute.Tag = isRow; absolute.Name = "absolute"; absolute.Click += new System.EventHandler(this.OnAbsoluteClick); percent.Text = SR.GetString(SR.TableLayoutPanelDesignerPercentageMenu); percent.Tag = isRow; percent.Name = "percent"; percent.Click += new System.EventHandler(this.OnPercentClick); autosize.Text = SR.GetString(SR.TableLayoutPanelDesignerAutoSizeMenu); autosize.Tag = isRow; autosize.Name = "autosize"; autosize.Click += new System.EventHandler(this.OnAutoSizeClick); ToolStripDropDownMenu menu = new ToolStripDropDownMenu(); menu.Items.AddRange(new ToolStripItem[] {add, insert, delete, separator, label, absolute, percent, autosize}); menu.Tag = isRow; menu.Opening += new System.ComponentModel.CancelEventHandler(this.OnRowColMenuOpening); IUIService uis = GetService(typeof(IUIService)) as IUIService; if (uis != null) { menu.Renderer = (ToolStripProfessionalRenderer)uis.Styles["VsRenderer"]; } return menu; } private void BuildActionLists() { actionLists = new DesignerActionListCollection(); // Add Column action list actionLists.Add(new TableLayouPanelRowColumnActionList(this)); // if one actionList has AutoShow == true then the chrome panel will popup when the user DnD the DataGridView onto the form // It would make sense to promote AutoShow to DesignerActionListCollection. // But we don't own the DesignerActionListCollection so we just set AutoShow on the first ActionList // actionLists[0].AutoShow = true; } private class TableLayouPanelRowColumnActionList : DesignerActionList { TableLayoutPanelDesigner owner; public TableLayouPanelRowColumnActionList(TableLayoutPanelDesigner owner) : base(owner.Component) { this.owner = owner; } public override DesignerActionItemCollection GetSortedActionItems() { DesignerActionItemCollection items = new DesignerActionItemCollection(); // We don't promote these Items to DesignerVerbs, since we need to be able // to disable/enable the Remove entries, based on the number of Rows/Cols. // Unfortunately, you cannot do that via the DesignerAction stuff. items.Add(new DesignerActionMethodItem(this, "AddColumn", // method name SR.GetString(SR.TableLayoutPanelDesignerAddColumn), // display name false)); // promoteToDesignerVerb items.Add(new DesignerActionMethodItem(this, "AddRow", // method name SR.GetString(SR.TableLayoutPanelDesignerAddRow), // display name false)); // promoteToDesignerVerb if (owner.Table.ColumnCount > 1) { items.Add(new DesignerActionMethodItem(this, "RemoveColumn", // method name SR.GetString(SR.TableLayoutPanelDesignerRemoveColumn), // display name false)); // promoteToDesignerVerb } if (owner.Table.RowCount > 1) { items.Add(new DesignerActionMethodItem(this, "RemoveRow", // method name SR.GetString(SR.TableLayoutPanelDesignerRemoveRow), // display name false)); // promoteToDesignerVerb } items.Add(new DesignerActionMethodItem(this, "EditRowAndCol", // method name SR.GetString(SR.TableLayoutPanelDesignerEditRowAndCol), // display name false)); // promoteToDesignerVerb return items; } // Called through reflection [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void AddColumn() { this.owner.OnAdd(false); } // Called through reflection [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void AddRow() { this.owner.OnAdd(true); } // Called through reflection [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void RemoveColumn() { this.owner.OnRemove(false); } // Called through reflection [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void RemoveRow() { this.owner.OnRemove(true); } // Called through reflection [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void EditRowAndCol() { this.owner.OnEdit(); } } ///Gets the design-time action lists supported by the component associated with the /// designer. ////// /// Called privately - we'll un-sync the removed event and quietly remove the control // from the table. /// private void RemoveControlInternal(Control c) { Table.ControlRemoved -= new ControlEventHandler(this.OnControlRemoved); Table.Controls.Remove(c); Table.ControlRemoved += new ControlEventHandler(this.OnControlRemoved); } ////// /// Called within this designer - this method will un-sync the /// 'ControlAdded' event and add a control (aboslutely positioned) /// to the row and column specified. /// private void AddControlInternal(Control c, int col, int row) { Table.ControlAdded -= new ControlEventHandler(this.OnControlAdded); Table.Controls.Add(c, col, row); Table.ControlAdded += new ControlEventHandler(this.OnControlAdded); } ////// /// Called in response to controls being added - through dragging, copy/paste, toolbox, etc... /// This method removes the control and re-adds it back to the table at an absolute /// position. This is done for more of a deterministic behavior when rows/cols change in /// our table. /// // Standard 'catch all - rethrow critical' exception pattern [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] private void ControlAddedInternal(Control control, Point newControlPosition, bool localReposition, bool fullTable, DragEventArgs de) { //If the table is full - we'll want to 'autogrow' either the row or column based on the grow style property //before we actually add the control. if (fullTable) { if (Table.GrowStyle == TableLayoutPanelGrowStyle.AddRows) { PropertyDescriptor rowProp = TypeDescriptor.GetProperties(Table)["RowCount"]; if (rowProp != null) { rowProp.SetValue(Table, Table.GetRowHeights().Length); } newControlPosition.X = 0; newControlPosition.Y = Table.RowCount - 1; } else if (Table.GrowStyle == TableLayoutPanelGrowStyle.AddColumns) { PropertyDescriptor colProp = TypeDescriptor.GetProperties(Table)["ColumnCount"]; if (colProp != null) { colProp.SetValue(Table, Table.GetColumnWidths().Length); } newControlPosition.X = Table.ColumnCount - 1; newControlPosition.Y = 0; } else { //fixed growstyle - what do we do here? } } //Here, we are adding a control to our table - we need to make sure we // 1) correctly place the new control at its drop location, and // 2) perform a 'swap' if this is entirely a local // reposition. DesignerTransaction trans = null; PropertyDescriptor controlsProp = TypeDescriptor.GetProperties(Table)["Controls"]; //find the control that currently resides at our newControlPosition - we'll want to either //remove it or swap it. try { // Are we doing a local copy bool localCopy = ((de != null) && (de.Effect == DragDropEffects.Copy) && localReposition); Control existingControl = ((TableLayoutPanel)Control).GetControlFromPosition(newControlPosition.X, newControlPosition.Y); if (localCopy) { Debug.Assert(existingControl == null, "We shouldn't be able to do a local copy of a cell with an existing control"); IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null) { trans = host.CreateTransaction(SR.GetString(SR.BehaviorServiceCopyControl, control.Site.Name)); } // Need to do this after the transaction is created PropChanging(controlsProp); } //does the newControlPosition contain a valid control //if so - we need to perform a 'swap' function if this is local - or default //to controls.add if this is from an external source else if (existingControl != null && !existingControl.Equals(control)) { if (localReposition) { // If we're swapping controls, create a DesignerTransaction // so this can be undoable. IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null) { trans = host.CreateTransaction(SR.GetString(SR.TableLayoutPanelDesignerControlsSwapped, control.Site.Name, existingControl.Site.Name)); } // Need to do this after the transaction is created PropChanging(controlsProp); RemoveControlInternal(existingControl);//we found our control to swap } else { //here we externally dragged a control onto a valid control in our table //we'll try to find a place to put it (since we shouldn't be here if our table //was full // [....] -- we shouldn't ever get here... PropChanging(controlsProp); existingControl = null;//null this out since we're not swapping } } else { //here we have a truly empty cell // If we are not doing a local move, then the DropSourceBehavior created the transaction for us if (localReposition) { IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null) { trans = host.CreateTransaction(SR.GetString(SR.BehaviorServiceMoveControl, control.Site.Name)); } } existingControl = null; PropChanging(controlsProp); } //Need to do this after the transaction has been created if (localCopy) { ArrayList temp = new ArrayList(); temp.Add(control); temp = DesignerUtils.CopyDragObjects(temp, Component.Site) as ArrayList; control = temp[0] as Control; } //if we are locally repositioning this control - remove it (internally) //from the table's child collection and add something in its place. This //will be a control to swap it with if (localReposition) { Point oldPosition = GetControlPosition(control); if (oldPosition != InvalidPoint) { RemoveControlInternal(control); if (oldPosition != newControlPosition) {//guard against dropping it back on itself if (existingControl != null) { //we have something to swap... AddControlInternal(existingControl, oldPosition.X, oldPosition.Y); } } } } //Finally - set our new control to the new position if (localReposition) { //If we are doing a local drag, then the control previously got removed AddControlInternal(control, newControlPosition.X, newControlPosition.Y); } else { //If not, then the control has already been added, and all we need to do is set the position Table.SetCellPosition(control, new TableLayoutPanelCellPosition(newControlPosition.X, newControlPosition.Y)); } PropChanged(controlsProp); if (de != null) { base.OnDragComplete(de); } if (trans != null) { trans.Commit(); trans = null; } // Set the selection to be the newly added control - but only if we are doing a local copy if (localCopy) { ISelectionService selSvc = GetService(typeof(ISelectionService)) as ISelectionService; if (selSvc != null) { selSvc.SetSelectedComponents(new object[] {control}, SelectionTypes.Primary | SelectionTypes.Replace); } } } // VSWhidbey #390285 catch (ArgumentException argumentEx) { IUIService uiService = GetService(typeof(IUIService)) as IUIService; if (uiService != null) { uiService.ShowError(argumentEx); } } catch( Exception ex ) { if(ClientUtils.IsCriticalException(ex)) { throw; } } finally { if (trans != null) { trans.Cancel(); } } } private void CreateEmptyTable() { //set the table's default rows and columns PropertyDescriptor colProp = TypeDescriptor.GetProperties(Table)["ColumnCount"]; if (colProp != null) { colProp.SetValue(Table, DesignerUtils.DEFAULTCOLUMNCOUNT); } PropertyDescriptor rowProp = TypeDescriptor.GetProperties(Table)["RowCount"]; if (rowProp != null) { rowProp.SetValue(Table, DesignerUtils.DEFAULTROWCOUNT); } //this will make sure we have styles created for every row & column EnsureAvailableStyles(); InitializeNewStyles(); } private void InitializeNewStyles() { Size tableSize = Table.Size; //adjust the two absolutely positioned columns Table.ColumnStyles[0].SizeType = SizeType.Percent; Table.ColumnStyles[0].Width = DesignerUtils.MINIMUMSTYLEPERCENT; Table.ColumnStyles[1].SizeType = SizeType.Percent; Table.ColumnStyles[1].Width = DesignerUtils.MINIMUMSTYLEPERCENT; //adjust two absolutely positioned rows Table.RowStyles[0].SizeType = SizeType.Percent; Table.RowStyles[0].Height = DesignerUtils.MINIMUMSTYLEPERCENT; Table.RowStyles[1].SizeType = SizeType.Percent; Table.RowStyles[1].Height = DesignerUtils.MINIMUMSTYLEPERCENT; } ////// /// Disposes of this object and all events and cached services. /// protected override void Dispose(bool disposing) { if (disposing) { IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); if (host != null) { host.TransactionClosing -= new DesignerTransactionCloseEventHandler(this.OnTransactionClosing); } if (undoEngine != null) { if (Undoing) { undoEngine.Undone -= new EventHandler(this.OnUndone); } undoEngine.Undoing -= new EventHandler(this.OnUndoing); } if (compSvc != null) { compSvc.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged); compSvc.ComponentChanging -= new ComponentChangingEventHandler(this.OnComponentChanging); } if (Table != null) { Table.ControlAdded -= new ControlEventHandler(this.OnControlAdded); Table.ControlRemoved -= new ControlEventHandler(this.OnControlRemoved); } rowStyleProp = null; colStyleProp = null; } base.Dispose(disposing); } ////// /// This draws a nice border around our panel and each cell only /// if the cell border styles are not set or 'none'. /// protected override void DrawBorder(Graphics graphics) { if (Table.CellBorderStyle != TableLayoutPanelCellBorderStyle.None) { //only draw a fake border if there is no borderstyle return; } base.DrawBorder(graphics); Rectangle rc = Control.DisplayRectangle; rc.Width --; rc.Height--; int[] cw = Table.GetColumnWidths(); int[] rh = Table.GetRowHeights(); using (Pen pen = BorderPen) { if (cw.Length > 1) { bool isRTL = (Table.RightToLeft == RightToLeft.Yes); // offset by padding int startX = isRTL ? rc.Right : rc.Left; for (int i = 0; i < cw.Length - 1; i++) { if (isRTL) { startX -= cw[i]; } else { startX += cw[i]; } graphics.DrawLine(pen, startX, rc.Top, startX, rc.Bottom); } } if (rh.Length > 1) { int startY = rc.Top; for (int i = 0; i < rh.Length - 1; i++) { startY += rh[i]; graphics.DrawLine(pen, rc.Left, startY, rc.Right, startY); } } } } //The StyleCollectionEditor uses these to make sure that the Styles collection //doesn't change underneath us internal void SuspendEnsureAvailableStyles() { ensureSuspendCount++; } //The StyleCollectionEditor uses these to make sure that the Styles collection //doesn't change underneath us internal void ResumeEnsureAvailableStyles(bool performEnsure) { if (ensureSuspendCount > 0) { ensureSuspendCount--; if (ensureSuspendCount == 0 && performEnsure) { EnsureAvailableStyles(); } } } ////// /// This is method ensures that for every row and column our /// runtime control has - that we have appropriate row and column /// styles for them. /// private bool EnsureAvailableStyles() { if (IsLoading || Undoing || ensureSuspendCount > 0) { return false; } int[] cw = Table.GetColumnWidths(); int[] rh = Table.GetRowHeights(); Table.SuspendLayout(); try { //if we have more columns then column styles add some... if (cw.Length > Table.ColumnStyles.Count) { int colDifference = cw.Length - Table.ColumnStyles.Count; PropChanging(rowStyleProp); for(int i = 0; i < colDifference; i++) { Table.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, DesignerUtils.MINIMUMSTYLESIZE)); } PropChanged(rowStyleProp); } //if we have more rows then row styles add some... if (rh.Length > Table.RowStyles.Count) { int rowDifference = rh.Length - Table.RowStyles.Count; PropChanging(colStyleProp); for(int i = 0; i < rowDifference; i++) { Table.RowStyles.Add(new RowStyle(SizeType.Absolute, DesignerUtils.MINIMUMSTYLESIZE)); } PropChanged(colStyleProp); } } finally { Table.ResumeLayout(); } return true; } ////// /// Takes a drageventargs and extracts the 0th dragging control. /// private Control ExtractControlFromDragEvent(DragEventArgs de) { DropSourceBehavior.BehaviorDataObject data = de.Data as DropSourceBehavior.BehaviorDataObject; if (data != null) { dragComps = new ArrayList(data.DragComponents); return dragComps[0] as Control; } return null; } ////// /// Called on drag-drop operations, give the point passed in, /// this method will attempt to find the appropriate cell. /// private Point GetCellPosition(Point pos) { //get some runtime table info int[] rows = Table.GetRowHeights(); int[] columns = Table.GetColumnWidths(); //By using DisplayRectangle here we handle the case where we are scrolled. VSWhidbey #399557 Point startingPoint = Table.PointToScreen(Table.DisplayRectangle.Location); Rectangle bounds = new Rectangle(startingPoint, Table.DisplayRectangle.Size); Point position = new Point(-1, -1); bool isRTL = Table.RightToLeft == RightToLeft.Yes; int offset = isRTL ? bounds.Right : bounds.X; //loop through the columns and identify where the mouse is for(int i = 0; i < columns.Length; i++) { if (isRTL) { if (pos.X >= offset - columns[i]) { position.X = i; break; } offset -= columns[i]; } else { if (pos.X <= offset + columns[i]) { position.X = i; break; } offset += columns[i]; } } if (position.X < 0) { //couldn't find it - then add it to the end position.X = columns.Length; } offset = bounds.Y; //loop through the rows and identify where the mouse is for(int i = 0; i < rows.Length; i++) { if (pos.Y <= offset + rows[i]) { position.Y = i; break; } offset += rows[i]; } if (position.Y < 0) { //couldn't find it - then add it to the end position.Y = rows.Length; } return position; } ////// /// Reverse lookup into our table. Given a control we'll find /// the location (colxrow) in the table. /// private Point GetControlPosition(Control control) { TableLayoutPanelCellPosition pos = Table.GetPositionFromControl(control); if ((pos.Row == -1) && (pos.Column == -1)) { return InvalidPoint; } return new Point (pos.Column, pos.Row); } ////// /// This method will add glyphs for every row and column in our Table. /// public override GlyphCollection GetGlyphs(GlyphSelectionType selectionType) { GlyphCollection glyphs = base.GetGlyphs(selectionType); PropertyDescriptor prop = TypeDescriptor.GetProperties(Component)["Locked"]; bool locked = (prop != null) ? ((bool)prop.GetValue(Component)) : false; //Before adding glyphs for every row/column, make sure we have a column/rowstyle for every column/row bool safeToRefresh = EnsureAvailableStyles(); //if we're somehow selected, not locked, and not inherited -then offer up glyphs for every //column/row line if (selectionType != GlyphSelectionType.NotSelected && !locked && InheritanceAttribute != InheritanceAttribute.InheritedReadOnly) { //get the correctly translated bounds //By using DisplayRectangle here we handle the case where we are scrolled. VSWhidbey #399689 Point loc = BehaviorService.MapAdornerWindowPoint(Table.Handle, Table.DisplayRectangle.Location); Rectangle bounds = new Rectangle(loc, Table.DisplayRectangle.Size); Point controlLoc = BehaviorService.ControlToAdornerWindow(Control); Rectangle checkBounds = new Rectangle(controlLoc, Control.ClientSize); // Can't use Control.Size since that will include any scrollbar int[] cw = Table.GetColumnWidths(); int[] rh = Table.GetRowHeights(); int halfSize = DesignerUtils.RESIZEGLYPHSIZE / 2; bool isRTL = (Table.RightToLeft == RightToLeft.Yes); int startLoc = isRTL ? bounds.Right : bounds.X; if (safeToRefresh) { //add resize glyphs for each column and row for (int i = 0; i < cw.Length - 1; i++) { //Do not add a glyph for columns of 0 width. This can happen for percentage columns, where the table is not //big enough for there to be any space for percentage columns if (cw[i] == 0) { continue; } if (isRTL) { startLoc -= cw[i]; } else { startLoc += cw[i];//x offset of column line } Rectangle gBounds = new Rectangle(startLoc - halfSize, checkBounds.Top, DesignerUtils.RESIZEGLYPHSIZE, checkBounds.Height); //Don't add glyphs for columns that are not within the clientrectangle if (!checkBounds.Contains(gBounds)) { continue; } Debug.Assert(Table.ColumnStyles[i] != null, "Table's ColumnStyle[" + i + "] is null!"); if (Table.ColumnStyles[i] != null) { TableLayoutPanelResizeGlyph g = new TableLayoutPanelResizeGlyph (gBounds, Table.ColumnStyles[i], Cursors.VSplit, Behavior); glyphs.Add(g); } } startLoc = bounds.Y;//reset for the rows... for (int i = 0; i < rh.Length - 1; i++) { //Do not add a glyph for rows of 0 height. This can happen for percentage columns, where the table is not //big enough for there to be any space for percentage columns if (rh[i] == 0) { continue; } startLoc += rh[i];//y offset of row line Rectangle gBounds = new Rectangle(checkBounds.Left, startLoc - halfSize, checkBounds.Width, DesignerUtils.RESIZEGLYPHSIZE); if (!checkBounds.Contains(gBounds)) { continue; } Debug.Assert(Table.RowStyles[i] != null, "Table's RowStyle[" + i + "] is null!"); if (Table.RowStyles[i] != null) { TableLayoutPanelResizeGlyph g = new TableLayoutPanelResizeGlyph (gBounds, Table.RowStyles[i], Cursors.HSplit, Behavior); glyphs.Add(g); } } } } return glyphs; } ////// /// Overrides the base and syncs load, change, and child added events. /// public override void Initialize(IComponent component) { base.Initialize(component); IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); if (host != null) { host.TransactionClosing += new DesignerTransactionCloseEventHandler(this.OnTransactionClosing); compSvc = host.GetService(typeof(IComponentChangeService)) as IComponentChangeService; } if (compSvc != null) { compSvc.ComponentChanging += new ComponentChangingEventHandler(this.OnComponentChanging); compSvc.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged); } Control.ControlAdded += new ControlEventHandler(this.OnControlAdded); Control.ControlRemoved += new ControlEventHandler(this.OnControlRemoved); rowStyleProp = TypeDescriptor.GetProperties(Table)["RowStyles"]; colStyleProp = TypeDescriptor.GetProperties(Table)["ColumnStyles"]; // VSWhidbey #424845. If the TLP 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); } } } // 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; } } ////// /// After a new comp is initialized - make sure our table has 2 rows and 2 columsn. /// public override void InitializeNewComponent(IDictionary defaultValues) { base.InitializeNewComponent(defaultValues); CreateEmptyTable(); } // We override this to find out if the table is full BEFORE the control is created. protected override IComponent[] CreateToolCore(ToolboxItem tool, int x, int y, int width, int height, bool hasLocation, bool hasSize) { rowCountBeforeAdd = Math.Max(0,Table.GetRowHeights().Length); // don't want negative colCountBeforeAdd = Math.Max(0,Table.GetColumnWidths().Length); return base.CreateToolCore(tool, x, y, width, height, hasLocation, hasSize); } ////// /// When a new child is added - we check to see if we have cached a cell pos. /// If so, we'll re-insert the control at this new position. /// private void OnControlAdded(object sender, ControlEventArgs e) { if (IsLoading || Undoing) { return; } // Calculate the number cells spanned by controls in the Table // This can be slow, but it is the only way to really calculate the number of cells spanned. // We cannot rely on checking the control's span since the TLP's growstyle might affect it. // E.g. RowCount = ColumnCount = 2, GrowStyle = AddRows, button in cell(0,0), button.ColumnSpan = 6 int totalArea = 0; int[] rows = Table.GetRowHeights(); int[] columns = Table.GetColumnWidths(); for (int row = 0; row < rows.Length; row++) { for (int column = 0; column < columns.Length; column++) { if (Table.GetControlFromPosition(column, row) != null) { ++totalArea; } } } //The control we are about to place, have already been added to the TLP's control collection, so -1 here. //This is because we want to know if the table was full BEFORE the control was added. bool fullTable = (totalArea - 1) >= (Math.Max(1, colCountBeforeAdd) * Math.Max(1, rowCountBeforeAdd)); if (droppedCellPosition == InvalidPoint) { droppedCellPosition = GetControlPosition(e.Control); } Debug.Assert(fullTable || (droppedCellPosition != InvalidPoint), "Why is neither fullTable or droppedCellPosition set?"); ControlAddedInternal(e.Control, droppedCellPosition, false, fullTable, null); droppedCellPosition = InvalidPoint; } private void OnControlRemoved(object sender, ControlEventArgs e) { // Need to do this to make sure undo/redo works // Since the Row/Col extended property is DesignerSerializationVisibility.Hidden, the undo engine // will not serialize the value out, so we need to reset it here. VSWhidbey #392705. if (e != null && e.Control != null) { Table.SetCellPosition(e.Control, new TableLayoutPanelCellPosition(-1, -1)); } } private bool IsOverValidCell() { Point dropPoint = GetCellPosition(Control.MousePosition); Control existingControl = (Control)((TableLayoutPanel)Control).GetControlFromPosition(dropPoint.X, dropPoint.Y); //If the cell is not empty, and we are not doing a local drag, then show the no-smoking cursor //or if we are doing a multi-select local drag, then show the no-smoking cursor. //or if we are doig a local drag, and the cell is not empty, and we are doing a copy if ((existingControl != null && localDragControl == null) || (localDragControl != null && dragComps.Count > 1) || (localDragControl != null && existingControl != null && Control.ModifierKeys == Keys.Control)) { return false; } return true; } protected override void OnContextMenu(int x, int y) { Point cell = GetCellPosition(new Point(x,y)); curRow = cell.Y; curCol = cell.X; //Set the SizeMode correctly EnsureAvailableStyles(); DesignerContextMenuStrip.Show(x, y); } ////// /// Fired when we get a drag enter - here, we'll check it out /// to see if we're dragging one of our child controls. /// protected override void OnDragEnter(DragEventArgs de) { base.OnDragEnter(de); //peak at what just entered her e- it it's a local control //we'll cache it off if (localDragControl == null) { Control dragControl = ExtractControlFromDragEvent(de); if (dragControl != null && Table.Controls.Contains(dragControl)) { localDragControl = dragControl; } } } ////// /// Called when a drag-drop operation leaves the control designer view /// /// protected override void OnDragLeave(EventArgs e) { localDragControl = null; //VSWhidbey #275678 dragComps = null; base.OnDragLeave(e); } ////// /// When a control is dropped we first determine which cell is related to the /// drop position. After this, if the control that was dropped is an existing /// child control of our Table - we remove and add it back at its new /// position. Otherwise, we'll do this in the OnChildAdded event. /// protected override void OnDragDrop(DragEventArgs de) { droppedCellPosition = GetCellPosition(Control.MousePosition); //the scenario where we just dropped our own child control if (localDragControl != null) { //local drag to our TLP - we need to re-insert or swap it... ControlAddedInternal(localDragControl, droppedCellPosition, true, false, de); localDragControl = null; } else { rowCountBeforeAdd = Math.Max(0,Table.GetRowHeights().Length); // don't want negative colCountBeforeAdd = Math.Max(0,Table.GetColumnWidths().Length); //If from the outside, just let the base class handle it base.OnDragDrop(de); // VSWhidbey #390230 // Devdiv Bugs 40804 // This will not fix VSWhidbey #390230 in the copy/paste scenario but it will for the // main drag/drop scneario. // We need to do this after the controls are added (after base.OnDragDrop above) // dragComps is null when dragging off the toolbox if (dragComps != null) { foreach (Control dragControl in dragComps) { if (dragControl != null) { PropertyDescriptor columnSpan = TypeDescriptor.GetProperties(dragControl)["ColumnSpan"]; PropertyDescriptor rowSpan = TypeDescriptor.GetProperties(dragControl)["RowSpan"]; if (columnSpan != null) { columnSpan.SetValue(dragControl, 1); } if (rowSpan != null) { rowSpan.SetValue(dragControl, 1); } } } } } droppedCellPosition = InvalidPoint; dragComps = null; } ////// /// If our table is full on drag enter -then we should not allow any /// drop operations on our surface. /// protected override void OnDragOver(DragEventArgs de) { // If we are not over a valid cell, then do not allow the drop if (!IsOverValidCell()) { de.Effect = DragDropEffects.None; return; } base.OnDragOver(de); } private DictionaryextenderProperties = null; private Dictionary ExtenderProperties { get { if (extenderProperties == null && Component != null) { extenderProperties = new Dictionary (); AttributeCollection attribs = TypeDescriptor.GetAttributes(Component.GetType()); foreach (Attribute a in attribs) { ProvidePropertyAttribute extender = a as ProvidePropertyAttribute; if (extender != null) { extenderProperties[extender.PropertyName] = true; } } } return extenderProperties; } } /// /// Checks if the given memberdescriptor represents an extender property offered /// by the TLP that affects layout and is not normally serialized (Row, Column, etc.) /// The UndoEngine will ignore these properties, so we need to force seralization. /// private bool DoesPropertyAffectPosition(MemberDescriptor member) { bool affectsPosition = false; DesignerSerializationVisibilityAttribute dsv = member.Attributes[typeof(DesignerSerializationVisibilityAttribute)] as DesignerSerializationVisibilityAttribute; if (dsv != null) { affectsPosition = dsv.Visibility == DesignerSerializationVisibility.Hidden && ExtenderProperties.ContainsKey(member.Name); } return affectsPosition; } private void OnComponentChanging(object sender, ComponentChangingEventArgs e) { Control changingControl = e.Component as Control; if (changingControl != null && changingControl.Parent == this.Component && e.Member != null && DoesPropertyAffectPosition(e.Member)) { PropertyDescriptor controlsProp = TypeDescriptor.GetProperties(Component)["Controls"]; compSvc.OnComponentChanging(Component, controlsProp); } } private void OnComponentChanged(object sender, ComponentChangedEventArgs e) { //VSWhidbey 233871 // //When the Row or Column property is being set on a control in the TLP, a Row/Col Style is not being added. //After the property is being set, the SelectionManager::OnSelectionChanged gets called. It in turn calls //GetGlyphs, and the TLP designer's GetGlyphs calls EnsureAvaiableStyles. Since no style was added, we will add //a default one of type Absolute, Height/Width 20. But but but... If the control has added its glyph before we //add the style, the glyphs wil be misaligned, since EnsureAvailableStyles also causes the TLP to do a layout. This //layout will actually size the control to a smaller size. So let's trap the Row/Col property changing, call //EnsureAvailableStyles, which will force the layout BEFORE the SelectionManager is called. if (e.Component != null) { Control c = e.Component as Control; if (c != null && c.Parent != null && c.Parent.Equals(Control) && e.Member != null && (e.Member.Name == "Row" || e.Member.Name == "Column")) { EnsureAvailableStyles(); } if (c != null && c.Parent == this.Component && e.Member != null && DoesPropertyAffectPosition(e.Member)) { PropertyDescriptor controlsProp = TypeDescriptor.GetProperties(Component)["Controls"]; compSvc.OnComponentChanged(Component, controlsProp, null, null); } } CheckVerbStatus(); } ////// /// Called when a designer transaction is closing (finishing). We sync this /// so that we can refresh our tablelayoutpanel control. Calling suspend and /// resume layout here will essentially invoke the internal 'onlayout' method /// on our control to force proper layout. /// private void OnTransactionClosing(object sender, DesignerTransactionCloseEventArgs e) { ISelectionService selSvc = GetService(typeof(ISelectionService)) as ISelectionService; if (selSvc != null && Table != null) { ICollection selectedComps = selSvc.GetSelectedComponents(); bool selectedComponentHasTableParent = false; foreach (object comp in selectedComps) { Control c = comp as Control; if (c != null && c.Parent == Table) { selectedComponentHasTableParent = true; break; } } if (selSvc.GetComponentSelected(Table) || selectedComponentHasTableParent) { //force an internal 'onlayout' event to refresh our control Table.SuspendLayout(); EnsureAvailableStyles(); Table.ResumeLayout(false); Table.PerformLayout(); } } } ////// /// Called before undo starts . We make sure to suspend designer /// operations during undo. /// private void OnUndoing(object sender, EventArgs e) { if (!Undoing) { if (undoEngine != null) { undoEngine.Undone += new EventHandler(this.OnUndone); } Undoing = true; } } ////// /// Called when undo ends. We'll make sure to resume /// unsink our Undone event and also refresh the designer. /// private void OnUndone(object sender, EventArgs e) { if (Undoing) { if (undoEngine != null) { undoEngine.Undone -= new EventHandler(this.OnUndone); } Undoing = false; bool isSafeToRefresh = EnsureAvailableStyles(); if (isSafeToRefresh) { Refresh(); } } } ////// /// If the user has drawn a rectangle with a toolbox item, /// we'll cache off the cell in which it happened so we can /// add it @ the right location in the event OnChildAdded. /// protected override void OnMouseDragBegin(int x, int y) { if (IsOverValidCell()) { //make sure we have a valid toolbox item and we're not just drawing a rect IToolboxService tbx = (IToolboxService)GetService(typeof(IToolboxService)); if (tbx != null && tbx.GetSelectedToolboxItem((IDesignerHost)GetService(typeof(IDesignerHost))) != null) { droppedCellPosition = GetCellPosition(Control.MousePosition); } } else { droppedCellPosition = InvalidPoint; Cursor.Current = Cursors.No; } base.OnMouseDragBegin(x, y); } protected override void OnMouseDragMove(int x, int y) { //If they are trying to draw in a cell that already has a control, then we //do not want to draw an outline if (droppedCellPosition == InvalidPoint) { Cursor.Current = Cursors.No; return; } base.OnMouseDragMove(x, y); } ////// /// If the user has drawn a rectangle with a toolbox item, /// we'll cache off the cell in which it happened so we can /// add it @ the right location in the event OnChildAdded. /// protected override void OnMouseDragEnd(bool cancel) { if (droppedCellPosition == InvalidPoint) { // If they are trying to draw in a cell that already has a control, then just act like a cancel cancel = true; } base.OnMouseDragEnd(cancel); } private void OnRowColMenuOpening(object sender, CancelEventArgs e) { e.Cancel = false; //Set the size mode correctly ToolStripDropDownMenu menu = sender as ToolStripDropDownMenu; if (menu != null) { int selCount = 0; ISelectionService selSvc = GetService(typeof(ISelectionService)) as ISelectionService; if (selSvc != null) { selCount = selSvc.SelectionCount; } // Always make sure and set the Enabled state in case the user // has changed the selection since the last time the menu was shown. bool enabled = (selCount == 1) && (InheritanceAttribute != InheritanceAttribute.InheritedReadOnly); menu.Items["add"].Enabled = enabled; menu.Items["insert"].Enabled = enabled; menu.Items["delete"].Enabled = enabled; menu.Items["sizemode"].Enabled = enabled; menu.Items["absolute"].Enabled = enabled; menu.Items["percent"].Enabled = enabled; menu.Items["autosize"].Enabled = enabled; if (selCount == 1) { ((ToolStripMenuItem)menu.Items["absolute"]).Checked = false; ((ToolStripMenuItem)menu.Items["percent"]).Checked = false; ((ToolStripMenuItem)menu.Items["autosize"]).Checked = false; bool isRow = (bool)menu.Tag; switch (isRow ? Table.RowStyles[curRow].SizeType : Table.ColumnStyles[curCol].SizeType) { case SizeType.Absolute: ((ToolStripMenuItem)menu.Items["absolute"]).Checked = true; break; case SizeType.Percent: ((ToolStripMenuItem)menu.Items["percent"]).Checked = true; break; case SizeType.AutoSize: ((ToolStripMenuItem)menu.Items["autosize"]).Checked = true; break; default: Debug.Fail("Unknown SizeType!"); break; } if ((isRow ? Table.RowCount : Table.ColumnCount) < 2) { //can't remove a row/column if we only have menu.Items["delete"].Enabled = false; } } } } ////// Adds a row/col to the end of the table /// private void OnAdd(bool isRow) { // get the property and add to it... IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null && Table.Site != null) { using(DesignerTransaction t = host.CreateTransaction(SR.GetString(isRow ? SR.TableLayoutPanelDesignerAddRowUndoUnit : SR.TableLayoutPanelDesignerAddColumnUndoUnit, Table.Site.Name))) { try { Table.SuspendLayout(); // To avoid flickering // This ensures that the Row/Col Style gets set BEFORE the row is added. This in turn // ensures that the row/col shows up. Since we turn off tablelayout, a style won't have been added // when EnsureVisibleStyles is called from the shadowed property. InsertRowCol(isRow, isRow ? Table.RowCount : Table.ColumnCount); Table.ResumeLayout(); t.Commit(); } catch (CheckoutException checkoutException) { if (CheckoutException.Canceled.Equals(checkoutException)) { if (t != null) { t.Cancel(); } } else { throw; } } } } } ////// Adds a row/col to the end of the table /// private void OnAddClick(object sender, EventArgs e) { //Tag = isRow OnAdd((bool)((ToolStripMenuItem)sender).Tag); } ////// Adds an row or column at the specified index. /// This method is also used by the StyleCollectionEditor, so any transaction must /// be managed by the caller. /// internal void InsertRowCol(bool isRow, int index) { // We shadow the ColumnCount/RowCount property, so let's add the style first // to make sure that the right style is added at the right location. try { if (isRow) { PropertyDescriptor rowProp = TypeDescriptor.GetProperties(Table)["RowCount"]; if (rowProp != null) { PropChanging(rowStyleProp); Table.RowStyles.Insert(index, new RowStyle(SizeType.Absolute, DesignerUtils.MINIMUMSTYLESIZE)); PropChanged(rowStyleProp); rowProp.SetValue(Table, Table.RowCount + 1); } } else { PropertyDescriptor colProp = TypeDescriptor.GetProperties(Table)["ColumnCount"]; if (colProp != null) { PropChanging(colStyleProp); Table.ColumnStyles.Insert(index, new ColumnStyle(SizeType.Absolute, DesignerUtils.MINIMUMSTYLESIZE)); PropChanged(colStyleProp); colProp.SetValue(Table, Table.ColumnCount + 1); } } } catch (System.InvalidOperationException ex) { IUIService uiService = (IUIService) GetService(typeof(IUIService)); uiService.ShowError(ex.Message); } // VSWhidbey # 490635 BehaviorService.Invalidate(BehaviorService.ControlRectInAdornerWindow(Control)); } ////// When we insert a row/col, we need to fix up any control that spans into the selected row/col to include the newly inserted row/col. /// We also need to move controls after the original row/col into the new row/col. /// index is the index of the row/col we just inserted /// Also used by the StyleCollectionEditor /// internal void FixUpControlsOnInsert(bool isRow, int index) { PropertyDescriptor childProp = TypeDescriptor.GetProperties(Table)["Controls"]; PropChanging(childProp); foreach (Control child in Table.Controls) { int currentIndex = isRow ? Table.GetRow(child) : Table.GetColumn(child); PropertyDescriptor prop = TypeDescriptor.GetProperties(child)[isRow ? "Row" : "Column"]; PropertyDescriptor spanProp = TypeDescriptor.GetProperties(child)[isRow ? "RowSpan" : "ColumnSpan"]; if (currentIndex == -1) { //this is a flow element. We don't really know where //this is going to go, so we cannot fix up anything. continue; } //push all controls >= the original row/col into the new row/col if (currentIndex >= index) { if (prop != null) { prop.SetValue(child, currentIndex + 1); } } else { //If the control is before the row/col we are inserting and the control has a span that includes the inserted row/col //the increase the span to include the insert row/col int span = isRow ? Table.GetRowSpan(child) : Table.GetColumnSpan(child); //span is always at least 1 if (currentIndex + span > index) { if (spanProp != null) { spanProp.SetValue(child, span + 1); } } } } PropChanged(childProp); } private void OnInsertClick(object sender, EventArgs e) { IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null && Table.Site != null) { bool isRow = (bool)((ToolStripMenuItem)sender).Tag; using(DesignerTransaction t = host.CreateTransaction(SR.GetString(isRow ? SR.TableLayoutPanelDesignerAddRowUndoUnit : SR.TableLayoutPanelDesignerAddColumnUndoUnit, Table.Site.Name))) { try { Table.SuspendLayout(); InsertRowCol(isRow, isRow ? curRow : curCol); FixUpControlsOnInsert(isRow, isRow ? curRow : curCol); Table.ResumeLayout(); t.Commit(); } catch (CheckoutException checkoutException) { if (CheckoutException.Canceled.Equals(checkoutException)) { if (t != null) { t.Cancel(); } } else { throw; } } catch (System.InvalidOperationException ex) { IUIService uiService = (IUIService) GetService(typeof(IUIService)); uiService.ShowError(ex.Message); } } } } /// /// When we remove a row/col, we need to fix up any control that spans into the selected row/col. /// We also need to adjust controls after the original row/col. /// index is the index of the row/col we are about to delete /// /// deleteList will hold a list of controls, which has the same index as the row/col, we are /// deleting. They are actually not deleted in this method because the method is also called, /// by the StyleCollectionEditor which does not want to delete controls right away. /// internal void FixUpControlsOnDelete(bool isRow, int index, ArrayList deleteList) { PropertyDescriptor childProp = TypeDescriptor.GetProperties(Table)["Controls"]; PropChanging(childProp); foreach (Control child in Table.Controls) { int currentIndex = isRow ? Table.GetRow(child) : Table.GetColumn(child); PropertyDescriptor prop = TypeDescriptor.GetProperties(child)[isRow ? "Row" : "Column"]; PropertyDescriptor spanProp = TypeDescriptor.GetProperties(child)[isRow ? "RowSpan" : "ColumnSpan"]; if (currentIndex == index) { //We add the deleteList.Contains check just to make extra sure. Could be //that the deleteList for some reason already contained the child. if (!deleteList.Contains(child)) { deleteList.Add(child); } continue; } if (currentIndex == -1 || deleteList.Contains(child)) { //If this is a flow element. We don't really know where this is going to go, so we cannot fix up anything. //If the child has already been marked for deletion, we can keep going continue; } Debug.Assert(currentIndex != index); //push all controls >= the original row/col into the new row/col, but only if (currentIndex > index) { if (prop != null) { prop.SetValue(child, currentIndex - 1); } } else { //If the control is before the row/col we are removing and the control has a span that includes the row/col //we are deleting, then decrease the span. int span = isRow ? Table.GetRowSpan(child) : Table.GetColumnSpan(child); //span is always at least 1 if (currentIndex + span > index) { if (spanProp != null) { //We've bled into the row/col, shrink up as expected spanProp.SetValue(child, span - 1); } } } } PropChanged(childProp); } // Also used by the StyleCollectionEditor internal void DeleteRowCol(bool isRow, int index) { if (isRow) { PropertyDescriptor rowProp = TypeDescriptor.GetProperties(Table)["RowCount"]; if (rowProp != null) { rowProp.SetValue(Table, Table.RowCount - 1); PropChanging(rowStyleProp); Table.RowStyles.RemoveAt(index); PropChanged(rowStyleProp); } } else { PropertyDescriptor colProp = TypeDescriptor.GetProperties(Table)["ColumnCount"]; if (colProp != null) { colProp.SetValue(Table, Table.ColumnCount - 1); PropChanging(colStyleProp); Table.ColumnStyles.RemoveAt(index); PropChanged(colStyleProp); } } } private void OnRemoveInternal(bool isRow, int index) { if ((isRow ? Table.RowCount : Table.ColumnCount) < 2) { //can't remove a row/column if we only have 1 return; } IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null && Table.Site != null) { using(DesignerTransaction t = host.CreateTransaction(SR.GetString(isRow ? SR.TableLayoutPanelDesignerRemoveRowUndoUnit : SR.TableLayoutPanelDesignerRemoveColumnUndoUnit, Table.Site.Name))) { try { Table.SuspendLayout(); ArrayList deleteList = new ArrayList(); //First fix up any controls in the row/col we are deleting FixUpControlsOnDelete(isRow, index, deleteList); //Then delete the row col DeleteRowCol(isRow, index); //Now delete any child control // IF YOU CHANGE THIS, YOU SHOULD ALSO CHANGE THE CODE IN StyleCollectionEditor.OnOkButtonClick if (deleteList.Count > 0) { PropertyDescriptor childProp = TypeDescriptor.GetProperties(Table)["Controls"]; PropChanging(childProp); foreach (object o in deleteList) { ArrayList al = new ArrayList(); DesignerUtils.GetAssociatedComponents((IComponent)o, host, al); foreach (IComponent comp in al) { compSvc.OnComponentChanging(comp, null); } host.DestroyComponent(o as Component); } PropChanged(childProp); } Table.ResumeLayout(); t.Commit(); } catch (CheckoutException checkoutException) { if (CheckoutException.Canceled.Equals(checkoutException)) { if (t != null) { t.Cancel(); } } else { throw; } } } } } ////// Always removes the last row/col /// private void OnRemove(bool isRow) { OnRemoveInternal(isRow, isRow ? Table.RowCount - 1 : Table.ColumnCount - 1); } ////// Removes a rol/col at a specific index. /// private void OnDeleteClick(object sender, EventArgs e) { try { bool isRow = (bool)((ToolStripMenuItem)sender).Tag; OnRemoveInternal(isRow, isRow ? curRow : curCol); } catch (System.InvalidOperationException ex) { IUIService uiService = (IUIService) GetService(typeof(IUIService)); uiService.ShowError(ex.Message); } } private void ChangeSizeType(bool isRow, SizeType newType) { TableLayoutStyleCollection styles = null; try { if (isRow) { styles = Table.RowStyles; } else { styles = Table.ColumnStyles; } int index = isRow ? curRow : curCol; if (styles[index].SizeType == newType) { // nuthin' to do return; } int[] rh = Table.GetRowHeights(); int[] ch = Table.GetColumnWidths(); if ((isRow && rh.Length < index - 1) || (!isRow && ch.Length < index - 1)) { //something got messed up Debug.Fail("Our indices are outta whack, how did that happen?"); return; } IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null && Table.Site != null) { using (DesignerTransaction t = host.CreateTransaction(SR.GetString(SR.TableLayoutPanelDesignerChangeSizeTypeUndoUnit, Table.Site.Name))) { try { Table.SuspendLayout(); PropChanging(isRow ? rowStyleProp : colStyleProp); switch (newType) { case SizeType.Absolute: styles[index].SizeType = SizeType.Absolute; if (isRow) { Table.RowStyles[index].Height = rh[index]; } else { Table.ColumnStyles[index].Width = ch[index]; } break; case SizeType.Percent: styles[index].SizeType = SizeType.Percent; if (isRow) { Table.RowStyles[index].Height = DesignerUtils.MINIMUMSTYLEPERCENT; } else { Table.ColumnStyles[index].Width = DesignerUtils.MINIMUMSTYLEPERCENT; } break; case SizeType.AutoSize: styles[index].SizeType = SizeType.AutoSize; break; default: Debug.Fail("Unknown SizeType!"); break; } PropChanged(isRow ? rowStyleProp : colStyleProp); Table.ResumeLayout(); t.Commit(); } catch (CheckoutException checkoutException) { if (CheckoutException.Canceled.Equals(checkoutException)) { if (t != null) { t.Cancel(); } } else { throw; } } } } } catch (System.InvalidOperationException ex) { IUIService uiService = (IUIService)GetService(typeof(IUIService)); uiService.ShowError(ex.Message); } } private void OnAbsoluteClick(object sender, EventArgs e) { ChangeSizeType((bool)((ToolStripMenuItem)sender).Tag, SizeType.Absolute); } private void OnPercentClick(object sender, EventArgs e) { ChangeSizeType((bool)((ToolStripMenuItem)sender).Tag, SizeType.Percent); } private void OnAutoSizeClick(object sender, EventArgs e) { ChangeSizeType((bool)((ToolStripMenuItem)sender).Tag, SizeType.AutoSize); } private void OnEdit() { try { EditorServiceContext.EditValue(this, Table, "ColumnStyles"); } catch (System.InvalidOperationException ex) { IUIService uiService = (IUIService) GetService(typeof(IUIService)); uiService.ShowError(ex.Message); } } ////// /// This is our callback for designer verbs: remove row and remove column. /// All we do here is increment or decrememnt out col/row count. /// private void OnVerbRemove(object sender, EventArgs e) { //sniff the text of the verb to see if we're adding columns or rows bool isRow = ((DesignerVerb)sender).Text.Equals(SR.GetString(SR.TableLayoutPanelDesignerRemoveRow)); OnRemove(isRow); } ////// /// This is our callback for designer verbs: add row and add column. /// private void OnVerbAdd(object sender, EventArgs e) { //sniff the text of the verb to see if we're adding columns or rows bool isRow = ((DesignerVerb)sender).Text.Equals(SR.GetString(SR.TableLayoutPanelDesignerAddRow)); OnAdd(isRow); } private void OnVerbEdit(object sender, EventArgs e) { OnEdit(); } ////// /// protected override void PreFilterProperties(IDictionary properties) { base.PreFilterProperties(properties); // Handle shadowed properties string[] shadowProps = new string[] { "ColumnStyles", "RowStyles", "ColumnCount", "RowCount" }; // VSWhidbey 491088 // To enable the PropertyGrid to work with the TableLayoutPanel at runtime (when no designer is available), // the above properties are marked browsable(false) and re-enabled when a designer is present. // Since so much of the logic for keeping the TLP in a valid Row/Column state is designer dependent, // these properties are not accessible by the PropertyGrid without a designer. Attribute[] attribs = new Attribute[] { new BrowsableAttribute(true) }; for (int i = 0; i < shadowProps.Length; i++) { PropertyDescriptor prop = (PropertyDescriptor)properties[shadowProps[i]]; if (prop != null) { properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(TableLayoutPanelDesigner), prop, attribs); } } // replace this one seperately because it is of a different type (DesignerTableLayoutControlCollection) than // the original property (TableLayoutControlCollection) // PropertyDescriptor controlsProp = (PropertyDescriptor)properties["Controls"]; if (controlsProp != null) { Attribute[] attrs = new Attribute[controlsProp.Attributes.Count]; controlsProp.Attributes.CopyTo(attrs, 0); properties["Controls"] = TypeDescriptor.CreateProperty(typeof(TableLayoutPanelDesigner), "Controls", typeof(DesignerTableLayoutControlCollection), attrs); } } ////// /// Called after we modify the table - sync the selection and repaint. /// private void Refresh() { //refresh selection, glyphs, and adorners BehaviorService.SyncSelection(); if (Table != null) { Table.Invalidate(true); } } private void PropChanging(PropertyDescriptor prop) { if (compSvc != null && prop != null) { compSvc.OnComponentChanging(Table, prop); } } private void PropChanged(PropertyDescriptor prop) { if (compSvc != null && prop != null) { compSvc.OnComponentChanged(Table, prop, null, null); } } [ListBindable(false)] [DesignerSerializer(typeof(DesignerTableLayoutControlCollectionCodeDomSerializer), typeof(CodeDomSerializer))] internal class DesignerTableLayoutControlCollection : TableLayoutControlCollection, IList { TableLayoutControlCollection realCollection; public DesignerTableLayoutControlCollection(TableLayoutPanel owner) : base(owner) { this.realCollection = owner.Controls; } ////// /// Retrieves the number of child controls. /// public override int Count { get { return realCollection.Count; } } ////// object ICollection.SyncRoot { get { return this; } } /// /// bool ICollection.IsSynchronized { get { return false; } } /// /// bool IList.IsFixedSize { get { return false; } } /// /// /// public new bool IsReadOnly { get { return realCollection.IsReadOnly; } } ///[To be supplied.] ////// int IList.Add(object control) { return ((IList)realCollection).Add(control); } public override void Add(Control c) { realCollection.Add(c); } /// /// /// public override void AddRange(Control[] controls) { realCollection.AddRange(controls); } ///[To be supplied.] ////// bool IList.Contains(object control) { return ((IList)realCollection).Contains(control); } public new void CopyTo(Array dest, int index) { realCollection.CopyTo(dest, index); } /// /// public override bool Equals(object other) { return realCollection.Equals(other); } public new IEnumerator GetEnumerator() { return realCollection.GetEnumerator(); } /// /// public override int GetHashCode() { return realCollection.GetHashCode(); } /// /// int IList.IndexOf(object control) { return ((IList)realCollection).IndexOf(control); } /// /// void IList.Insert(int index, object value) { ((IList)realCollection).Insert(index, value); } /// /// void IList.Remove(object control) { ((IList)realCollection).Remove(control); } /// /// void IList.RemoveAt(int index) { ((IList)realCollection).RemoveAt(index); } /// /// object IList.this[int index] { get { return ((IList)realCollection)[index]; } set { throw new NotSupportedException(); } } public override void Add(Control control, int column, int row) { realCollection.Add(control, column, row); } public override int GetChildIndex(Control child, bool throwException) { return realCollection.GetChildIndex(child,throwException); } // we also need to redirect this guy public override void SetChildIndex(Control child, int newIndex) { realCollection.SetChildIndex(child, newIndex); } /// /// /// public override void Clear() { // only remove the sited non-inherited components // for (int i = realCollection.Count - 1; i >= 0; i--) { if (realCollection[i] != null && realCollection[i].Site != null && TypeDescriptor.GetAttributes(realCollection[i]).Contains(InheritanceAttribute.NotInherited)) { realCollection.RemoveAt(i); } } } } // Custom code dom serializer for the DesignerControlCollection. We need this so we can filter out controls // that aren't sited in the host's container. internal class DesignerTableLayoutControlCollectionCodeDomSerializer : TableLayoutControlCollectionCodeDomSerializer { protected override object SerializeCollection(IDesignerSerializationManager manager, CodeExpression targetExpression, Type targetType, ICollection originalCollection, ICollection valuesToSerialize) { ArrayList subset = new ArrayList(); if (valuesToSerialize != null && valuesToSerialize.Count > 0) { foreach (object val in valuesToSerialize) { IComponent comp = val as IComponent; if (comp != null && comp.Site != null && !(comp.Site is INestedSite)) { subset.Add(comp); } } } return base.SerializeCollection(manager, targetExpression, targetType, originalCollection, subset); } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.[To be supplied.] ///
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- WebFaultException.cs
- ActivityExecutor.cs
- TreeWalker.cs
- ParameterModifier.cs
- PlatformCulture.cs
- MergeFilterQuery.cs
- XmlSchemaParticle.cs
- MDIControlStrip.cs
- FloatSumAggregationOperator.cs
- SimpleTypeResolver.cs
- IPEndPointCollection.cs
- OleAutBinder.cs
- DataBindingHandlerAttribute.cs
- _ReceiveMessageOverlappedAsyncResult.cs
- NativeMethods.cs
- NonParentingControl.cs
- ConstraintConverter.cs
- Pointer.cs
- KeyNameIdentifierClause.cs
- StartUpEventArgs.cs
- ExceptionUtil.cs
- TextTreeUndoUnit.cs
- FontUnitConverter.cs
- XmlSortKeyAccumulator.cs
- Page.cs
- QuadTree.cs
- FeatureAttribute.cs
- ResXDataNode.cs
- CodeMethodReturnStatement.cs
- VerticalAlignConverter.cs
- Attributes.cs
- SettingsSection.cs
- XmlCharType.cs
- ColorInterpolationModeValidation.cs
- Identifier.cs
- Function.cs
- HtmlHead.cs
- ZipFileInfoCollection.cs
- HttpCacheVaryByContentEncodings.cs
- OrderedDictionary.cs
- RewritingProcessor.cs
- PeerCustomResolverElement.cs
- OutOfProcStateClientManager.cs
- VisualStyleElement.cs
- CodeSnippetStatement.cs
- NamedObject.cs
- SwitchLevelAttribute.cs
- ConfigurationLocation.cs
- ConfigurationStrings.cs
- FileVersionInfo.cs
- ListViewGroupItemCollection.cs
- RightsManagementSuppressedStream.cs
- CacheEntry.cs
- Message.cs
- IndentTextWriter.cs
- sqlnorm.cs
- ToolBarOverflowPanel.cs
- Tokenizer.cs
- ListViewSortEventArgs.cs
- _FtpControlStream.cs
- QueryComponents.cs
- RtfNavigator.cs
- StrokeFIndices.cs
- XmlSchemaAttributeGroup.cs
- EntityTypeEmitter.cs
- ComponentEditorForm.cs
- SmiRequestExecutor.cs
- EdmItemCollection.cs
- QuotedPrintableStream.cs
- Transform.cs
- ServerValidateEventArgs.cs
- ObjectStateEntryBaseUpdatableDataRecord.cs
- StreamAsIStream.cs
- EventListener.cs
- WaveHeader.cs
- CompiledXpathExpr.cs
- TrailingSpaceComparer.cs
- AdapterUtil.cs
- ItemAutomationPeer.cs
- UIElement.cs
- Rfc4050KeyFormatter.cs
- NotificationContext.cs
- ProcessHostFactoryHelper.cs
- ModuleConfigurationInfo.cs
- TrustLevel.cs
- XmlBinaryReader.cs
- LayoutEvent.cs
- HttpProtocolImporter.cs
- GraphicsContext.cs
- PriorityRange.cs
- ImageFormat.cs
- VisualStates.cs
- PersonalizableAttribute.cs
- TimeIntervalCollection.cs
- TextAutomationPeer.cs
- HttpWriter.cs
- GroupBoxAutomationPeer.cs
- ValidateNames.cs
- FileSecurity.cs
- Rotation3DAnimationBase.cs