private void SetCurrentExtendedMultiSelectIndex( int newCaret, bool extendFromAnchor, bool preserveSelection, ModifySelectionAction selectCaretAction, bool select) { if (!GetStyleFlag(VTCStyleFlags.ExtendedMultiSelect) || !IsHandleCreated) { return; } // We fire off a lot of messages in this routine. Go ahead and use DefWindowProc to // bypass all of the extra handling done in the various subclassing routines (WinForms and us) var hWnd = Handle; var itemCount = (int)NativeMethods.SendMessage(hWnd, NativeMethods.LB_GETCOUNT, 0, 0); // Get the starting caret var startCaret = CaretIndex; var caretInitiallySelected = IsSelected(newCaret); // Get the initial anchor position var anchor = newCaret; if (extendFromAnchor) { anchor = AnchorIndex; if (anchor < 0) { extendFromAnchor = false; anchor = newCaret; } } if ((anchor == newCaret && !preserveSelection) || startCaret == newCaret) { SetSelected(newCaret, true); } else { // We're moving toward the anchor (the first two cases here), then the // remaining items are generally cleared with the !preverseSelection code // below, but we need to clear between the old and new carets even if we're // preserving selection elsewhere or things look really weird. if (newCaret >= anchor && startCaret > newCaret && startCaret > anchor) { if (preserveSelection && extendFromAnchor) { SetSelectionRange(newCaret + 1, startCaret, false); } } else if (newCaret <= anchor && startCaret < newCaret && startCaret < anchor) { if (preserveSelection && extendFromAnchor) { SetSelectionRange(startCaret, newCaret - 1, false); } } else { // Select all items between the old and the new carets, inclusive. Not all of these // items will be selectable items for multicolumn trees with blank expansions. However, // these items will be filtered when the selected items are enumerated. We have to handle // this state regardless because blocking the listbox from reaching this state when the // user selects items with the mouse is extremely difficult (basically impossible). We // could block here, but it is not worth creating the ColumnItemEnumerator. SetSelectionRange(Math.Min(newCaret, startCaret), Math.Max(newCaret, startCaret), select); } } if (!preserveSelection) { var keepSelTop = Math.Min(anchor, newCaret); var keepSelBottom = Math.Max(anchor, newCaret); // Use selitemrangeex with the lowest number last to clear if (keepSelTop > 0) { SetSelectionRange(0, keepSelTop - 1, false); } if (keepSelBottom < itemCount - 1) { SetSelectionRange(keepSelBottom + 1, itemCount - 1, false); } } switch (selectCaretAction) { case ModifySelectionAction.Clear: if (IsSelected(newCaret)) { SetSelected(newCaret, false); } else if (!caretInitiallySelected) { selectCaretAction = ModifySelectionAction.None; // nothing happened to the caret } break; case ModifySelectionAction.Select: if (!IsSelected(newCaret)) { SetSelected(newCaret, true); } else if (caretInitiallySelected) { selectCaretAction = ModifySelectionAction.None; // nothing happened to the caret } break; case ModifySelectionAction.Toggle: if (caretInitiallySelected) { goto case ModifySelectionAction.Clear; } else { goto case ModifySelectionAction.Select; } } // Some of the calls above will move the anchor index. Back sure it's where we want it. if (anchor != AnchorIndex) { AnchorIndex = anchor; } CaretIndex = newCaret; DoSelectionChanged(); FireWinEventsForSelection(extendFromAnchor, preserveSelection, selectCaretAction); }
/// <summary> /// Fires WinEvents for given selection change. /// </summary> private void FireWinEventsForSelection(bool extendFromAnchor, bool preserveSelection, ModifySelectionAction caretAction) { if (GetStateFlag(VTCStateFlags.RestoringSelection)) { return; // no events if we're restoring selection. } if (SelectionCount == 1) { // if there's currently only one thing selected, we know we should fire a regular selection event. if (VirtualTreeAccEvents.ShouldNotify(VirtualTreeAccEvents.eventObjectSelection, this)) { VirtualTreeAccEvents.Notify( VirtualTreeAccEvents.eventObjectSelection, CurrentIndex, CurrentColumn, this); } } else if (extendFromAnchor) { if (VirtualTreeAccEvents.ShouldNotify(VirtualTreeAccEvents.eventObjectSelectionWithin, this)) { // we extended selection from the anchor position, this constitutes a significant change in selection // state, so we fire selection within from the tree control, and let clients query us for the result. // UNDONE: querying currently won't work because we need a way to return an IEnumVariant from IAccessible::get_accSelection. // We just fire selection for the caret instead. // VirtualTreeAccEvents.Notify(VirtualTreeAccEvents.eventObjectSelectionWithin, VirtualTreeConstant.NullIndex, // VirtualTreeConstant.NullIndex, this); VirtualTreeAccEvents.Notify( VirtualTreeAccEvents.eventObjectSelection, CurrentIndex, CurrentColumn, this); } } else if (preserveSelection) { // we're preserving an original selection, which means that we fire either an add or a remove, // depending on what happened to the caret switch (caretAction) { case ModifySelectionAction.Select: if (VirtualTreeAccEvents.ShouldNotify(VirtualTreeAccEvents.eventObjectSelectionAdd, this)) { VirtualTreeAccEvents.Notify( VirtualTreeAccEvents.eventObjectSelectionAdd, CurrentIndex, CurrentColumn, this); } break; case ModifySelectionAction.Clear: if (VirtualTreeAccEvents.ShouldNotify(VirtualTreeAccEvents.eventObjectSelectionRemove, this)) { VirtualTreeAccEvents.Notify( VirtualTreeAccEvents.eventObjectSelectionRemove, CurrentIndex, CurrentColumn, this); } break; case ModifySelectionAction.Toggle: if (IsSelected(CaretIndex)) { goto case ModifySelectionAction.Select; } else { goto case ModifySelectionAction.Clear; } case ModifySelectionAction.None: // Caret position didn't change, but selection still changed overall. This happens, for instance, // during cross-column selection transfer. We would ideally fire selection within here, for now // we just fire selection at the caret position anyway. if (VirtualTreeAccEvents.ShouldNotify(VirtualTreeAccEvents.eventObjectSelectionWithin, this)) { //VirtualTreeAccEvents.Notify(VirtualTreeAccEvents.eventObjectSelectionWithin, VirtualTreeConstant.NullIndex, // VirtualTreeConstant.NullIndex, this); VirtualTreeAccEvents.Notify( VirtualTreeAccEvents.eventObjectSelection, CurrentIndex, CurrentColumn, this); } break; } } }
/// <summary> /// Set the multi select caret index on the given item. Calling this with false, false will /// make the new caret the only selected item. /// </summary> /// <param name="newCaret">The new caret position</param> /// <param name="extendFromAnchor">True to extend the selection from the current anchor position</param> /// <param name="preserveSelection">True to maintain selected items outside the anchored range</param> /// <param name="selectCaretAction">Specify how the selection state should be modified</param> public void SetCurrentExtendedMultiSelectIndex( int newCaret, bool extendFromAnchor, bool preserveSelection, ModifySelectionAction selectCaretAction) { SetCurrentExtendedMultiSelectIndex(newCaret, extendFromAnchor, preserveSelection, selectCaretAction, true); }
/// <summary> /// Locate and select an object in this control /// </summary> /// <param name="startingBranch">The branch to start the search from, or null for the root branch.</param> /// <param name="target">The object to locate</param> /// <param name="selectStyle">The object location style (sent to IBranch.LocateObject locateStyle parameter)</param> /// <param name="selectData">The object location data (sent to IBranch.LocateObject locateOptions parameter)</param> /// <param name="extendFromAnchor">Use when SelectionMode is ExtendedMultiSelect. True to extend the selection from the current anchor position</param> /// <param name="preserveSelection">Use when SelectionMode is ExtendedMultiSelect. True to maintain selected items outside the anchored range</param> /// <param name="selectCaretAction">Use when SelectionMode is ExtendedMultiSelect. Specify how the selection state should be modified</param> /// <returns>true if the item was selected</returns> public bool SelectObject( IBranch startingBranch, object target, int selectStyle, int selectData, bool extendFromAnchor, bool preserveSelection, ModifySelectionAction selectCaretAction) { if (myTree != null) { var locateTarget = myTree.LocateObject(startingBranch, target, selectStyle, selectData); if (locateTarget.IsValid) { var selectionColumn = mySelectionColumn; var targetColumn = locateTarget.Column; if (myColumnPermutation != null) { targetColumn = myColumnPermutation.GetPermutedColumn(targetColumn); if (targetColumn == -1) { // Item is not currently visible return false; } } if (GetStyleFlag(VTCStyleFlags.MultiSelect) && ExtendSelectionToAnchors) { // If the target column is in the same blank expansion as the // current column then we leave the selection column alone. if (myTree.GetBlankExpansion(locateTarget.Row, selectionColumn, myColumnPermutation).AnchorColumn == targetColumn) { // Block if condition below selectionColumn = targetColumn; } } if (selectionColumn != targetColumn) { SetSelectionColumn(locateTarget.Column, false); } if (GetStyleFlag(VTCStyleFlags.ExtendedMultiSelect)) { SetCurrentExtendedMultiSelectIndex(locateTarget.Row, extendFromAnchor, preserveSelection, selectCaretAction); } else { CurrentIndex = locateTarget.Row; } return true; } } return false; }