Question

I am using MVVM Light in a Windows Phone 7 (7.1 using the wp8 SDK and VS Ultimate 2012) application that asynchroneously retrieves data from a web service application. I use RelayCommands on each page that execute async methods to get the data then navigate to the next page. For example, in one of my ViewModels, I declare the following ICommand :

public ICommand ShowTreatmentDetailsCommand { get; set; }

then, in the VM's constructor, I assign it this way :

ShowTreatmentDetailsCommand = new RelayCommand(ShowTreatmentDetails);

and here's the method called by that command :

private async void ShowTreatmentDetails()
{
    try
    {
        Treatment refreshedTreatment = await treatmentService.LoadSingle(SelectedTreatment.id, LoggedUser.logon, LoggedUser.pwHash);

        if (refreshedTreatment != null)
        {
            DrugGroup anaestGroup = null;
            DrugGroup surgGroup = null;

            IEnumerable<DrugGroup> groups = await drugGroupService.Load(
                refreshedTreatment.id,
                LoggedUser.logon,
                LoggedUser.pwHash);

            anaestGroup = groups
                .Where(g => g.type == DrugType.Anaesthetic)
                .SingleOrDefault<DrugGroup>();

            surgGroup = groups
                .Where(g => g.type == DrugType.Surgical)
                .SingleOrDefault<DrugGroup>();

            Dictionary<string, object> parameters = new Dictionary<string, object>();
            parameters.Add(Keys.AnaestDrugGroup, anaestGroup);
            parameters.Add(Keys.SurgDrugGroup, surgGroup);
            parameters.Add(Keys.SelectedTreatment, refreshedTreatment);

            Messenger.Default.Send(parameters);
        }
        else
        {
            // Display error message
        }

        RefreshData();
    }
    catch (NullReferenceException) { }
}

This command is called from the View's xaml code when an item is selected from a ListBox, using an EventTrigger and the EventToCommand class (but the problem stays the same with button-related commands. Just in case, here's my ListBox element :

<ListBox x:Name="lbxTreatmentList"
         ItemsSource="{Binding Treatments}"
         SelectedItem="{Binding SelectedTreatment, Mode=TwoWay}">
    <int:Interaction.Triggers>
        <int:EventTrigger EventName="SelectionChanged">
            <com:EventToCommand Command="{Binding ShowTreatmentDetailsCommand}"
                                PassEventArgsToCommand="True" />
        </int:EventTrigger>
    </int:Interaction.Triggers>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <custom:TreatmentListItem PatientName="{Binding patient}"
                                      OpeDescription="{Binding description}"
                                      StartedAt="{Binding startedAt}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

And another exaple with a button :

<Button Grid.Row="2"
    HorizontalAlignment="Center"
    Foreground="{StaticResource BeldicoBlue}"
    BorderBrush="{StaticResource BeldicoBlue}"
    Margin="0,12,0,0"
    Padding="24,12"
    Command="{Binding ValidateCommand}">
    <TextBlock Text="Validate this treatment" FontWeight="ExtraBold" />
</Button>

The problem is that, each time the command is fired, the related method gets executed an increasing number of times. I.e. : once at the first call, then twice, then three, four, five... times. Which quickly becomes bandwidth and time consuming, as there is an async service call in the method.

I definitely don't get the reason behind this behavior, can anyone help ?

Était-ce utile?

La solution

There are couple of possible things going on here:

  1. You're creating multiple viewmodel instances attached to the page and each one is executing the command.

    Or, more likely:

  2. You're not fully accounting for how the SelectionChanged event works. This event can be triggered multiple times when the selection changes. If there is a selected item already, the changed event will fire when that item is removed and then again when the new selected item is added.
    If you were using the event handler directly you could query the SelectionChangedEventArgs directly to determine what has triggered the event. Your code is ignoring these args though, even though your xaml says you're passing them.

If you change your Action so that it supports these args you could add the check there.
e.g.

ShowTreatmentDetailsCommand =
     new RelayCommand<SelectionChangedEventArgs>(ShowTreatmentDetails);

and

private async void ShowTreatmentDetails(SelectionChangedEventArgs args)
{
    if (args.AddedItems.Count >= 1)
    {
        // your code
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top