MenuBase, от которой происходят ContextMenu и Menu, наследует ItemsControl, который не включает концепцию SelectedItem. Это то, что добавляет ListBox.
Однако у вас есть ItemsControl.ItemTemplate. Что круто.
Один из вариантов - сделать ItemTemplate кнопкой ToggleButton. Это дает вам пару вещей. По сути, ToggleButtons могут выглядеть так, как будто они выбраны с использованием их свойства IsChecked. Во-вторых, у них есть свойство Command, которое вы можете привязать к команде в вашей ViewModel.
Итак, если у вас есть что-то вроде:
<Menu ItemsSource="{Binding ThingsToBindTo}">
<Menu.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.Resources>
<conv:BindingProxy x:Key="proxy" Data="{Binding}" />
</Grid.Resources>
<ToggleButton Content="{Binding NameOrLabel}" CommandParameter="{Binding}" Command="{Binding Path=DataContext.SelectThingCommand, RelativeSource={RelativeSource AncestorType=Menu}}" >
<ToggleButton.IsChecked>
<Binding Mode="OneWay" Path="DataContext.SelectedThing" RelativeSource={RelativeSource AncestorType=Menu}">
<Binding.Converter>
<conv:ComparisonConverter CompareTo="{Binding Source={StaticResource proxy}, Path=Data}" />
</Binding.Converter>
</Binding>
</ToggleButton.IsChecked>
</ToggleButton>
</Grid>
</DataTemplate>
</Menu.ItemTemplate>
</Menu>
Так что это немного сложно.
Как обычно, вы привязываетесь к списку предметов. ThingsToBindTo должен быть тем, чем является ваш список. Затем вы начинаете определять свой шаблон. NameOrLabel - это любое свойство, которое вы хотите отобразить на кнопке переключения. Параметр команды связывает элемент данных, который оборачивает шаблон, используя не более чем «{Binding}». На самом деле команда находится в DataContext вашего меню, поэтому здесь используется RelativeSource.
Что это говорит о том, что вы собираетесь передать команду, которую только что щелкнули. По сути, вы выбираете кнопку, которую нажимаете. Затем вашей команде просто нужно установить свойство SelectedThing в вашей ViewModel равным тому, что ему передано. Надеюсь, вы реализовали класс, который реализует ICommand для создания ваших команд делегата. Если вы этого не сделаете, есть много статей о том, как это сделать. Если вы не знаете как, оставьте комментарий к этому сообщению, и я добавлю исходный код, чтобы сделать это.
Тогда у нас есть плохой мальчик "IsChecked". Мы на самом деле делаем связующую длинную руку там. Это более сложная часть, но она позволяет элементу DataTemplated фактически связываться с самим собой в конвертере.
Во-первых, вам нужен прокси-объект, который объясняется здесь:
http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
Очень просто реализовать. Как только это будет сделано, ресурс BindingProxy в вашей сетке начнет работать и может выступать в роли привязки к элементу, связанному с DataTemplate. Связанная статья объясняет почему.
Затем вам нужен конвертер, который сравнивает два объекта друг с другом.
public class ComparisonConverter : DependencyObject, IValueConverter
{
public object CompareTo
{
get { return (object)GetValue(CompareToProperty); }
set { SetValue(CompareToProperty, value); }
}
public static readonly DependencyProperty CompareToProperty =
DependencyProperty.Register("CompareTo", typeof(object), typeof(ComparisonConverter), new UIPropertyMetadata(null));
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (CompareTo != null)
{
return CompareTo.Equals(value);
}
else
{
return false;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Так что теперь эта привязка будет брать выбранный элемент из DataContext меню и сравнивать его с тем, с чем связана ToggleButton. Если два объекта совпадают, кнопка появляется нажатой / выбранной. Если они не совпадают, кнопка не выглядит выделенной.
Так что у меня есть BindingProxy и мой конвертер в одном и том же пространстве имен. Вам не обязательно делать это. У меня просто обычно есть пространство имен для классов "Xaml Trick", которые я должен программировать.
Это много, чтобы усвоить, и я рад что-нибудь прояснить.
Еще одна вещь ... если вам не нравится внешний вид "ToggleButton", вы всегда можете придать им стиль, чтобы они выглядели совершенно по-другому. Единственное, что покупает ToggleButton, это свойство IsChecked и свойство Command. Вы можете сделать так, чтобы ContentTemplate выглядел как угодно, что дает вам большую свободу в оформлении меню.