Quelle est la meilleure manière dans MVVM de créer un menu qui affiche différentes pages?

StackOverflow https://stackoverflow.com/questions/1018483

  •  06-07-2019
  •  | 
  •  

Question

Je souhaite créer une application simple avec le modèle MVVM.

Cette application comportera deux parties principales:

  • menu en haut de la page
  • contenu ci-dessous

La navigation sera simple:

  • chaque élément de menu (par exemple, "Gérer les clients" ou "Afficher les rapports") remplira la zone de contenu avec une nouvelle page dotée de fonctionnalités particulières

J'ai déjà fait cela auparavant avec le code derrière , où le gestionnaire d'événements code-behind pour les éléments de menu avait chargé toutes les pages et celle à afficher était chargée en tant qu'enfant de StackPanel. Toutefois, cela ne fonctionnera pas dans MVVM car vous ne voulez pas remplir manuellement un StackPanel mais afficher, par exemple, un " PageItem " objet avec un DataTemplate, etc.

Alors, ceux d'entre vous qui ont créé une application de menu contextuel simple comme celle-ci avec MVVM, quelle était votre structure d'application de base? Je réfléchis à ces lignes:

MainView.xaml:

<DockPanel LastChildFill="False">

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

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

</DockPanel>

où le menu est rempli avec une collection de "PageItems". et le DataTemplate affiche le titre de chaque " objet PageItem " comme en-tête de chaque élément de menu.

Et le ContentControl sera rempli avec une paire View / ViewModel qui a toutes les fonctionnalités, mais je ne suis pas sûr à ce sujet.

Était-ce utile?

La solution

Tout d’abord, je pense que vous devriez conserver le gestionnaire d’événements code-behind, il est inutile de changer un gestionnaire d’événements simple à 2 lignes en un monstre complexe commandé par des commandes sans raison pratique (et ne dites pas testebility, c’est le principal menu, il sera testé chaque fois que vous exécuterez l'application).

Maintenant, si vous voulez utiliser la route MVVM pure, il vous suffit de le faire pour que votre menu envoie une commande, commencez par ajouter ce style à une section de la ressource:

<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>

Ce style obligera l'élément de menu à déclencher un SwitchViewCommand sur le modèle de vue attaché avec le DataContext de MenuItem comme paramètre de commande.

La vue réelle est identique à votre code avec une référence supplémentaire à ce style comme ItemContainerStyle (elle s'applique donc à l'élément de menu et non au contenu du DataTemplate):

<DockPanel LastChildFill="False">

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

Maintenant, dans le modèle de vue dont vous avez besoin (j'ai utilisé des chaînes parce que je n'ai pas votre code 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; }

Et utilisez la classe de commande que vous utilisez pour que la commande appelle ce code:

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

Désormais, lorsque l'utilisateur clique sur un élément de menu, celui-ci appelle le commutateur SwitchViewCommand avec l'élément de page comme paramètre.

La commande appellera DoSwitchViewCommand qui définira la propriété SelectedPageItem

La propriété déclenchera le NotifyPropertyChanged qui fera la mise à jour de l'interface utilisateur via la liaison de données.

Vous pouvez également écrire un gestionnaire d'événements sur 2 lignes, à votre choix

Autres conseils

Je pourrais imaginer une ObservableCollection dans la VM, qui contiendrait toutes les pages pouvant être appelées à partir du menu. Liez ensuite un ItemsControl et le ContentControl à celui-ci pour que ContentControl affiche toujours le CurrentItem de cette liste. Bien sûr, le menu ne liera qu’à certaines propriétés Title.  alors que ContentControl adoptera l’ensemble de l’article et ajoutera une vue appropriée en fonction du type.

Une autre option consiste à utiliser un contrôle ListBox au lieu d'un menu, attribuez à ce dernier un style ressemblant à un menu, puis vous pouvez vous lier à la valeur sélectionnée, comme suit:

<DockPanel LastChildFill="False">

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

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

</DockPanel>

Notez IsSynchronizedWithCurrentItem = " True " pour définir l'élément sélectionné et {Binding PageItemsMainMenu /} avec la barre oblique finale pour l'utiliser.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top