Can't remove item from ObservableCollection through ContextMenu - c#

I have a program with a class called MyClass and Location. MyClass contains an ObservableCollection of Location items and Location contains a string property called Name. In MainPage.xaml I have a LongListSelector (with a ContextMenu for each item) populated with grids representing a Location.
When I click the 'remove' menu item from the context control, it will usually remove the underlying Location object and update the view. After a few cycles of populating the LongListSelector and removing all its items, some new items that are added can't be removed anymore.
Here's an example of what I mean: The LLS originally contains 2 items. Then I delete those 2 items and add 3 more. However, I can only remove the third one, in this case, but not the first 2.
Here's the ContextMenu MenuItem click event from MainPage.xaml.cs:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
var selectedItem = (sender as MenuItem).DataContext as Location;
for (int i = 0; i < MyClass.Locations.Count; i++)
{
if (MyClass.Locations[i].Name == selectedItem.Name)
{
MyClass.Locations.Remove(MyClass.Locations[i]);
break;
}
}
}
Prior to using a for loop, I used this LINQ code and still had the same problem:
var toRemove = MyClass.Locations.Where(x => x.Name == selectedItem.Name).SingleOrDefault();
MyClass.Locations.Remove(toRemove);
Any suggestions to fix this problem?

I suggest you to use a ListBox instead of LLS - if you are not using grouping option. It works much better and causes less problems.
By the way I've also encountered some problems with this Control - maybe similar to yours. Weird is also that LLS.UpdateLayout() doesn't work while in ListBox works perfect.

Related

Treeview.Items.Clear() method return null exception (e.NewValue==null) in SelectedItemChanged Event

I am from Iran and I cant speak English very well, sorry.
I made something like OpenFileDialog in WinForms
and work correctly.
After, for better user interface, I tried to make it in WPF.
I use TreeView and other controls for it in both platforms (Winforms and WPF)
in Winforms I could do this correctly usingbelow code:
private void Folder_FileTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
Folder_FileTreeView.Nodes.Clear();//this is necessary to clean first page node, after get new folders
if(e.Node.Text=="Desktop")//also this code is necessary to compare node
{
//Do something
}
}
Also in WPF I can get text of Item by below code:
private void Folder_FileTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue!=null)
{
StackPanel CustomStackPanel = (StackPanel)((TreeViewItem)e.NewValue).Header;
TextBlock textBlock = (TextBlock)CustomStackPanel.Children[1];
nodetext = textBlock.Text;//this line return text of item for compare
}
Folder_FileTreeView.Items.Clear();
}
If I don't use Folder_FileTreeView.Items.Clear() the above code return folders without clearing first page, but if I do use Folder_FileTreeView.Items.Clear() e.NewValue returns null.
Please help me to use together these codes: Folder_FileTreeView.Items.Clear();(or clear first page) and get text of selecteditem by user without return null
Thanks A lot
e.NewItem will be null if the TreeView used to have an item selected but now does not. When you clear the items, you are removing any selection, this of course changes the selection and raises the SelectedItemChanged event with null as the new selection- since there are no possible items that could be selected.
If you want to replace the items in the list with new items after the user makes a selection, the selected item will be null while that change is happening. You need to do the following:
Handle the SelectedItemChanged event and remember the new selected item in a variable.
For example, if they click on the item for "Desktop" set a variable (e.g. Path) to the path for the user's desktop (e.g. C:\Users\UserName\Desktop).
Clear the list of folders in the TreeView. This will trigger SelectedItemChanged again, but you want to ignore it this time because e.NewItem == null.
Read all the folders in Path and make new items for each of those folders.
The way was found by below code
private void Folder_FileTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
Folder_FileTreeView.SelectedItemChanged -= Folder_FileTreeView_SelectedItemChanged;
if (e.NewValue!=null)
{
StackPanel CustomStackPanel = (StackPanel)((TreeViewItem)e.NewValue).Header;
TextBlock textBlock = (TextBlock)CustomStackPanel.Children[1];
nodetext = textBlock.Text;//this line return text of item for compare
}
Folder_FileTreeView.Items.Clear();
Folder_FileTreeView.SelectedItemChanged += Folder_FileTreeView_SelectedItemChanged;
}
thank very much for every one helped me

Move selected index when typing in a WPF combobox

I have a problem with the combobox in WPF. You know that when you open the combobox and you start typing, that the selected index of the combobox is moving to the element that starts with the same letter. Well i actually need the same thing but a bit different.
The items in the combobox are actually binded to a class. This class has 2 properties, a Code property ( contains for example "XF15A") and a Description property ( contains for example "Radio"). I used a data template that actually binded the text for an combobox item to "[code] - [Description]".
Now when the type "XF" is goes to the combobox item that starts "XF". But what i now also need is that when you type "Ra" it should go to the combobox item "XF15A - Radio".
Do you guys know how to solve this? I'm also open for existing usercontrols.
Thanks,
My code is not quite what you want, but should give you an example of how you could do it yourself:
You got to handle PreviewTextInput yourself and let your algorithm decide which item to select. Here's a simple example:
XAML:
<ComboBox x:Name="cb" PreviewTextInput="ComboBox_PreviewTextInput">
<ComboBoxItem>adsfsf</ComboBoxItem>
<ComboBoxItem>adsfsf</ComboBoxItem>
<ComboBoxItem>acdd</ComboBoxItem>
<ComboBoxItem>adsfsf</ComboBoxItem>
</ComboBox>
Code Behind:
private void ComboBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
cb.IsDropDownOpen = true;
foreach (ComboBoxItem item in cb.Items)
{
var str = (string)item.Content;
if(str.Contains(e.Text))
{
cb.SelectedItem = item;
break;
}
}
}

Datagrid is not properly refreshing on row delete

I am working with a datagrid in WPF which is bound to a collectionviewsource. The viewsource is bound to an observable collection named Rows.
The datagrid has add and delete functions which function properly except on small problem.
Here are images:
The datagrid has more data than this. Each test starts off with two sequences (the two rows you see belong to a single test) and they are grouped and sorted by a unique ID.
I have clicked the red "X" to delete the row. I will now click the "Add" button located at the top-left of the image.
The data is still there.
These are my add and delete functions:
private void Add(object sender, ExecutedRoutedEventArgs e)
{
var testRun = e.Parameter as TestRun;
if (testRun != null)
{
var numberOfRows = testRun.Property.GetValue("numberOfRows").ToNullable<int>().GetValueOrDefault(2);
numberOfRows++;
testRun.Property.SetValue("numberOfRows", numberOfRows.ToString());
this.Rows.Add(new ESCHandle(testRun, numberOfRows));
}
}
private void Delete(object sender, ExecutedRoutedEventArgs e)
{
var esc = e.Parameter as ESCHandle;
if (esc != null)
{
this.Rows.Remove(esc);
var numberOfRows = esc.TestRun.Property.GetValue("numberOfRows").ToNullable<int>().GetValueOrDefault(2);
numberOfRows--;
esc.TestRun.Property.SetValue("numberOfRows", numberOfRows.ToString());
}
}
The ESC object is properly removed from the observablecollection on Delete. But on when I add another ESC object/row to the colleciton and datagrid, the data is somehow copied to the new object.
You may forget to call a refresh method on datagrid to update its visual elements, such as the rows.
Datagrid.Items.Refresh(), as described here:
http://programmer.wrighton.org/2009/01/wpf-datagrid-items-refresh.html
This problem may be caused because PropertyChange is not raised properly.

Reorder ListView items: Event and Drag/Drop

My page contains two ListView elements, ListA and ListB. You can copy items (objects) from ListA to ListB by use of simple drag and drop. For that I use the Lists' DragItemsStarting and Drop events. That works just fine. Now you can reorder the items in ListB. All required properties are set (reorder, drag and drop) and this works as well.
But now I want to react on the resorting, but I didn't find an event I could listen to. So I thought it might be possible to use the drag/drop events on the same list to get to know when the user has changed the psoition of an item.
So how can I recognize that the items of ListB have been reorderd?
My code (with the drag and drop approach):
private void ListB_DragItemsStarting(object sender, DragItemsStartingEventArgs e) {
var item = ((FeedItem)e.Items[0]);
e.Data.RequestedOperation = DataPackageOperation.Move;
e.Data.SetDataProvider("FeedItem", request => request.SetData(item));
}
private async void ListB_Drop(object sender, DragEventArgs e) {
DataPackageView view = e.Data.GetView();
if (view.Contains("FeedItem") && view.RequestedOperation == DataPackageOperation.Copy) {
//item from ListA
}
if (view.Contains("FeedItem") && view.RequestedOperation == DataPackageOperation.Move) {
//item from ListB
}
}
Ok, problem solved :)
To use drag and drop on the same UI element (in this case a ListView) you need to disable the CanReorderItems option. Then the code I provided above will work.
ListB.CanReorderItems = false;
But when you wish to keep the option to reorder the items in ListB (actually my original question) you can subscribe to the follwoing event:
view.VectorChanged += viewVectorChanged;
...
private void viewVectorChanged(Windows.Foundation.Collections.IObservableVector<object> sender, Windows.Foundation.Collections.IVectorChangedEventArgs #event) {
}
Since my list is bound to a CollectionViewSource this works fine for me. Otherwise ListView should have a similar event that you could subscribe.

How to use Virtual mode in ListView?

i'm using VirtualMode to fill the columns like
List<ListViewItem> m_lstItem;
private void Form1_Load(object sender, EventArgs e)
{
m_lstItem = Enumerable.Range(0, 100000).Select(X => new ListViewItem(new String[] { X.ToString(), (X + 1).ToString() })).ToList();
listView1.VirtualListSize = m_lstItem.Count;
}
private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
e.Item = m_lstItem[e.ItemIndex];
}
but i can not access the selected item. while accessing the selected item its throwing an error like Cannot access the selected items collection when the ListView is in virtual mode.
How do i get the selected items from the listView when it is in VirtualMode
Please help me to do this.
From MSDN:
In virtual mode, the Items collection is disabled. Attempting to access it results in an InvalidOperationException. The same is true of the CheckedItems collection and the SelectedItems collection. If you want to retrieve the selected or checked items, use the SelectedIndices and CheckedIndices collections instead.
The Items collection is not available as an iterable collection in Virtual Mode but it is always possible to access a single element using Items(SelectedIndices(0)). I found that it works also using FULLROWSELECT. The problem is referenced on another page of this same site: Cannot access the selected items collection when the ListView is in virtual mode?
For some reason the SelectedIndices were always invalid when I tried to use them, maybe because of using FULLROWSELECT.
The selected item was available however even if the documentation wasn't clear. I found it with the ItemSelectionChanged event handler as e.ItemIndex.
Hope this might be helpful to someone else.

Categories

Resources