Если вы серьезно относитесь к отслеживанию MVVM и не хотите иметь какой-либо код позади, а также не любите использовать Dispatcher
, что, честно говоря, тоже не изящно, следующее решение мне подходит и безусловно более элегантный, чем большинство решений, представленных здесь.
Он основан на том, что в коде позади вы можете остановить выбор, используя событие SelectionChanged
. Теперь, если это так, почему бы не создать для него поведение и не связать команду с событием SelectionChanged
. В модели представления вы можете легко запомнить предыдущий выбранный индекс и текущий выбранный индекс. Хитрость заключается в том, чтобы привязать вашу модель представления на SelectedIndex
и просто позволить этому изменяться всякий раз, когда изменяется выбор. Но сразу после того, как выбор действительно изменился, возникает событие SelectionChanged
, которое теперь сообщается с помощью команды вашей модели представления. Поскольку вы помните ранее выбранный индекс, вы можете проверить его и, если он не верный, переместить выбранный индекс обратно к исходному значению.
Код поведения выглядит следующим образом:
public class ListBoxSelectionChangedBehavior : Behavior<ListBox>
{
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);
}
}
Использование в XAML:
<ListBox x:Name="ListBox"
Margin="2,0,2,2"
ItemsSource="{Binding Taken}"
ItemContainerStyle="{StaticResource ContainerStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalContentAlignment="Stretch"
SelectedIndex="{Binding SelectedTaskIndex, Mode=TwoWay}">
<i:Interaction.Behaviors>
<b:ListBoxSelectionChangedBehavior Command="{Binding SelectionChangedCommand}"/>
</i:Interaction.Behaviors>
</ListBox>
Код, который подходит для модели представления, выглядит следующим образом:
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; }
В конструкторе модели представления:
SelectionChangedCommand = new RelayCommand(SelectionChanged);
RelayCommand
является частью MVVM light. Google это, если вы не знаете это.
Вы должны обратиться к
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
и, следовательно, вам нужно ссылаться на System.Windows.Interactivity
.