Выбор текущего элемента меню в wpf - PullRequest
2 голосов
/ 25 января 2012

Как выбрать текущий элемент в коллекции элементов меню. Как и в случае со списком. Я попытался обернуть коллекцию в collectionViewSource, однако это не принесло такой удачи.

Заранее спасибо.

Ответы [ 3 ]

2 голосов
/ 25 января 2012

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 выглядел как угодно, что дает вам большую свободу в оформлении меню.

0 голосов
/ 25 января 2012

Если у вас есть свойство в вашем контекстном файле (например, файл Codebehind или ViewModel), которое представляет currentSelectedItem, тогда вы можете написать следующее в xaml:

<ListView x:Name="MyList"
          ItemsSource="MySource"
          SelectedItem="{Binding Path=MyCurrentSelectedItem}" IsSynchronizedWithCurrentItem="True">

Codebehind / ViewModel

public MyType MyCurrentSelectedItem { get; set; }
0 голосов
/ 25 января 2012

Если ListBox имеет свой ItemsSource, установленный на общий список сложной сущности, то с помощью ListBox.SelectedValue вы получите текущие выбранные данные.

Например:

public partial class NameListView : Window
{
    /// <summary>
    /// Constructor
    /// </summary>
    public NameListView()
    {
        List<string> names = new List<string>();
        names.Add("John Doe");
        names.Add("Jane Doe");

        lbNameList.ItemsSource = names;
    }

    /// <summary>
    /// Selection changed event handler for ListBox lbNameList
    /// </summary>
    void lbNameList_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count > 0)
        {
            string currentValue = lbNameList.SelectedValue.ToString();
            MessageBox.Show("Currently selected value: " + currentValue);
        }
    }
}
...