如何从GridViewItem Tap事件(XAML)执行命令

oba*_*lis 9 xaml winrt-xaml windows-8.1

我试图在我的Windows 8.1商店应用程序(XAML)中遵循MVVM模式.

我想在UI中单击/点击GridViewItem时导航到新视图.我想在没有代码的情况下执行此操作以提升可测试性(使用MVVM Light).

为了允许我的UI绑定到视图模型命令,我一直在查看通过添加引用 - > Windows - >扩展添加的Microsoft行为SDK(XAML).

我点击网格视图项时,我视图中的以下代码会编译但会爆炸.不幸的是它提供了很少的帮助,只是抛出了未处理的win32异常[3476].

有人可以帮助解决这个问题吗?

使用的命名空间是;

xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"


<GridView x:Name="itemGridView"
                      AutomationProperties.AutomationId="ItemGridView"
                      AutomationProperties.Name="Grouped Items"            
                      ItemsSource="{Binding Source={StaticResource GroupedSource}}"                         
                      IsSwipeEnabled="True"
                      IsTapEnabled="True">

                <GridView.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="0"
                              Height="230">
                            <StackPanel Orientation="Vertical"
                                        HorizontalAlignment="Stretch">
                                <Image Source="{Binding Image}"                                  
                                       Stretch="UniformToFill"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"
                                       />
                                <StackPanel VerticalAlignment="Bottom"
                                            Height="45"
                                            Margin="0,-45,0,0">
                                    <StackPanel.Background>
                                        <SolidColorBrush Color="Black" 
                                                         Opacity="0.75" 
                                                         />
                                    </StackPanel.Background>
                                    <TextBlock FontSize="16"
                                               Margin="2"
                                               Text="{Binding Name}"
                                               TextWrapping="Wrap"
                                               VerticalAlignment="Bottom"
                                               />
                                </StackPanel>
                            </StackPanel>

                            <interactivity:Interaction.Behaviors>
                                <core:EventTriggerBehavior EventName="Tapped">
                                    <core:InvokeCommandAction Command="{Binding DataContext.SummaryCatagorySelectedCommand, ElementName=LayoutRoot}" />
                                </core:EventTriggerBehavior>                                                                        
                            </interactivity:Interaction.Behaviors>                                                                                             
                        </Grid>
                    </DataTemplate>
                </GridView.ItemTemplate>
Run Code Online (Sandbox Code Playgroud)

编辑.根据要求,我添加了视图模型,特别包含了我想从我的行为中触发的命令.

public class ViewModel : ViewModelBase
{
    public RelayCommand<string> SummaryCatagorySelectedCommand { get; set; }

    public ViewModel()
    {
        //
    }
}
Run Code Online (Sandbox Code Playgroud)

Jer*_*xon 13

最简单的答案是告诉您在这种情况下不应该使用命令.首先,命令的值是它既执行又无法执行交互式XAML控件.例如,当命令不可用时,该按钮被禁用.

但是因为你正在使用框架元素的tapped事件,所以你基本上只是使用控件,就好像它是一个简单的方法,而不是一个命令.当然,您的视图模型可以同时具有命令和方法.行为可以调用命令和方法.

为此,此解决方案的最佳方案是更改您在视图模型中调用命令的方法.您的困难是1.命令超出了数据模板的范围,并且2.命令参数在超出范围的线程上下文中传递.

以下是我的建议,让您的生活更轻松,您的应用程序更简单.

不要附加到项目的轻敲事件.而是附加到gridview的itemclicked事件.当然,这意味着您需要IsItemClickEnabled在gridview上设置为true.然后不要调用一个命令,这是你没有使用的开销,而是调用一个方法.

这是方法在viewmodel中的样子:

public async void ClickCommand(object sender, object parameter)
{
    var arg = parameter as Windows.UI.Xaml.Controls.ItemClickEventArgs;
    var item = arg.ClickedItem as Models.Item;
    await new MessageDialog(item.Text).ShowAsync()
}
Run Code Online (Sandbox Code Playgroud)

方法的名称无关紧要(我甚至将其称为命令来表明这一点),但签名确实如此.行为框架正在寻找一个零参数或两个对象类型参数的方法.方便的是,两个参数版本将事件签名转发给它.在这种情况下,这意味着您可以使用包含所单击项目的ItemClickEventArgs.很简单.

您的gridview也得到了简化.您可以简单地将gridview的自然范围引用到外部视图模型,而不是尝试在数据上下文中强制范围.它看起来像这样:

<GridView Margin="0,140,0,0" Padding="120,0,0,0" 
            SelectionMode="None" ItemsSource="{Binding Items}" 
            IsItemClickEnabled="True">
    <Interactivity:Interaction.Behaviors>
        <Core:EventTriggerBehavior EventName="ItemClick">
            <Core:CallMethodAction MethodName="ClickCommand" 
                TargetObject="{Binding Mode=OneWay}" />
        </Core:EventTriggerBehavior>
    </Interactivity:Interaction.Behaviors>
Run Code Online (Sandbox Code Playgroud)

这是一个更简单的解决方案,并且不违反MVVM模式中的任何内容,因为它仍然将逻辑推送到您的分离且可测试的视图模型.它允许您有效地将行为用作事件到命令,但实际上使用更简单的事件到方法模式.由于行为首先没有将CanExecute值传递回控件,这实际上也简化了您的viewmodel.

如果,事实证明你想要做的是重用已经在其他地方利用的现有命令(听起来像1%边缘的情况),你总是可以为此目的创建一个shell方法,在内部利用该命令.

作为警告,Windows 8.1附带的RelayCommand没有正确实现ICommand,因为它在调用Execute之前不首先测试CanExecute.此外,键入的RelayCommand中的CanExecute逻辑不会将CommandParameter传递给处理程序.这一切都不重要,取决于您首先使用命令的人.这对我很重要.

那么,这是你的答案.更改为GridView.ItemClicked并从ViewModel.Command更改为ViewModel.Method.这使您的生活更轻松,更轻松,并且如果您想重用数据模板,可以使您的XAML更具可移植性.

祝你好运!