Как лучше всего создать в MVVM меню, отображающее различные страницы? - PullRequest
8 голосов
/ 19 июня 2009

Я хочу создать простое приложение с шаблоном MVVM.

Это приложение будет состоять из двух основных частей:

  • меню сверху
  • содержание ниже

Навигация будет простой:

  • каждый пункт меню (например, «Управление клиентами» или «Просмотр отчетов») заполнит область содержимого новой страницей, имеющей определенные функции

Я сделал это ранее с кодом позади , где обработчик событий с выделенным кодом для пунктов меню загружал все страницы, а тот, который должен отображаться, был загружен как дочерний элемент StackPanel. Это, однако, не будет работать в MVVM, поскольку вы не хотите вручную заполнять StackPanel, а отображаете, например, объект PageItem с DataTemplate и т. д.

Итак, те из вас, кто сделал простое приложение с меню кликов, например, с помощью MVVM, какова была ваша базовая структура приложения? Я думаю по этому поводу:

MainView.xaml:

<DockPanel LastChildFill="False">

    <Menu 
        ItemsSource="{Binding PageItemsMainMenu}" 
        ItemTemplate="{StaticResource MainMenuStyle}"/>

    <ContentControl 
        Content="{Binding SelectedPageItem}"/>        

</DockPanel>

, где Меню заполнено коллекцией «PageItems», а DataTemplate отображает заголовок каждого «объекта PageItem» в качестве заголовка каждого MenuItem.

И ContentControl будет заполнен парой View / ViewModel, которая имеет полную функциональность, но не уверен в этом.

Ответы [ 3 ]

9 голосов
/ 20 июня 2009

Во-первых, я думаю, что вы должны сохранить обработчик событий code-behind, нет смысла менять простой двухстрочный обработчик событий на сложного командного монстра без какой-либо практической причины (и не говорите о тестируемости, это главное меню, оно будет проверяться при каждом запуске приложения).

Теперь, если вы хотите пройти чистый маршрут MVVM, все, что вам нужно сделать, чтобы заставить ваше меню запустить команду, сначала, в некотором разделе ресурсов, добавить этот стиль:

<Style x:Key="MenuItemStyle" TargetType="MenuItem">
    <Setter Property="Command" 
            Value="{Binding DataContext.SwitchViewCommand,
            RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"/>
    <Setter Property="CommandParameter" 
            Value="{Binding}"/>
</Style>

Этот стиль заставит пункт меню запустить команду SwitchViewCommand на присоединенной модели представления с DataContext MenuItem в качестве параметра команды.

Фактическое представление совпадает с вашим кодом с дополнительной ссылкой на этот стиль, как ItemContainerStyle (поэтому он применяется к элементу меню, а не к содержимому DataTemplate):

<DockPanel LastChildFill="False">

    <Menu DockPanel.Dock="Top"
        ItemsSource="{Binding PageItemsMainMenu}" 
        ItemTemplate="{StaticResource MainMenuStyle}"
        ItemContainerStyle="{StaticResource MenuItemStyle}"/>
    <ContentControl 
    Content="{Binding SelectedPageItem}"/>
</DockPanel>

Теперь в нужной модели представления (я использовал строки, потому что у меня нет вашего кода PageItem):

private string _selectedViewItem;
public List<string> PageItemsMainMenu { get; set; }
public string SelectedPageItem
{
    get { return _selectedViewItem; }
    set { _selectedViewItem = value; OnNotifyPropertyChanged("SelectedPageItem"); }
}
public ICommand SwitchViewCommand { get; set; }

И используйте любой класс команд, который вы используете, чтобы команда вызывала этот код:

private void DoSwitchViewCommand(object parameter)
{
    SelectedPageItem = (string)parameter;
}

Теперь, когда пользователь щелкает пункт меню, этот пункт меню вызывает SwitchViewCommand с элементом страницы в качестве параметра.

Команда вызовет DoSwitchViewCommand, который установит свойство SelectedPageItem

Свойство вызовет NotifyPropertyChanged, который будет обновлять пользовательский интерфейс посредством привязки данных.

Или вы можете написать 2-строчный обработчик событий на ваш выбор

0 голосов
/ 20 июня 2009

Другой вариант - использовать ListBox вместо меню, стилизовать ListBox, чтобы он выглядел как меню, а затем вы можете привязать к выбранному значению, например так:

<DockPanel LastChildFill="False">

    <ListBox 
        ItemsSource="{Binding PageItemsMainMenu}" 
        ItemTemplate="{StaticResource MainMenuStyle}"
        IsSynchronizedWithCurrentItem="True"/>

    <ContentControl 
        Content="{Binding PageItemsMainMenu/}"/>        

</DockPanel>

Обратите внимание на IsSynchronizedWithCurrentItem = "True", чтобы установить выбранный элемент, и {Binding PageItemsMainMenu /} с косой чертой для его использования.

0 голосов
/ 20 июня 2009

я мог бы представить ObservableCollection в виртуальной машине, которая содержит все страницы, которые можно вызывать из меню. Затем привяжите к нему ItemsControl And ContentControl, чтобы ContentControl всегда отображал CurrentItem из этого списка. Конечно, меню будет привязано только к некоторому свойству Title тогда как ContentControl примет весь элемент и подключит некоторое соответствующее представление в соответствии с типом.

...