web-dev-qa-db-fra.com

WPF Liaison d'événements d'interface utilisateur à des commandes dans ViewModel

Je suis en train de refactoriser une application simple pour suivre MVVM et ma question est de savoir comment déplacer un événement SelectionChanged de mon code vers le viewModel. J'ai examiné quelques exemples d'éléments de liaison aux commandes, mais je ne les ai pas bien compris. Quelqu'un peut-il aider avec cela. Merci!

Quelqu'un peut-il fournir une solution en utilisant le code ci-dessous? Merci beaucoup!  

public partial class MyAppView : Window 
{
    public MyAppView()
    {
        InitializeComponent();

        this.DataContext = new MyAppViewModel ();

        // Insert code required on object creation below this point.
    }

    private void contactsList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        //TODO: Add event handler implementation here.           
        //for each selected contact get the labels and put in collection 

        ObservableCollection<AggregatedLabelModel> contactListLabels = new ObservableCollection<AggregatedLabelModel>();

        foreach (ContactListModel contactList in contactsList.SelectedItems)
        {
            foreach (AggregatedLabelModel aggLabel in contactList.AggLabels)
            {
                contactListLabels.Add(aggLabel);
            }
        }
        //aggregate the contactListLabels by name
        ListCollectionView selectedLabelsView = new ListCollectionView(contactListLabels);

        selectedLabelsView.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
        tagsList.ItemsSource = selectedLabelsView.Groups;
    }
}
44
Ben

Vous devez utiliser une variable EventTrigger en combinaison avec InvokeCommandAction de l'espace de noms Windows.Interactivity. Voici un exemple:

<ListBox ...>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>

Vous pouvez référencer System.Windows.Interactivity en allant Add reference > Assemblies > Extensions

Et l’espace de nom i complet est: xmlns:i="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity".

92
Pavlo Glazkov

Cette question a un problème similaire.

WPF MVVM: Les commandes sont faciles. Comment connecter View et ViewModel avec RoutedEvent

La façon dont je traite ce problème consiste à avoir une propriété SelectedItem dans le ViewModel, puis à lier le SelectedItem de votre ListBox ou quoi que ce soit à cette propriété.

9
Cameron MacFarland

Votre meilleur pari utilise Windows.Interactivity. Utilisez EventTriggers pour associer une ICommand à une RoutedEvent.

Voici un article pour vous aider à démarrer: Comportements et déclencheurs de Silverlight et WPF

7
decyclone

Pour reformuler cela, vous devez changer votre façon de penser. Vous ne gérez plus un événement "sélection modifiée", mais stockez l'élément sélectionné dans votre modèle de vue. Vous utiliseriez ensuite une liaison de données bidirectionnelle de sorte que lorsque l'utilisateur sélectionne un élément, votre modèle de vue soit mis à jour et que, lorsque vous modifiez l'élément sélectionné, votre vue soit mise à jour.

7
M. Dudley
<ListBox SelectionChanged="{eb:EventBinding Command=SelectedItemChangedCommand, CommandParameter=$e}">

</ListBox>

Commande

{eb: EventBinding} (modèle de dénomination simple pour rechercher la commande)

{eb: EventBinding Command = NomCommande}

CommandParameter

$ e (EventAgrs)

$ this ou $ this.Property

chaîne

https://github.com/JonghoL/EventBindingMarkup

2
jongho

Je suivrais la réponse dans cette question

Fondamentalement, votre modèle de vue contiendra une liste de tous vos éléments et une liste des éléments sélectionnés. Vous pouvez ensuite attacher un comportement à votre zone de liste qui gère votre liste d'éléments sélectionnés.

Cela signifie que vous n'avez rien dans le code derrière et que xaml est assez facile à suivre. Le comportement peut également être réutilisé ailleurs dans votre application.

<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" />
1
Tom Dudfield

Parfois, la solution d'un événement de liaison à une commande via le déclencheur d'interactivité ne fonctionne pas, lorsqu'il est nécessaire de lier l'événement de usercontrol personnalisé . Dans ce cas, vous pouvez utiliser un comportement personnalisé.

Déclarez un comportement contraignant comme:

public class PageChangedBehavior
{
    #region Attached property

    public static ICommand PageChangedCommand(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(PageChangedCommandProperty);
    }
    public static void SetPageChangedCommand(DependencyObject obj, ICommand value)
    {
        obj.SetValue(PageChangedCommandProperty, value);
    }

    public static readonly DependencyProperty PageChangedCommandProperty =
        DependencyProperty.RegisterAttached("PageChangedCommand", typeof(ICommand), typeof(PageChangedBehavior),
            new PropertyMetadata(null, OnPageChanged));

    #endregion

    #region Attached property handler

    private static void OnPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as PageControl;
        if (control != null)
        {
            if (e.NewValue != null)
            {
                control.PageChanged += PageControl_PageChanged;
            }
            else
            {
                control.PageChanged -= PageControl_PageChanged;
            }
        }
    }

    static void PageControl_PageChanged(object sender, int page)
    {
        ICommand command = PageChangedCommand(sender as DependencyObject);

        if (command != null)
        {
            command.Execute(page);
        }
    }

    #endregion

}

Et ensuite, liez-le à la commande en xaml:

        <controls:PageControl
            Grid.Row="2"
            CurrentPage="{Binding Path=UsersSearchModel.Page,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            PerPage="{Binding Path=UsersSearchModel.PageSize,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            Count="{Binding Path=UsersSearchModel.SearchResults.TotalItemCount}"
            behaviors:PageChangedBehavior.PageChangedCommand="{Binding PageChangedCommand}">
        </controls:PageControl>
1
Fragment

Considérez Microsoft.Xaml.Behaviors.Wpf , son propriétaire est Microsoft que vous pouvez voir dans cette page.

System.Windows.Interactivity.WPF propriétaire est mthamil, tout le monde peut me dire s'il est fiable?

Exemple de Microsoft.Xaml.Behaviors.Wpf:

<UserControl ...
             xmlns:behaviors="http://schemas.Microsoft.com/xaml/behaviors"
             ...>

<Button x:Name="button">
    <behaviors:Interaction.Triggers>
        <behaviors:EventTrigger EventName="Click" SourceObject="{Binding ElementName=button}">
            <behaviors:InvokeCommandAction Command="{Binding ClickCommand}" />
        </behaviors:EventTrigger>
    </behaviors:Interaction.Triggers>
</Button>

</UserControl>
1
ifeng

Comme @Cameron MacFarland le mentionne, je voudrais simplement une liaison bidirectionnelle à une propriété du viewModel. Dans le configurateur de propriétés, vous pouvez utiliser la logique dont vous avez besoin, telle que l'ajout à une liste de contacts, en fonction de vos besoins. 

Cependant, je n'appellerais pas nécessairement la propriété 'SelectedItem', car viewModel ne devrait pas connaître le calque de vue et son interaction avec ses propriétés. Je l'appellerais quelque chose comme CurrentContact ou quelque chose comme ça. 

Évidemment, c’est à moins que vous souhaitiez simplement créer des commandes comme exercice à pratiquer, etc.

0
HAdes