WP8 LongListMultiSelector Can't Remove Selected Items - c#

I have a LongListMultiSelector which is binded to an ObservableCollection. When I select all items in the list and try to delete as so it starts failing and returns false after 35 successful removes.
int failed = 0;
while ((App.ViewModel.Queue.Count - failed)> 0)
{
//get playlist viewmodel
bool success = App.ViewModel.Queue.Remove((ItemViewModel)QueueList.SelectedItems[0]);
if (!success)
failed++;
System.Diagnostics.Debug.WriteLine("DELETING " + success);
}
If I do not include the check for failed items it gets stuck in an endless loop. After the task completes, 35 of the 300 items are removed. If I re-run the task another 35 items get removed and the remaining fail...
My Observable collection is defined like so
public ObservableCollection<ItemViewModel> Queue { get; set; }
this.Queue = new ObservableCollection<ItemViewModel>();
In my xaml the MultiSelector is defined like this
<toolkit:LongListMultiSelector
x:Name="QueueList"
Margin="0,0,-12,0"
ItemsSource="{Binding Queue}"
LayoutMode="List"
SelectionChanged="QueueList_SelectionChanged">
How the Queue is populated (selected items from one list are copied to the Queue
foreach (ItemViewModel item in OriginalList.SelectedItems)
{
Boolean isQueued = false;
foreach (ItemViewModel queueItem in App.ViewModel.Queue)
{
if (queueItem.Equals(item))
{
isQueued = true;
break;
}
}
ItemViewModel newItem = new ItemViewModel();
newItem = item;
if (!isQueued)
{
App.ViewModel.Queue.Add(newItem);
for (int i = 0; i < ApplicationBar.MenuItems.Count; i++)
{
ApplicationBarMenuItem menuItem = (ApplicationBarMenuItem)ApplicationBar.MenuItems[i];
if (menuItem.Text.Equals("clear queue") && !menuItem.IsEnabled)
menuItem.IsEnabled = true;
}
}
}
OriginalList.SelectedItems.Clear();
DataSerializer.SaveQueue();
}

Iterate array of SelectedItems and remove items one by one. Example:
public void delete()
{
while (Queue.SelectedItems.Count > 0)
{
Queue.ItemsSource.Remove(Queue.SelectedItems[0]);
}
}

You can also use the Clear method, which will iterate over the array and unselect the selected items.
private void Cancel_Selected(object sender, System.Windows.Input.GestureEventArgs e)
{
Queue.SelectedItems.Clear();
}

Related

Custom Objects Listbox Foreach Loop

I have a list of postcodeitems and I bind this to a listbox.
It contains two properties : postcode, area.
I want to loop through the listbox items and if the item is selected, add the postcodeitem object to another list.
List<Valueobjects.postcodeitem> temp = _BL.GetPostCodeAreasFromZones();
PCList.AddRange(temp);
ListBox1.DataSource = PCList;
ListBox1.DataBind();
List<Valueobjects.postcodeitem> postcodecollection = new List<Valueobjects.postcodeitem>();
foreach (ListItem listitem in ListBox1.Items)
{
if (listitem.Selected)
{
i = i + 1;
//Run at 20 to speed up query
if (i == 20)
{
//Get data
CustList.AddRange(BL.SearchCustomerByPostcodeArea(postcodecollection,2));
i = 0;
}
else
{
//add the post code to temp list
postcodecollection.Add(listitem);
}
}
}
if (i > 0)
{
//get data
CustList.AddRange(BL.SearchCustomerByPostcodeArea(postcodecollection,2));
}
}
Obviously where I am trying to add (listitem) isnt going to work as this is a list item and not a postcodeitem. My question is how do I get the postcodeitem object within the list where the list item is selected?
thanks
Try something like following.
IList<Employee> boundList = ListBox1.DataSource
var obj = boundList[ListBox1.SelectedIndex]
Update => I have not tested the code but something like following. Using for loop to track the element index.
for (int i = 0; i< ListBox1.Items.Length; i++)
{
if (ListBox1.Items[i].Selected)
{
i = i + 1;
//Run at 20 to speed up query
if (i == 20)
{
//Get data
CustList.AddRange(BL.SearchCustomerByPostcodeArea(postcodecollection,2));
i = 0;
}
else
{
//add the post code to temp list
postcodecollection.Add(ListBox1.DataSource.ToList().ElementAt(i));
}
}
}
if (i > 0)
{
//get data
CustList.AddRange(BL.SearchCustomerByPostcodeArea(postcodecollection,2));
}
}
Anyways this is not recommended way. You should get selected value and use that selected value(unique field) to fetch relevant data from any persistant storage like database.

How I can realize parallel search in ObservableCollection?

I have an ObservableCollection, where Item has 2 properties(for example: Name and Id) and collection contains of 12k elements. So, i have a textbox, i want to search elements, which names contains my textbox value and add these elems in new collection.
in real-proj:
Silverlight, TreeView(its ItemSource is my collection) which dynamically changing. And TreeView changing in UI.
My problem is just in slowly rendering results of search. I thing if it'll be parallel - it saves me.
for example, some code im using:
private ObservableCollection<ICDDocumentItemViewModel> LinearSearch(string searchText)
{
var filteredCollection = new ObservableCollection<ICDDocumentItemViewModel>();
if (searchText.Length > 3)
{
foreach (var itemViewModel in _linearCollection)
{
if (!itemViewModel.Model.Name.ToLower().Contains(searchText.ToLower())) continue;
if (itemViewModel.Children.Count != 0)
{
itemViewModel.IsExpanded = true;
}
filteredCollection.Add(itemViewModel);
}
}
if(searchText.Length <= 3)
{
return new ObservableCollection<ICDDocumentItemViewModel>(ICDItemsViewModelsMain);
}
return filteredCollection;
}
there is no need to have parallel processing in place normally, this code should help you here.
private ObservableCollection<ICDDocumentItemViewModel> GetFiltered(string filter)
{
ObservableCollection<ICDDocumentItemViewModel> filteredCollection;
if (filter.Length > 3)
{
filteredCollection = new ObservableCollection<ICDDocumentItemViewModel>(_linearCollection.Where(x => x.Name.ToLower().Contains(filter)));
filteredCollection.ToList().ForEach(DetectChildren);
}
else
{
filteredCollection = new ObservableCollection<ICDDocumentItemViewModel>();
}
return filteredCollection;
}
private void DetectChildren(ICDDocumentItemViewModel item)
{
item.IsExpanded = item.Children.Any();
}

Sorting a ListBox of objects by attributes

I have a list created by:
List<Song> SongList = new List<Song>();
Populated by a bunch of:
SongList.Add(new Song(songID, "Dirt", "Alice in Chains", "Rooster", "Rock", "02:32"));
The details of songs are populated into a ListBox by:
private void songTitle_TextChanged(object sender, EventArgs e)
{
i = 0;
for (; i < songTitle.Text.Length; i++)
{
songResultBox.Items.Clear();
var songResult = from song in SongList
where song.SongTitle != null && song.SongTitle.Length >= songTitle.Text.Length
&& songTitle.Text == song.SongTitle.Substring(0, i+1)
select new { sId = song.SongID, album = song.SongAlbum, artist = song.SongArtist, title = song.SongTitle,
genre = song.SongGenre, length = song.SongLength };
foreach (var item in songResult)
{
songResultBox.Items.Add(new Song(item.sId, item.album, item.artist, item.title, item.genre, item.length));
songResultBox.DisplayMember = "FullName";
songResultBox.ValueMember = "songID";
}
}
}
Question is: How would I create a button (or 4 in fact) that took the contents of the ListBox 'songResultBox' and sorted its contents by title, album, artist, genre etc.
Create the button, label it depending on what property you want to sort on, add a click event to the button, sort the items (hopefully you've maintained a list of them), then repopulate the listbox:
private bool descendingSongTitleSort = false;
private bool descendingArtistSort = false;
// Artist button clicked event
private void artistButtonClicked(object sender, EventArgs args)
{
Func<Song, IComparable> sortProp = (song => song.Artist);
sortListBox(songResultBox, descendingArtistSort, sortProp);
descendingSongTitleSort = false;
descendingArtistSort = !descendingArtistSort;
}
// Title button clicked event
private void titleButtonClicked(object sender, EventArgs args)
{
Func<Song, IComparable> sortProp = (song => song.Title);
sortListBox(songResultBox, descendingSongTitleSort, sortProp);
descendingSongTitleSort = !descendingSongTitleSort;
descendingArtistSort = false;
}
// Sorting function
private void sortListBox(
ListBox box,
bool descending,
Func<Song, IComparable> sortProperty)
{
List<Song> items = new List<Song>();
foreach (Object o in box.Items)
{
Song s = o as Song;
items.Add(s);
}
box.Items.Clear();
if(descending)
{
items = items.OrderByDescending(sortProperty).ToList();
}
else
{
items = items.OrderBy(sortProperty).ToList();
}
foreach(Song s in items)
{
box.Items.Add(s);
}
}
The descending bools aren't necessary if you don't want to worry about resorting in the opposite direction.

Moving an item up and down in a WPF list box

I have a list box with a bunch of values in it. I also have an UP button and a DOWN button.
With these buttons, I would like to move the selected item in the list box up/down. I am having trouble doing this.
Here is my code so far:
private void btnDataUp_Click(object sender, RoutedEventArgs e)
{
int selectedIndex = listBoxDatasetValues.SelectedIndex; //get the selected item in the data list
if (selectedIndex != -1 && selectedIndex != 0) //if the selected item is selected and not at the top of the list
{
//swap items here
listBoxDatasetValues.SelectedIndex = selectedIndex - 1; //keep the item selected
}
}
I do not know how to swap the values! Any help would be GREATLY appreciated!
Since you have populated the listbox by binding to a ObservableCollection using ItemsSource, you cant modify the Items property of the listbox.
ItemsSource can be set only when the Items collection is empty, and Items can be modified only when ItemsSource is null.
Otherwise you will get the error "Operation is not valid while ItemsSource is in use..."
What you have to do, is modify the underlying collection, and because it's an ObservableCollection, the ListBox will reflect the changes.
The following code shows how you can move an item up and down by swapping the item in the collection.
The corresponding XAML just contains a listbox called lbItems and 2 buttons which hook up the eventhandlers.
public partial class MainWindow : Window
{
private ObservableCollection<string> ListItems = new ObservableCollection<string>
{
"Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6"
};
public MainWindow()
{
InitializeComponent();
lbItems.ItemsSource = this.ListItems;
}
private void up_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex > 0)
{
var itemToMoveUp = this.ListItems[selectedIndex];
this.ListItems.RemoveAt(selectedIndex);
this.ListItems.Insert(selectedIndex - 1, itemToMoveUp);
this.lbItems.SelectedIndex = selectedIndex - 1;
}
}
private void down_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex + 1 < this.ListItems.Count)
{
var itemToMoveDown = this.ListItems[selectedIndex];
this.ListItems.RemoveAt(selectedIndex);
this.ListItems.Insert(selectedIndex + 1, itemToMoveDown);
this.lbItems.SelectedIndex = selectedIndex + 1;
}
}
}
I make some extension methods for this:
public static void MoveItemUp<T>(this ObservableCollection<T> baseCollection, int selectedIndex)
{
//# Check if move is possible
if (selectedIndex <= 0)
return;
//# Move-Item
baseCollection.Move(selectedIndex - 1, selectedIndex);
}
public static void MoveItemDown<T>(this ObservableCollection<T> baseCollection, int selectedIndex)
{
//# Check if move is possible
if (selectedIndex < 0 || selectedIndex + 1 >= baseCollection.Count)
return;
//# Move-Item
baseCollection.Move(selectedIndex + 1, selectedIndex);
}
public static void MoveItemDown<T>(this ObservableCollection<T> baseCollection, T selectedItem)
{
//# MoveDown based on Item
baseCollection.MoveItemDown(baseCollection.IndexOf(selectedItem));
}
public static void MoveItemUp<T>(this ObservableCollection<T> baseCollection, T selectedItem)
{
//# MoveUp based on Item
baseCollection.MoveItemUp(baseCollection.IndexOf(selectedItem));
}
There is no need to know the ListBox for this.
This is the easiest way to do this and it fires all the right events so you don't have to worry about XAML. ObservableCollection has a nice method called
MoveItem(previousIndex, newIndex)
Given that you have a ObservableCollection named DataItemList
public void MoveUp()
{
var currentIndex = DataItemList.SelectedIndex;
//Index of the selected item
if (currentIndex > 0)
{
int upIndex = currentIndex - 1;
//move the items
DataItemList.MoveItem(upIndex,currentIndex);
}
}
For Down you get the index of the preceding item.
Simple as that!
I would have added a comment, but I can't since I only have 3 reputation :/
Peter Hansen's solution is great, but if there isn't a selected element, down_click throws an ArgumentOutOfRange Exception. This is because if there is no element selected, the Index is equal to -1.
I'd edit down_click like this:
private void down_click(object sender, RoutedEventArgs e)
{
if (this.lbItems.SelectedIndex != -1) //Added condition
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex + 1 < this.ListItems.Count)
{
var itemToMoveDown = this.ListItems[selectedIndex];
this.ListItems.RemoveAt(selectedIndex);
this.ListItems.Insert(selectedIndex + 1, itemToMoveDown);
this.lbItems.SelectedIndex = selectedIndex + 1;
}
}
}
try this:
if (listBoxDatasetValues.SelectedItems.Count > 0)
{
object selected = listBoxDatasetValues.SelectedItem;
int indx = listBoxDatasetValues.Items.IndexOf(selected);
int totl = listBoxDatasetValues.Items.Count;
if (indx == 0)
{
listBoxDatasetValues.Items.Remove(selected);
listBoxDatasetValues.Items.Insert(totl - 1, selected);
listBoxDatasetValues.SetSelected(totl - 1, true);
}
else{
listBoxDatasetValues.Items.Remove(selected);
listBoxDatasetValues.Items.Insert(indx - 1, selected);
listBoxDatasetValues.SetSelected(indx - 1, true);
}
}
if(listBoxDatasetValues.ListIndex > 0)
{
// add a duplicate item up in the listbox
listBoxDatasetValues.AddItem(listBoxDatasetValues.Text, listBoxDatasetValues.ListIndex - 1);
// make it the current item
listBoxDatasetValues.ListIndex = (listBoxDatasetValues.ListIndex - 2);
// delete the old occurrence of this item
listBoxDatasetValues.RemoveItem(listBoxDatasetValues.ListIndex + 2);
}
You may try something like this:
For moving UP:
if (listboxName.SelectedIndex == -1 || listboxName.SelectedIndex == 0)
return;
Object select, previous, temp;
select = listboxName.Items[listboxName.SelectedIndex];
previous = listboxName.Items[listboxName.SelectedIndex-1];
temp = select;
select = previous;
previous = temp;
listboxName.Items[listboxName.SelectedIndex] = select;
listboxName.Items[listboxName.SelectedIndex-1] = previous;
listboxName.SelectedIndex--;
For moving Down:
if (listboxName.SelectedIndex == -1 || listboxName.SelectedIndex == listboxName.Items.Count-1)
return;
Object select, next, temp;
select = listboxName.Items[listboxName.SelectedIndex];
next = listboxName.Items[listboxName.SelectedIndex+1];
temp = select;
select = next;
next = temp;
listboxName.Items[listboxName.SelectedIndex] = select;
listboxName.Items[listboxName.SelectedIndex+1] = next;
listboxName.SelectedIndex++;
No need to use observable collection you can do it simpler just by relying on the collection object inside the ListBox, here is the optimized version of #Peter Hansen`s answer:
private void up_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex > 0)
{
var itemToMoveUp = this.lbItems.Items[selectedIndex];
this.lbItems.Items.RemoveAt(selectedIndex);
this.lbItems.Items.Insert(selectedIndex - 1, itemToMoveUp);
this.lbItems.SelectedIndex = selectedIndex - 1;
}
}
private void down_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex + 1 < this.lbItems.Items.Count)
{
var itemToMoveDown = this.lbItems.Items[selectedIndex];
this.lbItems.Items.RemoveAt(selectedIndex);
this.lbItems.Items.Insert(selectedIndex + 1, itemToMoveDown);
this.lbItems.SelectedIndex = selectedIndex + 1;
}
}

Refresh UltraGrid's GroupBy Sort on child bands when ListChanged?

I am using Infragistics 2009 vol 1.
My UltraGrid is bound to a BindingList of business objects "A" having themself a BindingList property of business objects "B". It results in having two bands: one named "BindingList`1", the other one "ListOfB" thanks to the currency manager.
I would like to refresh the GroupBy sort of the grid whenever a change is performed on the child band through the child business object and INotifyPropertyChange.
If I group by a property in the child band which is a boolean (let's say "Active") and I subscribe to the event ListChanged on the bindinglist datasource with this event handler:
void Grid_ListChanged(object sender, ListChangedEventArgs e)
{
if (e.ListChangedType == ListChangedType.ItemChanged)
{
string columnKey = e.PropertyDescriptor.Name;
if (e.PropertyDescriptor.PropertyType.Name == "BindingList`1")
{
ultraGrid.DisplayLayout.Bands[columnKey].SortedColumns.RefreshSort(true);
}
else
{
UltraGridBand band = ultraGrid.DisplayLayout.Bands[0];
UltraGridColumn gc = band.Columns[columnKey];
if (gc.IsGroupByColumn || gc.SortIndicator != SortIndicator.None)
{
band.SortedColumns.RefreshSort(true);
}
ColumnFilter cf = band.ColumnFilters[columnKey];
if (cf.FilterConditions.Count > 0)
{
ultraGrid.DisplayLayout.RefreshFilters();
}
}
}
}
the band.SortedColumns.RefreshSort(true) is called but It gives unpredictable results in the groupby area when the property Active is changed in the child band:
if one object out of three actives becomes inactive it goes from:
Active : True (3 items)
To:
Active : False (3 items)
Instead of (which is the case when I drag the column back and forth to the group by area)
Active : False (1 item)
Active : True (2 items)
Am I doing something wrong?
Is there a way to restore the expanded state of the rows when performing a RefreshSort(true); ?
Sounds like a bug to me - you should file one with Infragistics.
As to a workaround - this is not a nice solution and I haven't tested it but you could always try saving the sort-by (group) columns to a temp store, RefreshSort() on the band, then re-apply the sort-by (gorup) columns, and sort again?
ie. Remove group-by, then re-apply.
Nasty, but it might get you out of a bind.
Well, this is how I manage to solve the problem
var ultraGridBand = this.ExperimentGrid.DisplayLayout.Bands[0];
int nbGroup = ultraGridBand.Columns.All.Count(obj => ((UltraGridColumn) obj).IsGroupByColumn);
//if (ultraGridBand.Columns.All.Any(obj => ((UltraGridColumn)obj).SortIndicator != SortIndicator.None)))
if (nbGroup == 0)//no grouping
ultraGridBand.SortedColumns.RefreshSort(true);
else if (nbGroup > 0)
{
var expandedRows = new List<ExpandedRow>();
var rowLevel1 = this.ExperimentGrid.DisplayLayout.Rows;
ExtractExpandedRows(expandedRows, rowLevel1);
ultraGridBand.SortedColumns.RefreshSort(true);
SetExpandedRows(expandedRows, rowLevel1);
}
private static void SetExpandedRows(IEnumerable<ExpandedRow> expandedRows, RowsCollection rowLevel)
{
foreach (object obj in rowLevel.All)
{
var row = obj as UltraGridGroupByRow;
if (row != null)
{
var expandedRow = expandedRows.FirstOrDefault(x => x.Value == row.ValueAsDisplayText);
if (expandedRow != null)
{
row.Expanded = expandedRow.IsExpanded;
SetExpandedRows(expandedRow.SubRows, row.Rows);
}
}
}
}
private static void ExtractExpandedRows(ICollection<ExpandedRow> expandedRows, RowsCollection rowLevel)
{
foreach (object obj in rowLevel.All)
{
var row = obj as UltraGridGroupByRow;
if(row != null)
{
var expandedRow = new ExpandedRow() { Value = row.ValueAsDisplayText, IsExpanded = row.Expanded };
expandedRows.Add(expandedRow);
ExtractExpandedRows(expandedRow.SubRows, row.Rows);
}
}
}
And this is the object that contains the information
class ExpandedRow
{
public string Value { get; set; }
public bool IsExpanded { get; set; }
private readonly List<ExpandedRow> _subRows = new List<ExpandedRow>();
public List<ExpandedRow> SubRows { get { return _subRows; } }
}

Categories