+1  A: 

Since the SelectedItemChanged event is triggered after the SelectedItem has already changed, you can't really cancel the event at this point.

What you can do is listen for mouse-clicks and cancel them before the SelectedItem gets changed.

Eclipse
+2  A: 

Instead of selecting for Selected/Unselected, a better route might be to hook into PreviewMouseDown. The preblem with handling a Selected and Unselected event is that the event has already occurred when you receive the notification. There is nothing to cancel because it's already happened.

On the other hand, Preview events are cancelable. It's not the exact event you want but it does give you the oppuritunity to prevent the user from selecting a different node.

JaredPar
What about when they use the keyboard? Touch? Stylus? Do I need to handle all those as well?
Ray
+2  A: 

You can't cancel the event like you can, for example, a Closing event. But you can undo it if you cache the last selected value. The secret is you have to change the selection without re-firing the SelectionChanged event. Here's an example:

    private object _LastSelection = null;
    private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (IsUpdated)
        {
            MessageBoxResult result = MessageBox.Show("The current record has been modified. Are you sure you want to navigate away? Click Cancel to continue editing. If you click OK all changes will be lost.", "Warning", MessageBoxButton.OKCancel, MessageBoxImage.Hand);
            switch (result)
            {
                case MessageBoxResult.Cancel:
                    e.Handled = true;
                    // disable event so this doesn't go into an infinite loop when the selection is changed to the cached value
                    PersonListView.SelectionChanged -= new SelectionChangedEventHandler(OnSelectionChanged);
                    PersonListView.SelectedItem = _LastSelection;
                    PersonListView.SelectionChanged += new SelectionChangedEventHandler(OnSelectionChanged);
                    return;
                case MessageBoxResult.OK:
     // revert the object to the original state
                    LocalDataContext.Persons.GetOriginalEntityState(_LastSelection).CopyTo(_LastSelection);
                    IsUpdated = false;
                    Refresh();
                    break;
                default:
                    throw new ApplicationException("Invalid response.");
            }
        }

  // cache the selected item for undo
        _LastSelection = PersonListView.SelectedItem;
    }
AndyM
I've tried something like this (but with the TreeView, not the list view in your example). And after I've re-enabled the event, it fires again, trying to change to the new item.
Ray
Also with TreeView's you can't set the selected item like that, you need to set IsSelected on the item.
Ray
+1  A: 

CAMS_ARIES:

XAML:

code :

  private bool ManejarSeleccionNodoArbol(Object origen)
    {
        return true;  // with true, the selected nodo don't change
        return false // with false, the selected nodo change
    }


    private void Arbol_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {            
        if (e.Source is TreeViewItem)
        {
            e.Handled = ManejarSeleccionNodoArbol(e.Source);
        }
    }

    private void Arbol_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Source is TreeViewItem)
        {
           e.Handled=ManejarSeleccionNodoArbol(e.Source);
        }
    }
+1  A: 

UPDATE

Realized I could put the logic in SelectedItemChanged instead. A little cleaner solution.

Xaml

<TreeView Name="c_treeView"
          SelectedItemChanged="c_treeView_SelectedItemChanged">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

Code behind. I have some classes that is my ItemsSource of the TreeView so I made an interface (MyInterface) that exposes the IsSelected property for all of them.

private MyInterface m_selectedTreeViewItem = null;
private void c_treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
    if (m_selectedTreeViewItem != null)
    {
        if (e.NewValue == m_selectedTreeViewItem)
        {
            // Will only end up here when reversing item
            // Without this line childs can't be selected
            // twice if "No" was pressed in the question..   
            c_treeView.Focus();   
        }
        else
        {
            if (MessageBox.Show("Change TreeViewItem?",
                                "Really change",
                                MessageBoxButton.YesNo,
                                MessageBoxImage.Question) != MessageBoxResult.Yes)
            {
                EventHandler eventHandler = null;
                eventHandler = new EventHandler(delegate
                {
                    c_treeView.LayoutUpdated -= eventHandler;
                    m_selectedTreeViewItem.IsSelected = true;
                });
                // Will be fired after SelectedItemChanged, to early to change back here
                c_treeView.LayoutUpdated += eventHandler;
            }   
            else
            {
                m_selectedTreeViewItem = e.NewValue as MyInterface;
            }        
        }
    }
    else
    {
        m_selectedTreeViewItem = e.NewValue as MyInterface;
    }
}

I haven't found any situation where it doesn't revert back to the previous item upon pressing "No".

Meleak