UWP 数据绑定:如何在数据模板中将按钮命令设置为父数据上下文

本文关键字:数据 按钮 命令 设置 上下文 数据绑定 UWP | 更新日期: 2023-09-27 18:31:32

需求的简短解释:我需要使用ViewModel的DataContext中的方法触发DataTemplate中的按钮命令。

问题的简短解释:模板化按钮命令似乎只能绑定到项目本身的数据上下文。WPF 和 Windows 8.1 应用用于在可视化树中向上移动的语法似乎不起作用,包括 ElementName 和 Ancestor 绑定。 我非常不希望我的按钮命令位于模型内。

旁注:这是使用 MVVM 设计方法构建的。

下面的代码生成 VIEW 上的项列表。该列表是每个列表项的一个按钮。

            <ItemsControl x:Name="listView" Tag="listOfStories" Grid.Row="0" Grid.Column="1"
            ItemsSource="{x:Bind ViewModel.ListOfStories}"
            ItemTemplate="{StaticResource storyTemplate}"
            Background="Transparent"
            IsRightTapEnabled="False"
            IsHoldingEnabled="False"
            IsDoubleTapEnabled="False"
                 />

在同一 VIEW 的页面资源中,我创建了一个数据模板,其中包含有问题的按钮。 我继续删除了按钮内的大部分格式,例如文本,以使代码更易于阅读。 与按钮有关的所有内容都有效,除了列出的问题,即命令的绑定。

<Page.Resources>
        <DataTemplate x:Name="storyTemplate" x:DataType="m:Story">
            <Button
                Margin="0,6,0,0"
                Width="{Binding ColumnDefinitions[1].ActualWidth, ElementName=storyGrid, Mode=OneWay}"
                HorizontalContentAlignment="Stretch"
                CommandParameter="{Binding DataContext, ElementName=Page}"
                Command="{Binding Source={StaticResource Locator}}">
                <StackPanel HorizontalAlignment="Stretch" >
                    <TextBlock Text="{x:Bind StoryTitle, Mode=OneWay}"
                        FontSize="30"
                        TextTrimming="WordEllipsis"
                        TextAlignment="Left"/>
                </StackPanel>
            </Button>
        </DataTemplate>
    </Page.Resources>

由于这是一个数据模板,因此数据上下文已设置为构成列表的各个项 (MODEL)。 我需要做的是选择列表本身的 DataContext(VIEWMODEL),以便我可以访问导航命令。

如果您对 VIEW 页面的代码隐藏感兴趣,请参阅下文。

    public sealed partial class ChooseStoryToPlay_View : Page
    {
    public ChooseStoryToPlay_View()
    {
        this.InitializeComponent();
        this.DataContextChanged += (s, e) => { ViewModel = DataContext as ChooseStoryToPlay_ViewModel; };
    }
    public ChooseStoryToPlay_ViewModel ViewModel { get; set; }
}

我尝试过通过 ElementName 设置它,以及许多其他尝试,但都失败了。 当输入 ElementName 时,智能感知将"storyTemplate"检测为一个选项,这是本问题的第一个代码块中显示的数据模板的名称。

我不相信我的问题可能是独一无二的,但是我很难找到 UWP 的解决方案。请允许我提前道歉,这是一个简单的问题,但我花了将近两天的时间研究答案,似乎没有一个对 UWP 有用。

谢谢你们!

UWP 数据绑定:如何在数据模板中将按钮命令设置为父数据上下文

您使用的是什么 MVVM 工具包(如果有的话)?在 MVVM Light 中,您可以从 DataTemplate 获取 ViewModel,就像为视图设置 DataContext 一样:

<DataTemplate x:Key="SomeTemplate">
    <Button Command="{Binding Main.MyCommand, Source={StaticResource ViewModelLocator}}"/>
</DataTemplate>
不幸的是

,UWP 中没有祖先绑定。这使得像你这样的方案更难实现。

我能想到的唯一方法是在您的Page上创建一个ViewModel DependencyProperty

public ChooseStoryToPlay_ViewModel ViewModel
{
    get { return (ChooseStoryToPlay_ViewModel)GetValue(ViewModelProperty); }
    set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty =
    DependencyProperty.Register("ViewModel", typeof(ChooseStoryToPlay_ViewModel), typeof(MainPage), new PropertyMetadata(0));

现在,您可以从数据模板绑定到它:

<DataTemplate x:Name="storyTemplate" x:DataType="local:Story">
    <Button
        Margin="0,6,0,0"
        Width="{Binding ColumnDefinitions[1].ActualWidth, ElementName=storyGrid, Mode=OneWay}"
        HorizontalContentAlignment="Stretch"
        CommandParameter="{x:Bind Page}"
        Command="{Binding ViewModel.NavigateCommand, ElementName=Page}">
        <StackPanel HorizontalAlignment="Stretch" >
            <TextBlock Text="{x:Bind StoryTitle, Mode=OneWay}"
                FontSize="30"
                TextTrimming="WordEllipsis"
                TextAlignment="Left"/>
        </StackPanel>
    </Button>
</DataTemplate>

需要注意的几点:

  • CommandParameter中,我假设在您的Story类中有一个要作为参数传递给命令的Page属性。您可以在此处绑定到类的任何其他属性Story也可以绑定到类本身。
  • 您必须将页面的名称设置为 Pagex:name="Page" ),以便可以使用数据模板中的ElementName引用它。
  • 我假设您在ViewModel上调用的命令名为 NavigateCommand 并接受与绑定到 CommandParameter 的属性相同类型的参数:

    public ICommand NavigateCommand { get; } = 
        new RelayCommand<string>(name => Debug.WriteLine(name));
    

我希望这有所帮助,并且适用于您的方案。

有几种方法可以做到这一点。但我认为命令改变得更好...

例如,您有一个(网格,列表)视图,其中包含一些项模板,如下所示:

                    <GridView.ItemTemplate>
                        <DataTemplate>
                            <Grid
                                    x:Name="gdVehicleImage"
                                    Height="140"
                                    Width="140"
                                    Background="Gray"
                                    Margin="2"
                                >
                           </Grid>
                 </GridView.ItemTemplate>

您是否要发出命令,例如浮出控件菜单...但是命令它在视图模型中,而不是在网格视图中。

你能做的是...

                        <Grid
                                    x:Name="gdVehicleImage"
                                    Height="140"
                                    Width="140"
                                    Background="Gray"
                                    Margin="2"
                                >
                                <FlyoutBase.AttachedFlyout>
                                    <MenuFlyout
                                            Opened="MenuFlyout_Opened"
                                            Closed="MenuFlyout_Closed"
                                        >
                                        <MenuFlyout.MenuFlyoutPresenterStyle>
                                            <Style TargetType="MenuFlyoutPresenter">
                                                <Setter Property="Background" Value="DarkCyan"/>
                                                <Setter Property="Foreground" Value="White"/>
                                            </Style>
                                        </MenuFlyout.MenuFlyoutPresenterStyle>
                                        <MenuFlyoutItem 
                                                Loaded="mfiSetAsDefaultPic_Loaded" 
                                                CommandParameter="{Binding}"
                                                />
                                        <MenuFlyoutItem 
                                                Loaded="mfiDeletePic_Loaded" 
                                                CommandParameter="{Binding}"
                                                />
                                    </MenuFlyout>
                                </FlyoutBase.AttachedFlyout>

             </Grid>

在加载的事件中:

    private void mfiDeletePic_Loaded(object sender, RoutedEventArgs e)
    {
        var m = (MenuFlyoutItem)sender;
        if (m != null)
        {
            m.Command = Vm.DeleteImageCommand;
            //Vm is the ViewModel instance...
        }
    }

不完全美丽...但是你不会像这样破坏 mvvm 模式......