I am using the following code in my viewmodel to delete items out of a collection:
UnitMeasureCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(ListOfUnitMeasureCollectionChanged);
void ListOfUnitMeasureCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
if (NavigationActions.DeleteConfirmation("Delete Item.", "Are you sure you want to delete this item? This action cannot be undone."))
{
foreach (UnitMeasureBO item in e.OldItems)
{
UnitMeasureBO unitMeasureBO = item as UnitMeasureBO;
bool inUse = unitMeasureRepository.UnitMeasureInUse(unitMeasureBO.UnitMeasureValue);
if (inUse == true)
{
NavigationActions.ShowError("Cannot delete item", "This item cannot be deleted because it is used elsewhere in the application.");
}
else
{
unitMeasureRepository.DeleteUnitMeasure(unitMeasureBO.UnitMeasureValue);
}
}
}
}
}
I have a datagrid that is bound to the collection. I am wondering if there is anyway of canceling the remove action based on the confirmation prompt? I noticed NotifyCollectionChangedEventArgs does not have a cancel method. What happens is when a user deletes an item out of the datagrid but chooses 'no' on the confirmation, the item is still removed from the datagrid. It isn't deleted from the database and if the datagrid is refreshed it will appear again. I am using the mvvm pattern and I prefer to do this without having to code my datagrid. Any help is appreciated.
Well, you can't cancel a remove action during a CollectionChanged event.
My suggestion: if you're using MVVM, you should have a DeleteCommand somewhere that is triggered when the DeleteKey is pressed in the DataGrid. In the Execute() method of this command, you should:
Ask the confirmation.
If user chooses yes, then remove the item from the collection. This removal should directly be reflected on the DataGrid.
If user chooses no, do nothing.
This means, though that the DataGrid.CanUserDeleteRows is set to False since you basically have to control when the rows get deleted.
Hope this helps.
Related
I have a project where I have a ComboBox interacting with 2 Sliders.
Each o these 3 controls are triggered by events: namely a SelectionChanged for the ComboBox and a ValueChanged for the 2 sliders.
I thought the ValueChanged event was giving me problems, not updating the values and min/max accordingly to the combobox selection. For some reason I assumed that maybe both events were triggered simultaneously resulting in a mix up of my variables.
Well, I decided to change my ValueChanged events so that they only update a label so see if that fixed the problem. It did not.
In other words, the SelectionChanged event is where my problem is lying.
Looking through my code I didn't see any problem, and at this point the only thing I can think of is as follow:
private void chanList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch (chanList.Text)
{
case "Channel 1":
{ // blablabla }
}
}
Reason for the problem is that chanList.Text doesn't contain the "new selection", it still contains the old one. I am guessing that when the "SelectionChanged" event is triggered, the content of the of combobox (in my case chanList.Text) is not yet "updated". So it results in picking up the wrong case in my switch.
Now my questions:
1. are my assumptions correct?
2. if so, what should I replace my Switch test by? Assuming chanList.Text isn't updated yet, maybe going with something chanList.SelectedItem should be the way to go. However, I was able to find the correct verbose to access the content (text) of the selected item... That's why I was going with chanList.Text which has been working fine well at least until I started using that event.
Thanks for the help!
Steve
The combobox text will not be changed in the SelectionChanged event handler. The selections will be found in the SelectionChangedEventArgs object. Specifically e.AddedItems. It is possible, although perhaps not for your control, that the user will have selected multiple items in the combobox, so e.AddedItems is a list. Scroll through the list and make the updates necessary.
private void chanList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (object item in e.AddedItems)
{
if (item is string)
{
switch (item as string)
{
case "Channel 1":
Console.WriteLine("Channel 1");
break;
default:
break;
}
}
}
}
I have multiple ListViews in an MVVM WPF application, backed by ObservableCollections.
I'm implementing a "Delete Item" context menu item for each ListView. Currently I have the SelectedItem for each ListView bound to the same object in my ViewModel. However to delete the item from the ObservableCollection requires the name of the ListView (in this case Wk01CECollection):
private void DeleteLO()
{
this.Wk01CECollection.Remove(SelectedCE);
}
Is there a way to reference the ListView that the SelectedItem is a member of? As it is I'll need to wire up a separate delete method for each ListView.
I'm sure this is not the "best practice" way to achieve this, but it's quite simple:
You could pass the ListView's selected item as a parameter to the command:
<Button Content="Delete selected item" Command="{Binding DeleteCommand}" CommandParamenter="{Binding ElementName=SomeListView, Path=SelecteItem}" />
Since you can try to remove an item from an ObservableCollection even if it does not exist in the collection without getting an exception, in your private Delete method you can try to remove that item from all the ObservableCollections you have in your ViewModel. I'm assuming here that an item cannot be in two collections at the same time, if this is not the case, what I said won't work because you'll remove the item from all your collections. If you plan to do this, just check for null before removing, if you try to remove a null object you'll get an exception.
private void DeleteLO(object listitem)
{
if(listitem != null)
{
if(listitem as CollectionType1 != null) //cast your listitem to the appropiate type inside your collection
this.Wk01CECollection.Remove(listitem);
if(listitem as CollectionType2 != null)
this.Wk02CECollection.Remove(listitem);
//etc.
}
}
Besides all of this, if you are using MVVM it is best that the ViewModel does not know about the View, so referencing the ListView inside the VM would break that principle.
I understand the issue is long gone, but will post for googlers.
This worked for me:
private void LvPrevKeyDown(object sender, KeyEventArgs e)
{
//Nothing to do here
if (Lv.SelectedItems.Count == 0) return;
//Empty space for other key combinations
//Let's remove items. If it's a simple delete key so we'll remove just the selected items.
if (e.Key != Key.Delete || Keyboard.Modifiers != ModifierKeys.None) return;
var tmp = Lv.SelectedItems.Cast<ColorItem>().ToList();
foreach (var colorItem in tmp)
{
_cList.Remove(colorItem);
}
}
Nothing fancy in the xaml. Just some columns bound to _cList properties and items source bound to _cList, of course.
Hope it'll help someone!
Kind regards!
I have an ObservableCollection of "things" in my view model, and a couple filtered subsets of that list in additonal ObservableCollections. I have two DataGrids on the screen, and I have bound them each to one of the subset ObservableCollections.
Both DataGrids have their SelectedItem property bound to a SelectedThing property in the view model.
When I change SelectedThing either programatically or by selecting a row in one of the two grids, it will change as expected. If the item now pointed to by SelectedThing exists in a grid, the grid will update it's selected item.
So here is my problem... if SelectedThing does not exist in the grid's ItemSource, the selection acts like nothing happened and remains in whatever state it was in before SelectedThing was changed. Ideally I would like the selected to Clear if the underlying view model property no longer is set to something in the grid's ItemsSource... anyone have any suggestions?
Ok. Got it working. In case it helps someone else in the future, here's what made it work...
In your code behind, register an event handler for the view model's PropertyChanged event, and then use that to check each grid to see if it contains the item being selected. If not, then clear the selected in that grid. I also modified my SelectedThing property to ignore incoming NULL values to avoid a deadlock (and in my app it will never be NULL after initialization)
_vm is a Property that returns my view model.
_vm.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_vm_PropertyChanged);
void _vm_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedThing")
{
CheckSelection(grid1, _vm.SelectedThing);
CheckSelection(grid2, _vm.SelectedThing);
}
}
void CheckSelection(DataGrid grid, object selectedItem)
{
if (grid.ItemsSource != null)
{
bool itemInGrid = false;
foreach (var item in grid.ItemsSource)
{
if (item == selectedItem)
{
itemInGrid = true;
break;
}
}
if (!itemInGrid) // clear selection
{
grid.SelectedItem = null;
// not sure why, but this causes the highlight to clear. Doesn't work otherwise
grid.IsEnabled = false;
grid.IsEnabled = true;
}
}
}
When I populate a ListBox with RIA Services, an item is automatically selected. This triggers the SelectionChanged event. If I move the selection up or down with the arrow keys, the event also gets triggered.
I don't want this. I want the user to press enter or click the item for it to be selected. How do I accomplish this?
You could handle the MouseLeftButtonDown and KeyDown events for the ListBox. For the KeyDown event, you'll need to check the EventArgs to determine whether the Enter key was pressed (as opposed to any other key).
These events can fire even when an item is not selected (e.g., if the user clicks inside the ListBox but not over an actual item), so within your event handlers you should check for this.
Your event handlers might look something like this:
public void MyListBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ItemSelected();
}
public void MyListBox_KeyDown(object sender, KeyEventArgs e)
{
if ((e.Key & Key.Enter) == Key.Enter)
{
ItemSelected();
}
}
public void ItemSelected()
{
if (MyListBox.SelectedItem != null)
{
// Handle item selection here
}
}
These are off the top of my head, so you may need to tweak these slightly to get them to work exactly right. Hopefully you see the general idea though.
Another way to do it would be to simply remove the SelectionChanged event handler when populating the ListBox with items (use the "-=" syntax), then re-attach it once this operation is complete.
I'd recommend doing it this way (since you're concerned about the event firing when the list is populated). It wouldn't stop the users from selecting items using the Up and Down arrow keys, but unless you have a really good reason for doing so you're making things unnecessary inconvenient (users don't want to be arbitrarily restricted from doing things that ought to work).
For a ListView how can you make it so you CAN'T deselect the selected index by holding the control button while you select any item?
Thank you very much
Subscribe to the PreviewMouseButtonDown event on the ListView. In that event handler you can catch when the user ctrl-clicks and mark the event has handled. Then it won't be passed on.
As andrea pointed out they can do unselect through shortcut keys as well. Instead I think you should subscribe to the SelectionChangedEvent. You can then loop through the removed items and remark them as selected:
void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.RemovedItems)
{
myList.SelectedItems.Add(item);
}
}