/// <summary> /// Handles special key bindings for key down events. Base class handles space and tab. /// </summary> /// <param name="row">Index into this branch</param> /// <param name="column">Column index</param> /// <param name="key">Character typed by the user</param> /// <param name="modifiers">Any modifiers keys currently held down</param> /// <returns>ProcessKeyResult structure that indicates what action should be taken (if any)</returns> protected virtual ProcessKeyResult ProcessKeyDown(int row, int column, KeyEventArgs e) { var result = new ProcessKeyResult(); result.Action = KeyAction.Process; result.Direction = (e.Modifiers != Keys.Shift) ? NavigationDirection.Right : NavigationDirection.Left; switch (e.KeyCode) { case Keys.F2: result.Action = KeyAction.Handle; result.StartLabelEdit = true; result.Direction = NavigationDirection.Down; break; case Keys.Tab: result.Action = KeyAction.Handle; result.ColumnType = null; break; case Keys.Space: if (_columns[column].ColumnIsCheckBox || InLabelEdit) { // check box and text box columns should use default // key processing for the space bar (toggle value) result.Action = KeyAction.Process; } break; case Keys.Up: // let Ctrl-Up fall through to the base control processing. if (!e.Control) { result.Action = KeyAction.Handle; result.Local = false; result.Direction = NavigationDirection.Up; } break; case Keys.Down: // let Ctrl-Down fall through to the base control processing. if (!e.Control) { result.Action = KeyAction.Handle; result.Local = false; result.Direction = NavigationDirection.Down; // Alt + Down should open a drop down if (e.Alt) { result.StartLabelEdit = true; } } break; case Keys.Delete: result.Action = KeyAction.Handle; result.Delete = true; break; } return(result); }
/// <summary> /// retrieves the column index given a type. Assumes there /// will only be a single instance of each type in the columns array. /// returns -1 if the column is not found /// </summary> private int FindCurrentColumnOfType(ProcessKeyResult result) { var index = -1; int i; for (i = 0; i < _currentColumns.Length; i++) { var columnType = _currentColumns[i].GetType(); if (result.ColumnType == null || columnType == result.ColumnType || columnType.IsSubclassOf(result.ColumnType)) { index = i; break; } } return(index); }
/// <summary> /// Handles special key bindings for key down events. Base class handles space and tab. /// </summary> /// <param name="row">Index into this branch</param> /// <param name="column">Column index</param> /// <param name="key">Character typed by the user</param> /// <param name="modifiers">Any modifiers keys currently held down</param> /// <returns>ProcessKeyResult structure that indicates what action should be taken (if any)</returns> protected virtual ProcessKeyResult ProcessKeyDown(int row, int column, KeyEventArgs e) { var result = new ProcessKeyResult(); result.Action = KeyAction.Process; result.Direction = (e.Modifiers != Keys.Shift) ? NavigationDirection.Right : NavigationDirection.Left; switch (e.KeyCode) { case Keys.F2: result.Action = KeyAction.Handle; result.StartLabelEdit = true; result.Direction = NavigationDirection.Down; break; case Keys.Tab: result.Action = KeyAction.Handle; result.ColumnType = null; break; case Keys.Space: if (_columns[column].ColumnIsCheckBox || InLabelEdit) { // check box and text box columns should use default // key processing for the space bar (toggle value) result.Action = KeyAction.Process; } break; case Keys.Up: // let Ctrl-Up fall through to the base control processing. if (!e.Control) { result.Action = KeyAction.Handle; result.Local = false; result.Direction = NavigationDirection.Up; } break; case Keys.Down: // let Ctrl-Down fall through to the base control processing. if (!e.Control) { result.Action = KeyAction.Handle; result.Local = false; result.Direction = NavigationDirection.Down; // Alt + Down should open a drop down if (e.Alt) { result.StartLabelEdit = true; } } break; case Keys.Delete: result.Action = KeyAction.Handle; result.Delete = true; break; } return result; }
/// <summary> /// Process key down events. /// </summary> public ProcessKeyResult ProcessKeyDown(int row, int column, KeyEventArgs e) { var result = new ProcessKeyResult(KeyAction.Process); if (e != null) { switch (e.KeyCode) { case Keys.Tab: result.Action = KeyAction.Handle; result.Direction = !e.Shift ? NavigationDirection.Right : NavigationDirection.Left; result.Local = false; return result; case Keys.Space: if (_columns[column].ColumnIsCheckBox) { // if we're on a check box, use standard processing return new ProcessKeyResult(KeyAction.Process); } // special case for only one checkbox in the row, // it should be toggled no matter where the focus is. var checkBoxIndex = -1; for (var i = 0; i < _columns.Length; i++) { if (_columns[i].ColumnIsCheckBox && (GetCheckBoxValue(row, i) != CheckBoxState.Unsupported)) { if (checkBoxIndex == -1) { checkBoxIndex = i; } else { // more than one checkbox, use standard processing return new ProcessKeyResult(KeyAction.Process); } } if (checkBoxIndex != -1) { ((IBranch)this).ToggleState(row, checkBoxIndex); // need to refresh both ourselves and our children. Do we need BranchModificationAction.ToggleState? if (_onBranchModification != null) { _onBranchModification( this, BranchModificationEventArgs.DisplayDataChanged( new DisplayDataChangedData( VirtualTreeDisplayDataChanges.StateImage, this, row, checkBoxIndex, 1))); _onBranchModification( this, BranchModificationEventArgs.DisplayDataChanged( new DisplayDataChangedData( VirtualTreeDisplayDataChanges.StateImage, _childBranchArray[row].Branch, -1, checkBoxIndex, -1))); } return new ProcessKeyResult(KeyAction.Discard); // we've handled this, no further processing necessary } } break; case Keys.Up: // let Ctrl-Up fall through to the base control processing. if (!e.Control) { result.Action = KeyAction.Handle; result.Local = false; result.Direction = NavigationDirection.Up; } break; case Keys.Down: // let Ctrl-Down fall through to the base control processing. if (!e.Control) { result.Action = KeyAction.Handle; result.Local = false; result.Direction = NavigationDirection.Down; } break; } } return result; }
/// <summary> /// Process key down events. /// </summary> public ProcessKeyResult ProcessKeyDown(int row, int column, KeyEventArgs e) { var result = new ProcessKeyResult(KeyAction.Process); if (e != null) { switch (e.KeyCode) { case Keys.Tab: result.Action = KeyAction.Handle; result.Direction = !e.Shift ? NavigationDirection.Right : NavigationDirection.Left; result.Local = false; return(result); case Keys.Space: if (_columns[column].ColumnIsCheckBox) { // if we're on a check box, use standard processing return(new ProcessKeyResult(KeyAction.Process)); } // special case for only one checkbox in the row, // it should be toggled no matter where the focus is. var checkBoxIndex = -1; for (var i = 0; i < _columns.Length; i++) { if (_columns[i].ColumnIsCheckBox && (GetCheckBoxValue(row, i) != CheckBoxState.Unsupported)) { if (checkBoxIndex == -1) { checkBoxIndex = i; } else { // more than one checkbox, use standard processing return(new ProcessKeyResult(KeyAction.Process)); } } if (checkBoxIndex != -1) { ((IBranch)this).ToggleState(row, checkBoxIndex); // need to refresh both ourselves and our children. Do we need BranchModificationAction.ToggleState? if (_onBranchModification != null) { _onBranchModification( this, BranchModificationEventArgs.DisplayDataChanged( new DisplayDataChangedData( VirtualTreeDisplayDataChanges.StateImage, this, row, checkBoxIndex, 1))); _onBranchModification( this, BranchModificationEventArgs.DisplayDataChanged( new DisplayDataChangedData( VirtualTreeDisplayDataChanges.StateImage, _childBranchArray[row].Branch, -1, checkBoxIndex, -1))); } return(new ProcessKeyResult(KeyAction.Discard)); // we've handled this, no further processing necessary } } break; case Keys.Up: // let Ctrl-Up fall through to the base control processing. if (!e.Control) { result.Action = KeyAction.Handle; result.Local = false; result.Direction = NavigationDirection.Up; } break; case Keys.Down: // let Ctrl-Down fall through to the base control processing. if (!e.Control) { result.Action = KeyAction.Handle; result.Local = false; result.Direction = NavigationDirection.Down; } break; } } return(result); }
private bool ProcessKey(ProcessKeyResult result, ITreeGridDesignerBranch branch, int absIndex, int relIndex, int column) { var actionOccurred = false; var foundEditableColumn = false; var inLabelEdit = InLabelEdit; if (result.Action == KeyAction.Discard) { return true; } if (result.Action == KeyAction.Handle) { var treeDirection = TranslateNavigationDirection(result.Direction); if (result.StartLabelEdit) { // key should just put us in edit mode InLabelEdit = true; // Alt + Down case - open drop down if (result.Direction == NavigationDirection.Down) { var dropDown = LabelEditControl as TypeEditorHost; if (dropDown != null) { dropDown.OpenDropDown(); } } return true; // currently, we don't allow combining this with other options } if (result.Delete) { branch.Delete(relIndex, column); // don't restore edit mode if we deleted something inLabelEdit = false; } BatchDrawItem = true; if (inLabelEdit) { var tridDesignerBranch = branch as TreeGridDesignerBranch; if (tridDesignerBranch != null && relIndex >= tridDesignerBranch.ElementCount) { // creator node edit. unless the user has actually typed some text here, // we treat this as non-edit mode (i.e, don't restore edit mode after navigation) inLabelEdit = LabelEditControl.Text.Length > 0; } } try { InLabelEdit = false; // the branch may block deactivation of the edit control, // because of an incorrect entry, for example. In that case, // we should just get out. if (InLabelEdit) { return false; } // expand branch first, if necesary if (result.ExpandBranch) { // using column = 0 because TreeGrid designer // doesn't support sub-item expansions. if (!Tree.IsExpanded(absIndex, 0)) { // the branch is requesting an expansion, but we can't do it. // just get out and leave things the way they are. if (!Tree.IsExpandable(absIndex, 0)) { return false; } Tree.ToggleExpansion(absIndex, 0); actionOccurred = true; } } var branchType = branch.GetType(); if (result.ColumnType != null) { // limit search to a particular column var newColumn = FindCurrentColumnOfType(result); Debug.Assert(newColumn != -1, "Couldn't find column of type: " + result.ColumnType); if ((treeDirection == TreeNavigation.RightColumn && column < newColumn) || (treeDirection == TreeNavigation.LeftColumn && column > newColumn)) { // in this case, we're done as long as the branch is of the appropriate type and supports navigation or is expandable, because we have // the correct row/column indices. column = newColumn; foundEditableColumn = (result.BranchType == null || branchType == result.BranchType || branchType.IsSubclassOf(result.BranchType)) && ((branch.GetValueSupported(relIndex, column) & TreeGridDesignerValueSupportedStates.SupportsKeyboardNavigation) != 0 || (branch as IBranch).IsExpandable(relIndex, column)); } if (!foundEditableColumn) { // need to do additional search, translate to an up or down search in this particular column column = newColumn; treeDirection = treeDirection == TreeNavigation.RightColumn ? TreeNavigation.Down : TreeNavigation.Up; } } if (result.Delete) { // we are already focused on an editable column InvalidateItem(CurrentIndex); actionOccurred = true; } else { // search for next matching row/column int oldAbsIndex; while (!foundEditableColumn) { oldAbsIndex = absIndex; if (treeDirection == TreeNavigation.LeftColumn && column == 0) { absIndex--; if (absIndex >= 0) { var info = Tree.GetItemInfo(absIndex, 0, false); column = GetLastColumnIndex(info.Branch, info.Row); } } else if (treeDirection == TreeNavigation.RightColumn && column == GetLastColumnIndex((IBranch)branch, relIndex)) { absIndex++; column = 0; } else if (result.ColumnType == null) { // search is not restricted to a particular column, so we translate up/down to // left/right search to give a better experience. if (treeDirection == TreeNavigation.Up) { absIndex--; treeDirection = TreeNavigation.LeftColumn; if (absIndex >= 0) { // handle jagged column cases var info = Tree.GetItemInfo(absIndex, 0, false); if (info.Branch != null) { column = GetLastColumnIndex(info.Branch, info.Row); } } } else if (treeDirection == TreeNavigation.Down) { absIndex++; treeDirection = TreeNavigation.RightColumn; column = 0; } } if (absIndex < 0 || absIndex >= Tree.VisibleItemCount) { break; } var coordinate = new VirtualTreeCoordinate(absIndex, column); if (absIndex == oldAbsIndex) { // if the above didn't result in any navigation, ask the tree to do it itself. coordinate = Tree.GetNavigationTarget(treeDirection, absIndex, column, ColumnPermutation); } if (coordinate.IsValid) { absIndex = coordinate.Row; column = coordinate.Column; if (oldAbsIndex != absIndex) { // we've transitioned to a new row, retrieve new row data from the tree. var info = Tree.GetItemInfo(absIndex, 0, false); if (result.Local && branch != null && branch != info.Branch) { // stop search if we shouldn't go past current branch break; } branch = info.Branch as ITreeGridDesignerBranch; branchType = branch.GetType(); relIndex = info.Row; } // allow focus on expandable cells or cells that support navigation that are of the appropriate branch type. if (branch != null && (result.BranchType == null || branchType == result.BranchType || branchType.IsSubclassOf(result.BranchType)) && ((branch.GetValueSupported(relIndex, column) & TreeGridDesignerValueSupportedStates.SupportsKeyboardNavigation) != 0 || (branch as IBranch).IsExpandable(relIndex, column))) { foundEditableColumn = true; break; } } else { break; } } } if (foundEditableColumn) { var currentIndex = CurrentIndex; if (absIndex != currentIndex) { if (currentIndex != -1) { // currentIndex may be -1 if we toggled expansion, // but in that case the base control will take care // of the redraw // TODO : is this a bug in the control? shouldn't selection // be restored after toggling expansion? InvalidateItem(currentIndex); } CurrentIndex = absIndex; actionOccurred = true; } if (column != CurrentColumn) { CurrentColumn = column; actionOccurred = true; } } InLabelEdit = inLabelEdit; } finally { BatchDrawItem = false; } } return actionOccurred; }
/// <summary> /// retrieves the column index given a type. Assumes there /// will only be a single instance of each type in the columns array. /// returns -1 if the column is not found /// </summary> private int FindCurrentColumnOfType(ProcessKeyResult result) { var index = -1; int i; for (i = 0; i < _currentColumns.Length; i++) { var columnType = _currentColumns[i].GetType(); if (result.ColumnType == null || columnType == result.ColumnType || columnType.IsSubclassOf(result.ColumnType)) { index = i; break; } } return index; }
private bool ProcessKey(ProcessKeyResult result, ITreeGridDesignerBranch branch, int absIndex, int relIndex, int column) { var actionOccurred = false; var foundEditableColumn = false; var inLabelEdit = InLabelEdit; if (result.Action == KeyAction.Discard) { return(true); } if (result.Action == KeyAction.Handle) { var treeDirection = TranslateNavigationDirection(result.Direction); if (result.StartLabelEdit) { // key should just put us in edit mode InLabelEdit = true; // Alt + Down case - open drop down if (result.Direction == NavigationDirection.Down) { var dropDown = LabelEditControl as TypeEditorHost; if (dropDown != null) { dropDown.OpenDropDown(); } } return(true); // currently, we don't allow combining this with other options } if (result.Delete) { branch.Delete(relIndex, column); // don't restore edit mode if we deleted something inLabelEdit = false; } BatchDrawItem = true; if (inLabelEdit) { var tridDesignerBranch = branch as TreeGridDesignerBranch; if (tridDesignerBranch != null && relIndex >= tridDesignerBranch.ElementCount) { // creator node edit. unless the user has actually typed some text here, // we treat this as non-edit mode (i.e, don't restore edit mode after navigation) inLabelEdit = LabelEditControl.Text.Length > 0; } } try { InLabelEdit = false; // the branch may block deactivation of the edit control, // because of an incorrect entry, for example. In that case, // we should just get out. if (InLabelEdit) { return(false); } // expand branch first, if necesary if (result.ExpandBranch) { // using column = 0 because TreeGrid designer // doesn't support sub-item expansions. if (!Tree.IsExpanded(absIndex, 0)) { // the branch is requesting an expansion, but we can't do it. // just get out and leave things the way they are. if (!Tree.IsExpandable(absIndex, 0)) { return(false); } Tree.ToggleExpansion(absIndex, 0); actionOccurred = true; } } var branchType = branch.GetType(); if (result.ColumnType != null) { // limit search to a particular column var newColumn = FindCurrentColumnOfType(result); Debug.Assert(newColumn != -1, "Couldn't find column of type: " + result.ColumnType); if ((treeDirection == TreeNavigation.RightColumn && column < newColumn) || (treeDirection == TreeNavigation.LeftColumn && column > newColumn)) { // in this case, we're done as long as the branch is of the appropriate type and supports navigation or is expandable, because we have // the correct row/column indices. column = newColumn; foundEditableColumn = (result.BranchType == null || branchType == result.BranchType || branchType.IsSubclassOf(result.BranchType)) && ((branch.GetValueSupported(relIndex, column) & TreeGridDesignerValueSupportedStates.SupportsKeyboardNavigation) != 0 || (branch as IBranch).IsExpandable(relIndex, column)); } if (!foundEditableColumn) { // need to do additional search, translate to an up or down search in this particular column column = newColumn; treeDirection = treeDirection == TreeNavigation.RightColumn ? TreeNavigation.Down : TreeNavigation.Up; } } if (result.Delete) { // we are already focused on an editable column InvalidateItem(CurrentIndex); actionOccurred = true; } else { // search for next matching row/column int oldAbsIndex; while (!foundEditableColumn) { oldAbsIndex = absIndex; if (treeDirection == TreeNavigation.LeftColumn && column == 0) { absIndex--; if (absIndex >= 0) { var info = Tree.GetItemInfo(absIndex, 0, false); column = GetLastColumnIndex(info.Branch, info.Row); } } else if (treeDirection == TreeNavigation.RightColumn && column == GetLastColumnIndex((IBranch)branch, relIndex)) { absIndex++; column = 0; } else if (result.ColumnType == null) { // search is not restricted to a particular column, so we translate up/down to // left/right search to give a better experience. if (treeDirection == TreeNavigation.Up) { absIndex--; treeDirection = TreeNavigation.LeftColumn; if (absIndex >= 0) { // handle jagged column cases var info = Tree.GetItemInfo(absIndex, 0, false); if (info.Branch != null) { column = GetLastColumnIndex(info.Branch, info.Row); } } } else if (treeDirection == TreeNavigation.Down) { absIndex++; treeDirection = TreeNavigation.RightColumn; column = 0; } } if (absIndex < 0 || absIndex >= Tree.VisibleItemCount) { break; } var coordinate = new VirtualTreeCoordinate(absIndex, column); if (absIndex == oldAbsIndex) { // if the above didn't result in any navigation, ask the tree to do it itself. coordinate = Tree.GetNavigationTarget(treeDirection, absIndex, column, ColumnPermutation); } if (coordinate.IsValid) { absIndex = coordinate.Row; column = coordinate.Column; if (oldAbsIndex != absIndex) { // we've transitioned to a new row, retrieve new row data from the tree. var info = Tree.GetItemInfo(absIndex, 0, false); if (result.Local && branch != null && branch != info.Branch) { // stop search if we shouldn't go past current branch break; } branch = info.Branch as ITreeGridDesignerBranch; branchType = branch.GetType(); relIndex = info.Row; } // allow focus on expandable cells or cells that support navigation that are of the appropriate branch type. if (branch != null && (result.BranchType == null || branchType == result.BranchType || branchType.IsSubclassOf(result.BranchType)) && ((branch.GetValueSupported(relIndex, column) & TreeGridDesignerValueSupportedStates.SupportsKeyboardNavigation) != 0 || (branch as IBranch).IsExpandable(relIndex, column))) { foundEditableColumn = true; break; } } else { break; } } } if (foundEditableColumn) { var currentIndex = CurrentIndex; if (absIndex != currentIndex) { if (currentIndex != -1) { // currentIndex may be -1 if we toggled expansion, // but in that case the base control will take care // of the redraw // TODO : is this a bug in the control? shouldn't selection // be restored after toggling expansion? InvalidateItem(currentIndex); } CurrentIndex = absIndex; actionOccurred = true; } if (column != CurrentColumn) { CurrentColumn = column; actionOccurred = true; } } InLabelEdit = inLabelEdit; } finally { BatchDrawItem = false; } } return(actionOccurred); }