【问题标题】:UWP: Binding ListView ItemTemplate MenuFlyOut events to ViewModelUWP:将 ListView ItemTemplate MenuFlyOut 事件绑定到 ViewModel
【发布时间】:2019-02-03 19:35:04
【问题描述】:

我有一个现有的 UWP 应用程序来管理网站和帐户的密码,但是当我大约 3 年前编写它时,我不太了解 MVVM,因此所有事件处理程序都在 View 的代码中,现在我正在尝试通过将此代码移至 ViewModel 来解决此问题并使其更符合 MVVM。

我拥有的现有功能之一是每个 ListView 项目上的弹出菜单,因此用户可以编辑/删除条目(并执行其他几个功能),但是因为我正在为 ListView ItemTemplate 数据模板定义 DataType现在将无法识别与我的 Viewmodel 中的 Click 事件处理程序的绑定。

正如你所料,我在命名空间和页面数据上下文中都定义了我的 ViewModel,如下所示:

    <Page ....
      xmlns:vm="using:PassPort.ViewModels"
      ...>

    <Page.DataContext>
        <vm:MainViewModel/>
    </Page.DataContext>

这是我的 ListView,它是 ItemTemplate 和 DataTemplate。在 FlyOut 中的每个 MenuFlyoutItem 中,我试图告诉它在我的 ViewModel 中使用处理程序,但它无法解析 'vm' - 它显示波浪线并显示“在类型 'Account' 中找不到属性 'vm'” .

    </ListView Name="lvwAccounts"  
               ItemsSource="{x:Bind vm.AccountsView}" 
               .....>
       <ListView.ItemTemplate>
        <DataTemplate x:DataType="model:Account">
            <Grid MinHeight="36" HorizontalAlignment="Left" RightTapped="AccountsList_RightTapped">

                <FlyoutBase.AttachedFlyout>
                    <MenuFlyout Placement="Bottom">
                        <MenuFlyoutItem x:Name="OpenWebsiteButton" Text="Open Website" Click="{x:Bind vm.FlyoutOpenWebsiteButton_Click}"/>
                        <MenuFlyoutItem x:Name="EditButton" Text="Edit Account" Click="{x:Bind vm.FlyoutEditButton_Click}"/>
                        <MenuFlyoutItem x:Name="AddButton" Text="Add Account" Click="{x:Bind vm. FlyoutAddButton_Click}"/>
                        <MenuFlyoutItem x:Name="DeleteButton" Text="Delete Account" Click="{x:Bind vm.FlyoutDeleteButton_Click}"/>
                    </MenuFlyout>
                </FlyoutBase.AttachedFlyout>

                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="200"/>
                    <ColumnDefinition Width="150"/>
                    <ColumnDefinition Width="200"/>
                    <ColumnDefinition Width="150"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind AccountName}"  Foreground="{StaticResource Light}"
                           VerticalAlignment="Center" HorizontalAlignment="Stretch"/>

                <TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Bind Category}"  Foreground="{StaticResource Light}"
                           VerticalAlignment="Center" HorizontalAlignment="Stretch" TextWrapping="Wrap" />

                <TextBlock Grid.Row="0" Grid.Column="2" Text="{x:Bind UserID}" Foreground="{StaticResource Light}"
                           VerticalAlignment="Center" HorizontalAlignment="Left"/>

                <TextBlock Grid.Row="0" Grid.Column="3" Text="{x:Bind Password}" Foreground="{StaticResource Light}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" />

                <TextBlock Grid.Row="0" Grid.Column="4" Text="{x:Bind PasswordHint}" Foreground="{StaticResource Light}"/>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

代码“vm”中的所有其他地方都已解决,因此似乎由于 DataType 它无法/不会解析我的 ViewModel 引用。

我也尝试过使用 'Click="{Binding vm.[event handler]"} 但没有区别 - 所以有人知道我该如何解决这个问题吗?

【问题讨论】:

  • 我认为你不应该将 ViewModel 绑定到 MenuFlyout。
  • 可以绑定其他Element来解决。
  • x:Bind 不使用当前 DataContext 作为默认绑定源。当您编写x:Bind vm.AccountsView 时,视图代码中应该有一个vm 属性,该属性包含一个视图模型实例。最好不要使用x:Bind,写ItemsSource="{Binding AccountsView}"

标签: c# xaml listview mvvm uwp


【解决方案1】:

DataTemplate and x:Bind微软说:

在 DataTemplate 内部(无论用作项目模板、内容模板还是标题模板),Path 的值不是在页面上下文中解释的,而是在被模板化的数据对象的上下文中解释的。为了在编译时验证其绑定(并为其生成有效代码),DataTemplate 需要使用x:DataType 声明其数据对象的类型。

要在 DataTemplate 中绑定 ViewModel,您需要使用 binding 而不是 x:bind,就像这段代码一样。

<ListView Name="lvwAccounts"  
          ItemsSource="{x:Bind vm.AccountsView}" >
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="model:Account">
                <FlyoutBase.AttachedFlyout>
                    <MenuFlyout Placement="Bottom">
                        <MenuFlyoutItem x:Name="OpenWebsiteButton" Text="Open Website" Command="{Binding ElementName=lvwAccounts,Path=DataContext.FlyoutOpenWebsite}"/>

                    </MenuFlyout>
                </FlyoutBase.AttachedFlyout>
        </DataTemplate>
    </ListView.ItemTemplate>      
</ListView>

但是UWP中的binding不能绑定方法,binding只能绑定Command。您应该将方法更改为命令。

我在 MenuFlyoutItem 中找不到 Command 属性,我们可以使用 Behavior 将 UI 事件绑定到 ViewModel 中的命令参见:WPF Binding UI events to commands in ViewModel

见:https://stackoverflow.com/a/40774956/6116637

Why can't I use {x:Bind {RelativeSource Self}} in a data template?

【讨论】:

  • 感谢 lindexi - 在发布答案后我做了更多阅读,并意识到我可以使用“Binding ElementName=...”,但也意识到我需要将 VM 中的事件处理程序更改为命令。
  • 对于像只显示 FlyOutMenu 这样的事情,肯定可以在代码隐藏中完成吗?显然,每个菜单选项的命令必须在 VM 中实现,因为它涉及业务逻辑,但只是为了呈现菜单控件,我不能将其保留在 View 代码隐藏中吗?如果没有,为什么不呢?
猜你喜欢
  • 1970-01-01
  • 2011-12-17
  • 1970-01-01
  • 2013-10-11
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
  • 1970-01-01
  • 2017-12-14
相关资源
最近更新 更多