protected internal override void HandleIsSelectedChanged(bool oldValue, bool newValue) { base.HandleIsSelectedChanged(oldValue, newValue); if (INTERNAL_VisualTreeManager.IsElementInVisualTree(this)) { //todo: remove what's inside the "if" above once we will have defined a default style for the ItemsControl, that does about the same thing. if (INTERNAL_ParentSelectorControl != null) { if (INTERNAL_ParentSelectorControl is MultiSelector) { var parent = (MultiSelector)INTERNAL_ParentSelectorControl; if (newValue) { if (!INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Contains(parent.SelectedItems, this.INTERNAL_CorrespondingItem)) { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Add(parent.SelectedItems, this.INTERNAL_CorrespondingItem); } } else { if (INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Contains(parent.SelectedItems, this.INTERNAL_CorrespondingItem)) { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Remove(parent.SelectedItems, this.INTERNAL_CorrespondingItem); } } } } } }
protected override void OnItemsSourceChanged_BeforeVisualUpdate(IEnumerable oldValue, IEnumerable newValue) { base.OnItemsSourceChanged_BeforeVisualUpdate(oldValue, newValue); if (SelectedItems != null && SelectedItems.Count != 0) { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); } }
//we didn't implement a SelectionMode_Changed method since I don't think it would be of any use. protected override void ManageSelectedIndex_Changed(DependencyPropertyChangedEventArgs e) { // Note: in this method, we use "Convert.ToInt32()" intead of casting to "(int)" because otherwise the JS code is not compatible with IE 9 (or Windows Phone 8.0). base.ManageSelectedIndex_Changed(e); if (e.OldValue != null && e.OldValue is int && Convert.ToInt32(e.OldValue) >= 0 && SelectionMode == SelectionMode.Single) { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); } if (e.NewValue != null && e.NewValue is int && Convert.ToInt32(e.NewValue) >= 0) { int newValue = Convert.ToInt32(e.NewValue); if (SelectionMode == SelectionMode.Single) { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); SelectedItems.Add(Items[newValue]); } else if (!INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Contains(SelectedItems, e.NewValue)) { SelectedItems.Add(Items[newValue]); } } }
void listBoxItem_Click(object sender, RoutedEventArgs e) { #if MIGRATION ModifierKeys modifiersKey = new ModifierKeys(); modifiersKey = Keyboard.Modifiers; bool isControl = modifiersKey.HasFlag(ModifierKeys.Control); bool isShift = modifiersKey.HasFlag(ModifierKeys.Shift); #else VirtualKeyModifiers modifiersKey = new VirtualKeyModifiers(); modifiersKey = Keyboard.Modifiers; bool isControl = modifiersKey.HasFlag(VirtualKeyModifiers.Control); bool isShift = modifiersKey.HasFlag(VirtualKeyModifiers.Shift); #endif SelectorItem selectorItem = (SelectorItem)sender; //--------------------------------------------------- //Single //--------------------------------------------------- if (SelectionMode == SelectionMode.Single) { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); SelectedItems.Add(selectorItem.INTERNAL_CorrespondingItem); } //--------------------------------------------------- //Multiple //--------------------------------------------------- else if (SelectionMode == SelectionMode.Multiple) { //If click on an already selected element if (INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Contains(SelectedItems, selectorItem.INTERNAL_CorrespondingItem))//if we are in a multiple mode and the currently selected element is the one we clicked, we want to unselect it. { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Remove(SelectedItems, selectorItem.INTERNAL_CorrespondingItem); } //else click on a new element else { SelectedItems.Add(selectorItem.INTERNAL_CorrespondingItem); } } //--------------------------------------------------- //Extended //--------------------------------------------------- else if (SelectionMode == SelectionMode.Extended) { int indexOfClickedItem = ConvertToListOfObjectsOrNull(_actualItemsSource).IndexOf(selectorItem.INTERNAL_CorrespondingItem); //todo-perfs: this is O(N). //if Shift is pressed if (isShift) { int indexStart = _indexOfLastClickedItemWithoutShiftKey; int indexEnd = indexOfClickedItem; int change; if (indexStart > indexEnd) { change = indexEnd; indexEnd = indexStart; indexStart = change; } int index = 0; //clear before adding INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); foreach (object item in _actualItemsSource) { if (indexStart <= index && index <= indexEnd) { if (!INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Contains(SelectedItems, item)) //todo-perfs: use a dictionary { SelectedItems.Add(item); } } index++; } } //if Control is pressed else if (isControl) { //If click on an already selected element if (INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Contains(SelectedItems, selectorItem.INTERNAL_CorrespondingItem))//if we are in a multiple mode and the currently selected element is the one we clicked, we want to unselect it. { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Remove(SelectedItems, selectorItem.INTERNAL_CorrespondingItem); } //else click on a new element else { SelectedItems.Add(selectorItem.INTERNAL_CorrespondingItem); } } //Nothing is pressed else { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); SelectedItems.Add(selectorItem.INTERNAL_CorrespondingItem); } if (!isShift) { _indexOfLastClickedItemWithoutShiftKey = indexOfClickedItem; } } else { throw new NotSupportedException("SelectionMode is not Single, Multiple or Extended."); } }
void listBoxItem_Click(object sender, RoutedEventArgs e) { _skipSelectionChangedEvent = true; // Note: we keep this to true for the whole method since this method will handle the SelectionChanged event itself (this way we don't risk having multiple calls to SelectionChanged when adding multiple elements for example). //object[] oldItems = null; bool hasSelectionChanged = false; List <object> addedItems = new List <object>(); List <object> removedItems = new List <object>(); #if MIGRATION ModifierKeys modifiersKey = new ModifierKeys(); modifiersKey = Keyboard.Modifiers; bool isControl = modifiersKey.HasFlag(ModifierKeys.Control); bool isShift = modifiersKey.HasFlag(ModifierKeys.Shift); #else VirtualKeyModifiers modifiersKey = new VirtualKeyModifiers(); modifiersKey = Keyboard.Modifiers; bool isControl = modifiersKey.HasFlag(VirtualKeyModifiers.Control); bool isShift = modifiersKey.HasFlag(VirtualKeyModifiers.Shift); #endif SelectorItem selectorItem = (SelectorItem)sender; //--------------------------------------------------- //Single //--------------------------------------------------- if (SelectionMode == SelectionMode.Single) { if (SelectedItem != selectorItem.INTERNAL_CorrespondingItem) //todo: SelectedItems contains more than one element (I'm pretty sure we ignore is as long as the clicked element is the SelectedItem). { HashSet <object> selectedItemsHashSet = CopyToHashSet(SelectedItems); INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); SelectedItems.Add(selectorItem.INTERNAL_CorrespondingItem); addedItems.Add(selectorItem.INTERNAL_CorrespondingItem); if (selectedItemsHashSet.Contains(selectorItem.INTERNAL_CorrespondingItem)) { selectedItemsHashSet.Remove(selectorItem.INTERNAL_CorrespondingItem); } foreach (object item in selectedItemsHashSet) { removedItems.Add(item); } hasSelectionChanged = true; } } //--------------------------------------------------- //Multiple //--------------------------------------------------- else if (SelectionMode == SelectionMode.Multiple) { //If click on an already selected element if (INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Contains(SelectedItems, selectorItem.INTERNAL_CorrespondingItem))//if we are in a multiple mode and the currently selected element is the one we clicked, we want to unselect it. { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Remove(SelectedItems, selectorItem.INTERNAL_CorrespondingItem); removedItems.Add(selectorItem.INTERNAL_CorrespondingItem); } //else click on a new element else { SelectedItems.Add(selectorItem.INTERNAL_CorrespondingItem); addedItems.Add(selectorItem.INTERNAL_CorrespondingItem); } hasSelectionChanged = true; } //--------------------------------------------------- //Extended //--------------------------------------------------- else if (SelectionMode == SelectionMode.Extended) { int indexOfClickedItem = INTERNAL_ListsHelper.ConvertToListOfObjectsOrNull(Items).IndexOf(selectorItem.INTERNAL_CorrespondingItem); //todo-perfs: this is O(N). //if Shift is pressed if (isShift) { int indexStart = _indexOfLastClickedItemWithoutShiftKey; int indexEnd = indexOfClickedItem; int change; if (indexStart > indexEnd) { change = indexEnd; indexEnd = indexStart; indexStart = change; } int index = 0; //copy the SelectedItems in a hashset so we can easily know which items have been removed: HashSet <object> selectedItemsHashSet = CopyToHashSet(SelectedItems); //clear before adding INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); foreach (object item in Items) { if (indexStart <= index && index <= indexEnd) { if (!INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Contains(SelectedItems, item)) //todo-perfs: use a dictionary { SelectedItems.Add(item); addedItems.Add(item); if (selectedItemsHashSet.Contains(item)) { selectedItemsHashSet.Remove(item); } } } index++; } foreach (object item in selectedItemsHashSet) { removedItems.Add(item); } hasSelectionChanged = removedItems.Count > 0 || addedItems.Count > 0; } //if Control is pressed else if (isControl) { //If click on an already selected element if (INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Contains(SelectedItems, selectorItem.INTERNAL_CorrespondingItem))//if we are in a multiple mode and the currently selected element is the one we clicked, we want to unselect it. { INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Remove(SelectedItems, selectorItem.INTERNAL_CorrespondingItem); removedItems.Add(selectorItem.INTERNAL_CorrespondingItem); } //else click on a new element else { SelectedItems.Add(selectorItem.INTERNAL_CorrespondingItem); addedItems.Add(selectorItem.INTERNAL_CorrespondingItem); } hasSelectionChanged = true; } //Nothing is pressed else { if (SelectedItem != selectorItem.INTERNAL_CorrespondingItem) { HashSet <object> selectedItemsHashSet = CopyToHashSet(SelectedItems); INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); SelectedItems.Add(selectorItem.INTERNAL_CorrespondingItem); addedItems.Add(selectorItem.INTERNAL_CorrespondingItem); if (selectedItemsHashSet.Contains(selectorItem.INTERNAL_CorrespondingItem)) { selectedItemsHashSet.Remove(selectorItem.INTERNAL_CorrespondingItem); } foreach (object item in selectedItemsHashSet) { removedItems.Add(item); } hasSelectionChanged = true; } } if (!isShift) { _indexOfLastClickedItemWithoutShiftKey = indexOfClickedItem; } } else { throw new NotSupportedException("SelectionMode is not Single, Multiple or Extended."); } OnSelectionChanged(new SelectionChangedEventArgs(removedItems, addedItems)); _skipSelectionChangedEvent = false; }
//we didn't implement a SelectionMode_Changed method since I don't think it would be of any use. protected override void ManageSelectedIndex_Changed(DependencyPropertyChangedEventArgs e) { if (!_skipSelectionChangedEvent) //this test because if it is true (meaning the SelectionChanged event is dealt with somewhere else), it means that SelectedItems has already been updated. { bool skipSelectionEventHere = _skipSelectionChangedEvent; _skipSelectionChangedEvent = true; // Note: we keep this to true for the whole method since this method will handle the SelectionChanged event itself. base.ManageSelectedIndex_Changed(e); bool hasSelectionChanged = false; List <object> addedItems = new List <object>(); List <object> removedItems = new List <object>(); HashSet <object> selectedItemsHashSet = null; // Note: in this method, we use "Convert.ToInt32()" intead of casting to "(int)" because otherwise the JS code is not compatible with IE 9 (or Windows Phone 8.0). bool isNewValueSet = e.NewValue != null && e.NewValue is int && Convert.ToInt32(e.NewValue) >= 0; bool isOldValueSet = e.OldValue != null && e.OldValue is int && Convert.ToInt32(e.OldValue) >= 0; bool isNewValueDifferentFromOldValue = e.NewValue != e.OldValue; if (isOldValueSet && isNewValueDifferentFromOldValue) { //We clear the selection and remember the old items if needed for the SelectionChanged event: if (!skipSelectionEventHere) { selectedItemsHashSet = CopyToHashSet(SelectedItems); hasSelectionChanged = true; } INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); } if (isNewValueSet) { int newValue = Convert.ToInt32(e.NewValue); object item = Items[newValue]; if (SelectedItems.Count == 0 || SelectedItems[0] != item) //If SelectedItem stays the same (the first item in SelectedItems), we do nothing { if (SelectedItems.Count != 0) //this test is to avoid calling clear (and entering MultiSelector.RefreshSelectedItems) for no reason. { if (!skipSelectionEventHere) { selectedItemsHashSet = CopyToHashSet(SelectedItems); } INTERNAL_WorkaroundObservableCollectionBugWithJSIL.Clear(SelectedItems); } SelectedItems.Add(item); if (!skipSelectionEventHere) { if (selectedItemsHashSet != null && selectedItemsHashSet.Contains(item)) { selectedItemsHashSet.Remove(item); } addedItems.Add(item); hasSelectionChanged = true; } } } if (hasSelectionChanged && !skipSelectionEventHere) { if (selectedItemsHashSet != null) { foreach (object item in selectedItemsHashSet) { removedItems.Add(item); } } OnSelectionChanged(new SelectionChangedEventArgs(removedItems, addedItems)); } _skipSelectionChangedEvent = skipSelectionEventHere; } }