/// <summary> /// This is in response to focused cell change because of Keyboard navigation. /// </summary> private void FocusCellChanged() { this.selectedRanges.Clear(); this.anchorPoint = this.ReportGrid.CurrentReportCell.Rect.Center(); this.currentSelectedCell = this.ReportGrid.CurrentReportCell; }
/// <summary> /// Handles the mouse button being pressed. /// </summary> /// <param name="e">The event arguments.</param> protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e) { // This state variable will control how the 'Mouse Move' and 'Mouse Up' event handlers interpret the user action. The // 'selectedColumn' field is used as the starting point for any drag-and-drop type of operation. this.mouseState = MouseState.ButtonDown; // Evaluate the state of the keyboard. Key combinations involving the shift and control keys will alter the areas that // are selected. bool isShiftKeyPressed = ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift); bool isControlKeyPressed = ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control); // The mouse selected with the mouse button is used as an anchor point unless the shift key is pressed. The anchor // point allows for ranges of columns to be selected. Everything between the anchor and the currently selected column // will be selected when the shift key is down. This is modeled after the Excel extended range selection keys. Point mouseDownLocation = e.GetPosition(this); // Do not clear the select cells in the Report Grid if the right mouse button is pressed. if (e.RightButton != MouseButtonState.Pressed) { //Since the user is selecting a cell, it will invalidate any row selection that there may be. foreach (List <ReportRow> block in this.ReportGrid.SelectedRowBlocks) { foreach (ReportRow row in block) { foreach (ReportCell cell in row.Cells) { cell.IsSelected = false; } } } this.ReportGrid.SelectedRowBlocks.Clear(); this.ReportGrid.SelectedRowHeaderBlocks.Clear(); } if (!isShiftKeyPressed) { IInputElement iInputElement = this.InputHitTest(mouseDownLocation); DependencyObject dependencyObject = iInputElement as DependencyObject; while (dependencyObject != null) { ReportCell reportCell = DynamicReport.GetCell(dependencyObject); if (reportCell != null) { Keyboard.Focus(dependencyObject as IInputElement); currentSelectedCell = reportCell; break; } dependencyObject = VisualTreeHelper.GetParent(dependencyObject); } this.anchorPoint = mouseDownLocation; } // The shift and control key extend the selection operation in the same way as Microsoft Excel. if (isShiftKeyPressed || isControlKeyPressed) { // When the shift key is pressed during column selection, every column between the last column selected // and the current column is selected. if (isShiftKeyPressed) { // This is an ordered rectangle that encompasses all the selected cells. Rect selectedRectangle = GetSelectionRectangle(mouseDownLocation); SetSelectedRectangle(selectedRectangle); } // When the control key is pressed a single column is added to the range of columns selected. if (isControlKeyPressed) { // A point marks the range when the control key is pressed. This is almost identical to pressing the mouse // button except the previous ranges are not cleared. This is part of the extended selection algorithm that is // modeled after Excel. Rect selectedRange = new Rect(mouseDownLocation, new Size(0, 0)); // This removes any previous instance of this column in the selection. foreach (Rect existingRange in this.selectedRanges) { if (selectedRange == existingRange) { this.selectedRanges.Remove(existingRange); break; } } // The new range becomes the starting point for any further extended selection operations. this.selectedRanges.Add(selectedRange); } // This instructs the event handlers how the mouse movement is to be interpreted. this.mouseState = MouseState.Selecting; } else { // Evaluate if we need to be able to determine the when we right click within the currently selected range then do not clear the selectedRanges. if (!((e.RightButton == MouseButtonState.Pressed) && (CurrentSelectedCell != null) && (CurrentSelectedCell.IsSelected))) //MIGHT try this --> if (reportCell.Rect.IntersectsWith(selectedRange)) { // Clear the selected ranges as we do not have a right mouse button pressed with the currently Selected ranges. // A simple selection that doesn't involve the modifier keys will clear out any previously selected ranges. this.selectedRanges.Clear(); // The column is added at the start of a new range of selected columns. this.selectedRanges.Add(new Rect(mouseDownLocation, new Size(0, 0))); } } // Evaluate if we need to be able to determine the when we right click within the currently selected range then do not clear the selectedRanges. if (!((e.RightButton == MouseButtonState.Pressed) && (CurrentSelectedCell != null) && (CurrentSelectedCell.IsSelected))) //MIGHT try this --> if (reportCell.Rect.IntersectsWith(selectedRange)) { // This will select all the columns in the selected ranges of columns and remove the selection from all the rest of the cells. SelectCells(); } }
/// <summary> /// Create the event arguments. /// </summary> /// <param name="row">The row the cell is in.</param> /// <param name="cell">The cell itself.</param> /// <param name="property">The property that changed.</param> public ReportCellPropertyChangedEventArgs(ReportRow row, ReportCell cell, String property) { this.row = row; this.cell = cell; this.property = property; }
/// <summary> /// Handles the mouse button being pressed. /// </summary> /// <param name="e">The event arguments.</param> protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e) { // This gets the location of the mouse in document coordinates. Mouse.Capture(this); // This state variable will control how the 'Mouse Move' and 'Mouse Up' event handlers interpret the user action. The // 'selectedColumn' field is used as the starting point for any drag-and-drop type of operation. this.mouseState = MouseState.ButtonDown; // Evaluate the state of the keyboard. Key combinations involving the shift and control keys will alter the areas that // are selected. bool isShiftKeyPressed = ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift); bool isControlKeyPressed = ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control); // The mouse selected with the mouse button is used as an anchor point unless the shift key is pressed. The anchor // point allows for ranges of columns to be selected. Everything between the anchor and the currently selected column // will be selected when the shift key is down. This is modeled after the Excel extended range selection keys. this.mouseDownLocation = e.GetPosition(this); if (!isShiftKeyPressed) { this.anchorPoint = this.mouseDownLocation; } // The mouse indicates which column has been selected and the anchor indicates the starting point of the selection in // an extended selection operation. ReportColumn selectedColumn = this.ReportGrid.Columns.FindColumnAt(this.mouseDownLocation.X); this.anchorColumn = this.ReportGrid.Columns.FindColumnAt(this.anchorPoint.X); // Every cell that appears in the header canvas is considered part of the selectable header. This will collect all the // selected cells in a list while creating a rectangle that is the union of all those selected cells. this.headerCells.Clear(); foreach (ReportRow reportRow in this.ReportGrid.Rows) { ReportCell reportCell = reportRow[this.anchorColumn]; if (reportRow.Top < this.ActualHeight) { this.headerCells.Add(reportCell); } } // Every cell that appears in the header canvas is considered part of the selectable header. This will collect all the // selected cells in a list while creating a rectangle that is the union of all those selected cells. Rect selectedRect = this.headerCells[0].Rect; for (int index = 1; index < this.headerCells.Count; index++) { selectedRect = Rect.Union(selectedRect, this.headerCells[index].Rect); } // If a column is selected then the position and movement of the mouse will suggest one of several gestures that need // to be interpreted: is a column being moved, is it being resized, is it being deleted or selected? The code below // will begin to interpret the input gesture. if (selectedColumn != null) { // The header has two modes: when the headers are frozen, only selection operations are enabled with the mouse. // When not frozen, the columns can be moved, resized, resorted and removed. if (this.IsHeaderFrozen) { // The shift and control key extend the selection operation in the same way as Microsoft Excel. if (isShiftKeyPressed || isControlKeyPressed) { // When the shift key is pressed during column selection, every column between the last column selected // and the current column is selected. if (isShiftKeyPressed) { // In the unlikely event that the shift key was down during the setting of the anchor point, this will // create a dummy entry in the list of selected column ranges. if (this.selectedRanges.Count == 0) { List <ReportColumn> reportColumns = new List <ReportColumn>(); reportColumns.Add(selectedColumn); this.selectedRanges.Add(reportColumns); } // The most recent range will be replaced with a new range when the mouse is dragged around the column // headers. This has the effect of clearing the columns that are no longer selected and selecting only // the columns between the anchor and the currently selected column. List <ReportColumn> lastRange = this.selectedRanges[this.selectedRanges.Count - 1]; int firstIndex = this.ReportGrid.Columns.IndexOf(this.anchorColumn); int secondIndex = this.ReportGrid.Columns.IndexOf(selectedColumn); int startIndex = firstIndex < secondIndex ? firstIndex : secondIndex; int endIndex = firstIndex > secondIndex ? firstIndex : secondIndex; // This will replace the last range in the list with the new range selected from the current position // and the anchor position. lastRange.Clear(); for (int index = startIndex; index <= endIndex; index++) { lastRange.Add(this.ReportGrid.Columns[index] as ReportColumn); } } // When the control key is pressed a single column is added to the range of columns selected. if (isControlKeyPressed) { // This removes any previous instance of this column in the selection. foreach (List <ReportColumn> columnRange in this.selectedRanges) { if (columnRange.Contains(selectedColumn)) { columnRange.Remove(selectedColumn); } } // The column is added (or re-added) at the start of the range of selected columns. List <ReportColumn> reportColumns = new List <ReportColumn>(); reportColumns.Add(selectedColumn); this.selectedRanges.Add(reportColumns); } } else { // A simple selection that doesn't involve the modifier keys will clear out any previously selected ranges. selectedRanges.Clear(); // The column is added at the start of a new range of selected columns. List <ReportColumn> reportColumns = new List <ReportColumn>(); reportColumns.Add(selectedColumn); this.selectedRanges.Add(reportColumns); } // This will select all the columns in the selected ranges of columns and remove the selection from all the // rest of the cells. SelectCells(); // This instructs the event handlers how the mouse movement is to be interpreted. this.mouseState = MouseState.Selecting; } else { // The left mouse button can either select the column or begin a resizing operation. This will perform a 'Hit // Test' to see which operation should be performed. if (e.LeftButton == MouseButtonState.Pressed) { // This is a 'Hit Test' for the right edge of the column header tile to see if the user is trying to // change the size of the column. If the mouse is close to the right edge, then the drag operation to // change the size of the tile is begun. if (selectedColumn.Right - DynamicReport.splitBorder <= this.mouseDownLocation.X && this.mouseDownLocation.X < selectedColumn.Right) { this.resizeStart = selectedColumn.Right; this.mouseState = MouseState.ResizingColumn; this.destinationColumn = null; } else { // This is a 'Hit Test' for the left edge of the column header tile to see if the user is trying to // change the size of the column. Note that because the left edge really belongs to the previous // column header when resizing, that the previous column is selected for the operation. if (selectedColumn.Left <= this.mouseDownLocation.X && this.mouseDownLocation.X < selectedColumn.Left + DynamicReport.splitBorder) { this.resizeStart = selectedColumn.Left; this.mouseState = MouseState.ResizingColumn; int index = this.ReportGrid.Columns.IndexOf(selectedColumn); if (index != 0) { this.anchorColumn = this.ReportGrid.Columns[index - 1] as ReportColumn; this.destinationColumn = null; } } } } // At this point, a resizing operation has been selected from the input gesture of the mouse. if (this.mouseState == MouseState.ResizingColumn) { // The parent window will watch for this event to tell it how to draw the column width indicator lines. The // dimension and location of those lines are outside of this window and must be handled by the parent. if (this.ResizeMouseMove != null) { this.ResizeMouseMove(this, new ResizeColumnEventArgs(this.anchorColumn, this.anchorColumn.Width, false)); } // This window provides quantitative feedback for the new width of the column. The offsets were arrived at // empirically from reverse engineering Excel. this.columnWidthPopup.HorizontalOffset = this.mouseDownLocation.X - 2.0; this.columnWidthPopup.Content = selectedColumn.Width; this.columnWidthPopup.IsOpen = true; } else { // This will select the button momentarily for drag-and-drop and sorting operations. foreach (ReportCell reportCell in this.headerCells) { reportCell.IsSelected = true; } } } } }
/// <summary> /// Handles the mouse button being pressed. /// </summary> /// <param name="e">The event arguments.</param> protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e) { // The sort order is maintained in this structure. this.sortOrder = new List <SortItem>(); // This gets the location of the mouse in document coordinates. Mouse.Capture(this); // This state variable will control how the 'Mouse Move' and 'Mouse Up' event handlers interpret the user action. The // 'selectedRow' field is used as the starting point for any drag-and-drop type of operation. this.mouseState = MouseState.ButtonDown; // Evaluate the state of the keyboard. Key combinations involving the shift and control keys will alter the areas that // are selected. bool isShiftKeyPressed = ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift); bool isControlKeyPressed = ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control); // This will use the current position for an anchor unless the shift key is pressed. this.mouseDownLocation = e.GetPosition(this); if (!isShiftKeyPressed) { this.anchorPoint = this.mouseDownLocation; } // The mouse indicates which column has been selected and the anchor indicates the starting point of the selection in // an extended selection operation. ReportRow selectedRow = this.ReportGrid.Rows.FindRowAt(this.mouseDownLocation.Y); ReportRow anchorRow = this.ReportGrid.Rows.FindRowAt(this.anchorPoint.Y); Point mouseDownLocation = e.GetPosition(this); if (!isShiftKeyPressed) { IInputElement iInputElement = this.InputHitTest(mouseDownLocation); DependencyObject dependencyObject = iInputElement as DependencyObject; while (dependencyObject != null) { if (DynamicReport.GetCell(dependencyObject) != null) { Keyboard.Focus(dependencyObject as IInputElement); break; } dependencyObject = VisualTreeHelper.GetParent(dependencyObject); } } // Every cell that appears in the header canvas is considered part of the selectable header. This will collect all the // selected cells in a list while creating a rectangle that is the union of all those selected cells. this.headerCells.Clear(); foreach (ReportColumn reportColumn in this.ReportGrid.Columns) { ReportCell reportCell = selectedRow[reportColumn]; if (reportColumn.Left < this.ActualWidth) { this.headerCells.Add(reportCell); } } // If a row is selected then the position and movement of the mouse will suggest one of several gestures that need // to be interpreted: is a row being moved, is it being resized, is it being deleted or selected? The code below // will begin to interpret the input gesture. if (selectedRow != null) { // The header has two modes: when the headers are frozen, only selection operations are enabled with the mouse. // When not frozen, the rows can be moved, resized, resorted and removed. if (this.IsHeaderFrozen) { // The shift and control key extend the selection operation in the same way as Microsoft Excel. if (isShiftKeyPressed || isControlKeyPressed) { // When the shift key is pressed during row selection, every row between the last row selected // and the current row is selected. if (isShiftKeyPressed) { // In the unlikely event that the shift key was down during the setting of the anchor point, this will // create a dummy entry in the list of selected row ranges. if (this.selectedRanges.Count == 0) { List <ReportRow> reportRows = new List <ReportRow>(); reportRows.Add(selectedRow); this.selectedRanges.Add(reportRows); } // The most recent range will be replaced with a new range when the mouse is dragged around the row // headers. This has the effect of clearing the rows that are no longer selected and selecting only // the rows between the anchor and the currently selected row. List <ReportRow> lastRange = this.selectedRanges[this.selectedRanges.Count - 1]; // This will select each row between the last selected row and the one just selected. Note that the // list of rows is distinct, so duplicate rows are ignored. ReportRow firstRow = anchorRow.Top < selectedRow.Top ? anchorRow : selectedRow; ReportRow secondRow = anchorRow.Top < selectedRow.Top ? selectedRow : anchorRow; lastRange.Clear(); foreach (ReportRow reportRow in this.ReportGrid.Rows) { if (firstRow.Top <= reportRow.Top && reportRow.Top <= secondRow.Top) { lastRange.Add(reportRow); } } } // When the control key is pressed a single row is added to the range of rows selected. if (isControlKeyPressed) { // This removes any previous instance of this row in the selection. foreach (List <ReportRow> rowRange in this.selectedRanges) { if (rowRange.Contains(selectedRow)) { rowRange.Remove(selectedRow); } } // The row is added (or re-added) at the start of the range of selected rows. List <ReportRow> reportRows = new List <ReportRow>(); reportRows.Add(selectedRow); this.selectedRanges.Add(reportRows); } } else { // The row is added at the start of a new range of selected rows. List <ReportRow> reportRows = new List <ReportRow>(); // The if condition below fixes issue that allows the right click to not clear the selected rows. // Hence it allows every other condition to clear the state. if (e.RightButton != MouseButtonState.Pressed) { // A simple selection that doesn't involve the modifier keys will clear out any previously selected ranges. this.selectedRanges.Clear(); } else { // This removes any previous instance of this row in the selection. foreach (List <ReportRow> rowRange in this.selectedRanges) { if (rowRange.Contains(selectedRow)) { rowRange.Remove(selectedRow); } } } reportRows.Add(selectedRow); this.selectedRanges.Add(reportRows); } // This will select all the rows in the selected ranges of rows and remove the selection from all the // rest of the cells. SelectRows(); // This instructs the event handlers how the mouse movement is to be interpreted. this.mouseState = MouseState.Selecting; } else { // The top mouse button can either select the row or begin a resizing operation. This will perform a 'Hit // Test' to see which operation should be performed. if (e.LeftButton == MouseButtonState.Pressed) { // This is a 'Hit Test' for the bottom edge of the row header tile to see if the user is trying to change // the size of the row. If the mouse is close to the bottom edge, then the drag operation to change the // size of the tile is begun. if (selectedRow.Bottom - DynamicReport.splitBorder <= this.mouseDownLocation.Y && this.mouseDownLocation.Y < selectedRow.Bottom) { this.resizeStart = selectedRow.Bottom; this.mouseState = MouseState.ResizingRow; this.destinationRow = null; } else { // This is a 'Hit Test' for the top edge of the row header tile to see if the user is trying to change // the size of the row. Note that because the top edge really belongs to the previous row header when // resizing, that the previous row is selected for the operation. if (selectedRow.Top <= this.mouseDownLocation.Y && this.mouseDownLocation.Y < selectedRow.Top + DynamicReport.splitBorder) { this.resizeStart = selectedRow.Top; this.mouseState = MouseState.ResizingRow; foreach (ReportRow reportRow in this.ReportGrid.Rows) { if (reportRow.Bottom == selectedRow.Top) { selectedRow = reportRow; } } this.destinationRow = null; } } } // At this point, a resizing operation has been selected from the input gesture of the mouse. if (this.mouseState == MouseState.ResizingRow) { // The parent window will watch for this event to tell it how to draw the row width indicator lines. The // dimension and location of those lines are outside of this window and must be handled by the parent. if (this.ResizeMouseMove != null) { this.ResizeMouseMove(this, new ResizeRowEventArgs(selectedRow, selectedRow.Height, false)); } // This window provides quantitative feedback for the new width of the row. The offsets were arrived at // empirically from reverse engineering Excel. this.rowHeightPopup.VerticalOffset = this.mouseDownLocation.Y - 2.0; this.rowHeightPopup.Content = selectedRow.Height; this.rowHeightPopup.IsOpen = true; } else { // This will select the button momentarily for drag-and-drop and sorting operations. foreach (ReportCell reportCell in this.headerCells) { reportCell.IsSelected = true; } } } } }