WPF: Cancel a user selection in a databound ListBox?

前端 未结 8 788
独厮守ぢ
独厮守ぢ 2020-12-05 01:02

How do I cancel a user selection in a databound WPF ListBox? The source property is set correctly, but the ListBox selection is out of sync.

I have an MVVM app that

8条回答
  •  被撕碎了的回忆
    2020-12-05 01:26

    If you are serious about following MVVM and don't want any code behind, and also don't like the use of the Dispatcher, which frankly is not elegant either, the following solution works for me and is by far more elegant than most of the solutions provided here.

    It is based on the notion that in code behind you are able to stop the selection using the SelectionChanged event. Well now, if this is the case, why not create a behavior for it, and associate a command with the SelectionChanged event. In the viewmodel you can then easily remember the previous selected index and the current selected index. The trick is to have binding to your viewmodel on SelectedIndex and just let that one change whenever the selection changes. But immediately after the selection really has changed, the SelectionChanged event fires which now is notified via the command to your viewmodel. Because you remember the previously selected index, you can validate it and if not correct, you move the selected index back to the original value.

    The code for the behavior is as follows:

    public class ListBoxSelectionChangedBehavior : Behavior
    {
        public static readonly DependencyProperty CommandProperty 
            = DependencyProperty.Register("Command",
                                         typeof(ICommand),
                                         typeof(ListBoxSelectionChangedBehavior), 
                                         new PropertyMetadata());
    
        public static DependencyProperty CommandParameterProperty
            = DependencyProperty.Register("CommandParameter",
                                          typeof(object), 
                                          typeof(ListBoxSelectionChangedBehavior),
                                          new PropertyMetadata(null));
    
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
    
        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }
    
        protected override void OnAttached()
        {
            AssociatedObject.SelectionChanged += ListBoxOnSelectionChanged;
        }
    
        protected override void OnDetaching()
        {
            AssociatedObject.SelectionChanged -= ListBoxOnSelectionChanged;
        }
    
        private void ListBoxOnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Command.Execute(CommandParameter);
        }
    }
    

    Using it in XAML:

    
        
            
        
    
    

    The code that is appropriate in the viewmodel is as follows:

    public int SelectedTaskIndex
    {
        get { return _SelectedTaskIndex; }
        set { SetProperty(ref _SelectedTaskIndex, value); }
    }
    
    private void SelectionChanged()
    {
        if (_OldSelectedTaskIndex >= 0 && _SelectedTaskIndex != _OldSelectedTaskIndex)
        {
            if (Taken[_OldSelectedTaskIndex].IsDirty)
            {
                SelectedTaskIndex = _OldSelectedTaskIndex;
            }
        }
        else
        {
            _OldSelectedTaskIndex = _SelectedTaskIndex;
        }
    }
    
    public RelayCommand SelectionChangedCommand { get; private set; }
    

    In the constructor of the viewmodel:

    SelectionChangedCommand = new RelayCommand(SelectionChanged);
    

    RelayCommand is part of MVVM light. Google it if you don't know it. You need to refer to

    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    

    and hence you need to reference System.Windows.Interactivity.

提交回复
热议问题