// apply column information to a grid // this will set the column positions, caption, format, and visibility // (any columns not included in the 'columns' array will not be displayed private void SetupColumns(C1FlexDataTree grid, string[] columns) { // initialize column position int position = grid.Cols.Fixed; // apply column information foreach (string s in columns) { // split column info (name, caption, format) string[] columnInfo = s.Split(','); // move column to proper position based on its name Column column = grid.Cols[columnInfo[0].Trim()]; column.Move(position); position++; // apply caption if (columnInfo.Length > 1) { column.Caption = columnInfo[1].Trim(); } // apply format if (columnInfo.Length > 2) { column.Format = columnInfo[2].Trim(); } } // hide all other columns for (int i = position; i < grid.Cols.Count; i++) { grid.Cols[i].Visible = false; } }
// update position of child grids when size changes // // when the grid is resized, move child grids so they // stay in their proper position. // override protected void OnSizeChanged(EventArgs e) { // always call base implementation base.OnSizeChanged(e); // if this is the top-level grid, update position of child grids C1FlexDataTree parent = Parent as C1FlexDataTree; if (parent == null) { UpdateChildren(); } }
// customize grid display to show selected columns, captions, formats, and data maps private void _flex_SetupColumns(object sender, System.EventArgs e) { // get grid that was just bound C1FlexDataTree grid = sender as C1FlexDataTree; if (grid == null) { return; } // get data source name ITypedList itl = grid.DataSource as ITypedList; if (itl == null) { return; } string tableName = itl.GetListName(null); // look up table in data set // (the table has formatting information in its ExtendedProperties) DataTable dt = _ds.Tables[tableName]; // apply custom column order, captions, format string[] columns = dt.ExtendedProperties["ShowColumns"] as string[]; if (columns != null) { SetupColumns(grid, columns); } // apply custom data maps foreach (Column gridColumn in grid.Cols) { DataColumn dataColumn = dt.Columns[gridColumn.Name]; if (dataColumn == null) { continue; } gridColumn.DataMap = dataColumn.ExtendedProperties["DataMap"] as IDictionary; if (gridColumn.DataMap != null) { gridColumn.TextAlign = TextAlignEnum.LeftCenter; } } // all done, auto size to show mapped data grid.AutoSizeCols(1, grid.Cols.Count - 1, 12); }
private void ApplyParentStyles(C1FlexDataTree parent) { if (parent != null) { ShowCursor = parent.ShowCursor; AllowAddNew = parent.AllowAddNew; AllowDelete = parent.AllowDelete; BorderStyle = parent.BorderStyle; Styles.ParseString(parent.Styles.BuildString(true)); foreach (GlyphEnum g in Enum.GetValues(typeof(GlyphEnum))) { Glyphs[g] = parent.Glyphs[g]; } Cols[0].Width = parent.Cols[0].WidthDisplay; } }
// update position of this child grid within its parent. // this is called by the UpdateChildren method above and also // when child grids are created. private void UpdatePosition() { // sanity C1FlexDataTree parent = Parent as C1FlexDataTree; Row parentRow = Tag as Row; if (parent == null || parentRow == null) { return; } // get cell rectangle int row = parentRow.Index; Rectangle rc = parent.GetCellRectDisplay(row, 0); // calculate child location and client size rc.X = rc.Right; rc.Y = rc.Bottom; rc.Width = Cols[Cols.Count - 1].Right; rc.Height = Rows[Rows.Count - 1].Bottom; // make sure child grid width doesn't extend past parent client width int maxRight = parent.ClientSize.Width - 2; if (rc.Right > maxRight) { rc.Width = maxRight - rc.X; } // update size/position if (Location != rc.Location) { Location = rc.Location; } if (ClientSize != rc.Size) { ClientSize = rc.Size; } // update height of container row var updateRowIndex = row + 1; if (parent.Rows.Count > updateRowIndex) { parent.Rows[updateRowIndex].Height = Height; } }
// update size/position of all child grids and of this grid within this parent. // this is called when the grid scrolls, when it's size changes, and when // rows or columns are added, removed, or resized. private void UpdateChildren() { try { // update position of all children for (int row = 0; row < Rows.Count; row++) { C1FlexDataTree child = Rows[row].UserData as C1FlexDataTree; if (child != null) { child.UpdatePosition(); } } } catch { } // and update position of this grid within its parent UpdatePosition(); }
// keep track of column with child information (if any) // // when the grid receives a Refresh notification from the DataSource, // scan the columns looking for subordinate tables, and keep track of // the column so we can draw the collapse/expand icons and bind the // child grids to the subordinate tables. // // note: this implementation keeps only one detail column per table. // if the master table contains several detail columns, the grid will // only show the first subordinate column. e.g. a Client table with // ClientOrders and ClientDetails would expand to show ClientOrders // only. // override protected void OnDataRefresh(ListChangedEventArgs e) { // always call base implementation base.OnDataRefresh(e); // only want the Reset event if (e.ListChangedType != ListChangedType.Reset) { return; } // clear any existing children for (int i = 0; i < Controls.Count; i++) { C1FlexDataTree childGrid = Controls[i] as C1FlexDataTree; if (childGrid != null) { Controls.Remove(childGrid); i--; } } // keep track of hierarchical column _colChild = null; foreach (Column col in Cols) { Type type = col.DataType; if (type != null && typeof(IList).IsAssignableFrom(type) && type != typeof(byte[])) { _colChild = col; break; } } // fire event to allow customization of master/child grids ParentGrid.OnSetupColumns(this); }
// expand/collapse child grid // // if the user clicks the icons (drawn in the method above), either // create a child grid (and bind it to the detail records), or remove // the existing one. // override protected void OnBeforeMouseDown(BeforeMouseDownEventArgs e) { // always call base implementation base.OnBeforeMouseDown(e); // check that it's the Left button and that we have a hierarchy if (e.Button != MouseButtons.Left || _colChild == null) { return; } // check that it's a row header cell HitTestInfo hit = HitTest(e.X, e.Y); if (hit.Type != HitTestTypeEnum.RowHeader || hit.Row < Rows.Fixed) { return; } // check that the click was over the collapse/expand icon if (e.X < Cols[0].Right - (Glyphs[GlyphEnum.Collapsed].Width + 4) || _colChild[hit.Row] == null) { return; } // all checks OK, cancel click before proceeding e.Cancel = true; Select(hit.Row, Cols.Fixed, hit.Row, Cols.Count - 1, false); // create and show child grid if (Rows[hit.Row].UserData == null) { // create child grid C1FlexDataTree childGrid = new C1FlexDataTree(); childGrid.Visible = false; childGrid.ScrollBars = ScrollBars.Horizontal; // attach child grid to parent, set data source Controls.Add(childGrid); childGrid.ApplyParentStyles(this); childGrid.DataSource = _colChild[hit.Row]; // save references: // child grid Tag property contains a reference to the parent row // parent row UserData contains a reference to the child grid childGrid.Tag = Rows[hit.Row]; Rows[hit.Row].UserData = childGrid; // add node row (unbound) to display child Rows.InsertNode(hit.Row + 1, -1); // move child grid into position, make it visible childGrid.UpdatePosition(); childGrid.Visible = true; childGrid.Focus(); } else // hide and delete child grid { // get child grid C1FlexDataTree childGrid = (C1FlexDataTree)Rows[hit.Row].UserData; // break references Rows[hit.Row].UserData = null; childGrid.Tag = null; // clear child and remove it from parent childGrid.Controls.Clear(); Controls.Remove(childGrid); // delete container row var removeRowIndex = hit.Row + 1; if (Rows.Count > removeRowIndex) { Rows.Remove(removeRowIndex); } } }