.net RelayCommands执行多次

.net RelayCommands执行多次,.net,windows-phone-7.1,mvvm-light,async-await,relaycommand,.net,Windows Phone 7.1,Mvvm Light,Async Await,Relaycommand,我在WindowsPhone7(7.1使用WP8SDK和VSUltimate 2012)应用程序中使用MVVMLight,该应用程序从web服务应用程序异步检索数据。我在每个执行异步方法的页面上使用RelayCommands来获取数据,然后导航到下一个页面。例如,在我的一个ViewModels中,我声明了以下ICommand: public ICommand ShowTreatmentDetailsCommand { get; set; } 然后,在VM的构造函数中,我按如下方式分配它: Sh

我在WindowsPhone7(7.1使用WP8SDK和VSUltimate 2012)应用程序中使用MVVMLight,该应用程序从web服务应用程序异步检索数据。我在每个执行异步方法的页面上使用RelayCommands来获取数据,然后导航到下一个页面。例如,在我的一个ViewModels中,我声明了以下ICommand:

public ICommand ShowTreatmentDetailsCommand { get; set; }
然后,在VM的构造函数中,我按如下方式分配它:

ShowTreatmentDetailsCommand = new RelayCommand(ShowTreatmentDetails);
下面是该命令调用的方法:

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) { }
}
private异步void showtreamentdetails()
{
尝试
{
治疗刷新治疗=等待治疗服务.LoadSingle(选择治疗.id,LoggedUser.logon,LoggedUser.pwHash);
if(刷新处理!=null)
{
药物组anaestGroup=null;
DrugGroup surgGroup=null;
IEnumerable groups=wait drugGroupService.Load(
refreshedTreatment.id,
LoggedUser.logon,
LoggedUser.pwHash);
麻醉组=组
.式中(g=>g.type==药物类型.麻醉剂)
.SingleOrDefault();
surgGroup=组
其中(g=>g.type==DrugType.Surgical)
.SingleOrDefault();
字典参数=新字典();
添加参数(key.AnaestDrugGroup,anaestGroup);
parameters.Add(key.SurgDrugGroup,surgGroup);
参数。添加(键。SelectedTreatment,refreshedTreatment);
Messenger.Default.Send(参数);
}
其他的
{
//显示错误消息
}
刷新数据();
}
捕获(NullReferenceException){}
}
当使用EventTrigger和EventToCommand类从列表框中选择项时,将从视图的xaml代码调用此命令(但与按钮相关的命令的问题仍然存在。以防万一,下面是我的列表框元素:

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

还有一个按钮的例子:

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

问题是,每次启动命令时,相关方法的执行次数都在增加。即:第一次调用时执行一次,然后执行两次,然后执行三次、四次、五次…次。由于方法中存在异步服务调用,因此这很快会占用带宽和时间


我绝对不明白这种行为背后的原因,有人能帮忙吗?

这里有两种可能的情况:

  • 您正在创建多个附加到页面的viewmodel实例,每个实例都在执行该命令

    或者,更有可能:

  • 您没有完全说明
    SelectionChanged
    事件的工作方式。当选择更改时,此事件可能会触发多次。如果已经有选定的项目,则在删除该项目时将触发更改的事件,然后在添加新的选定项目时再次触发。
    如果您直接使用事件处理程序,则可以直接查询
    SelectionChangedEventArgs
    ,以确定触发事件的原因。但是,您的代码忽略了这些参数,即使xaml表示您正在传递它们

  • 如果更改操作以使其支持这些参数,则可以在此处添加复选框。
    e、 g


    为什么要定义
    公共ICommand ShowTreatmentDetailsCommand
    ,然后定义
    ShowTreatmentDetailsCommand=new RelayCommand
    。用RelayCommand替换ICommand。可能不会解决您的问题,但如果我做对了,我不会。单击按钮,方法将被调用一次。再次单击按钮,方法将被禁用调用了两次,对吗?您是否查看了每次执行的堆栈跟踪,以找出触发执行的原因?@Rudi:我将我的命令声明为ICommand,因为ICommand是在.NET framework中构建的,而RelayCommand继承了ICommand接口,但随MVVM Light包而来。将命令声明为RelayCommands代替ICommand没有什么区别,但我只是习惯性地使用ICommand。是的,你的问题是对的。我也考虑过NR1,因为这也发生在我身上,但根据他的代码,我(我们)看不到他在何处或何时调用/创建ViewModel。SelectionChangedEvent考虑得很好,但他说当他单击按钮时也会发生这种情况,所以我认为这更可能是第1个问题。这实际上是我的第2个解决方案。我不知道SelectionChanged事件在每次选择时都会触发多次,谢谢分享提示。顺便说一下,在我的情况下,检查args.AddedItems.Count是否严格等于1,而不是“小于或等于”效果更好。我以前在一些按钮上也有类似的错误,但当我重构我的视图层时,它似乎已经被修复,使它在每次导航时都系统地调用CleanViewModel方法。IMHO
    SelectionChangedEventArgs
    Viewmodel
    中会使该方法与视图耦合,因此它在没有moc的情况下无法测试king the
    selection changedeventsargs
    还有更优雅的方式吗?
    ShowTreatmentDetailsCommand =
         new RelayCommand<SelectionChangedEventArgs>(ShowTreatmentDetails);
    
    private async void ShowTreatmentDetails(SelectionChangedEventArgs args)
    {
        if (args.AddedItems.Count >= 1)
        {
            // your code
        }
    }