我正在尝试在我的Windows 8.1商店应用程序(XAML)中遵循MVVM模式。
我想在UI中单击/点击GridViewItem时导航到新视图。我想在没有代码的情况下执行此操作以提升可测试性(使用MVVM Light)。
为了允许我的UI绑定到视图模型命令,我一直在查看通过添加引用添加的Microsoft行为SDK(XAML) - > Windows - >扩展。
当我点击网格视图项时,我视图中的以下代码会编译但会爆炸。不幸的是,它提供的帮助很小。只是抛出一个未处理的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>
编辑。根据要求,我添加了视图模型,特别包含了我想从我的行为中触发的命令。
public class ViewModel : ViewModelBase
{
public RelayCommand<string> SummaryCatagorySelectedCommand { get; set; }
public ViewModel()
{
//
}
}
答案 0 :(得分:13)
最简单的答案是告诉您在这种情况下不应该使用命令。首先,命令的值是它既执行又无法执行交互式XAML控件。例如,当命令不可用时,该按钮被禁用。
但是由于你正在使用框架元素的tapped事件,你基本上只是使用控件,就像它是一个简单的方法,而不是一个命令。当然,您的视图模型可以同时具有命令和方法。行为可以调用命令和方法。
为此,此解决方案的最佳方案是更改您在视图模型中调用命令的方法。您的困难是1.命令超出了数据模板的范围,并且2.命令参数在超出范围的线程上下文中传递。
这就是我建议的方式,让您的生活更轻松,简化您的应用。
请勿附加到项目的点击事件。而是附加到gridview的itemclicked事件。当然,这意味着您需要在gridview上将IsItemClickEnabled
设置为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()
}
该方法的名称无关紧要(我甚至将其称为命令来指出这一点),但签名确实如此。行为框架正在寻找一个零参数或两个对象类型参数的方法。方便的是,两个参数版本将事件签名转发给它。在这种情况下,这意味着您可以使用包含所单击项目的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>
这是一个更简单的解决方案,并且不违反MVVM模式中的任何内容,因为它仍然将逻辑推送到您的分离且可测试的视图模型。它允许您有效地将行为用作事件到命令,但实际上使用更简单的事件到方法模式。由于行为不会首先将CanExecute值传递回控件,这实际上也简化了您的viewmodel。
如果,事实证明你想要做的是重用已经在其他地方利用的现有命令(听起来像1%的边缘情况)你总是可以为此目的创建一个shell方法,在内部利用命令你。
作为警告,Windows 8.1附带的RelayCommand没有正确实现ICommand,因为它在调用Execute之前不首先测试CanExecute。此外,键入的RelayCommand中的CanExecute逻辑不会将CommandParameter传递给处理程序。这一切都不重要,取决于您首先使用命令的人。这对我很重要。
所以,这是你的答案。更改为GridView.ItemClicked并从ViewModel.Command更改为ViewModel.Method。这使您的生活更轻松,更轻松,并且如果您想要重复使用数据模板,可以使您的XAML更具可移植性。
祝你好运!
答案 1 :(得分:0)
当您点击任何项目时,您会更改SelectedItem,我猜。你可以绑定(Mode = TwoWay)SelectedItem并在属性的Set()中引发所需的动作。
或者您可以使用类似的东西并将其用作GridView的依赖属性。
public class GridViewItemClickCommand
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand),
typeof(GridViewItemClickCommand), new PropertyMetadata
(null, CommandPropertyChanged));
public static void SetCommand(DependencyObject attached, ICommand value)
{
attached.SetValue(CommandProperty, value);
}
public static ICommand GetCommand(DependencyObject attached)
{
return (ICommand)attached.GetValue(CommandProperty);
}
private static void CommandPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
// Attach click handler
(d as GridView).ItemClick += gridView_ItemClick;
}
private static void gridView_ItemClick(object sender,
ItemClickEventArgs e)
{
// Get GridView
var gridView = (sender as GridView);
// Get command
ICommand command = GetCommand(gridView);
// Execute command
command.Execute(e.ClickedItem);
}
}
如果您有任何问题,请询问:)
答案 2 :(得分:0)
我发了同样的东西,但我用另一种方式解决了。我没有把行为放在datatemplate中,我是在GridView中做的:
<GridView x:Uid="Flow"
x:Name="PlacesGridView"
Grid.Column="1" Grid.Row="2"
ItemTemplate="{StaticResource YourDataTemplate}"
ItemsSource="{Binding YourSource}" >
<Interactivity:Interaction.Behaviors>
<Behaviors:GoToDestinationOnSelected/>
</Interactivity:Interaction.Behaviors>
</GridView>
这就是GoToDestinationOnSelected的样子:
public class GoToDestinationOnSelected : DependencyObject, IBehavior
{
.....
void GoToDestinationOnGridViewItemSelected_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
object obj = (sender as ListViewBase).SelectedItem;
if (obj == null)
return;
if (obj is YourClass)
{
App.RootFrame.Navigate(typeof(CountryPlacesPage));
return;
((AssociatedObject) as ListViewBase).SelectedIndex = -1;
}
public DependencyObject AssociatedObject
{
get;
private set;
}
public void Attach(DependencyObject associatedObject)
{
AssociatedObject = associatedObject;
(associatedObject as ListViewBase).SelectionChanged += GoToDestinationOnGridViewItemSelected_SelectionChanged;
}
public void Detach()
{
(AssociatedObject as ListViewBase).SelectionChanged -= GoToDestinationOnGridViewItemSelected_SelectionChanged;
}
~GoToDestinationOnSelected()
{
}
答案 3 :(得分:0)
在UWP(Window 10及更新版本)移动应用程序中,应用以下代码段
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="ItemClick">
<Core:EventTriggerBehavior.Actions>
<Core:`enter code here`InvokeCommandAction Command="{Binding itemclick}"/>
</Core:EventTriggerBehavior.Actions>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>