C# UWP数据绑定:如何将button命令设置为DataTemplate内的父DataContext

C# UWP数据绑定:如何将button命令设置为DataTemplate内的父DataContext,c#,xaml,data-binding,uwp,datacontext,C#,Xaml,Data Binding,Uwp,Datacontext,简短的需求说明:我需要使用ViewModel的DataContext中的一个方法,在DataTemplate中启动一个按钮的命令 问题的简短解释:模板化按钮命令似乎只能绑定到项本身的datacontext。WPF和Windows 8.1应用程序在可视化树中使用的语法似乎不起作用,包括ElementName和祖先绑定。我非常不希望按钮命令位于模型内部 旁注:这是使用MVVM设计方法构建的 下面的代码生成视图上的项目列表。该列表是每个列表项的一个按钮 <ItemsCon

简短的需求说明:我需要使用ViewModel的DataContext中的一个方法,在DataTemplate中启动一个按钮的命令

问题的简短解释:模板化按钮命令似乎只能绑定到项本身的datacontext。WPF和Windows 8.1应用程序在可视化树中使用的语法似乎不起作用,包括ElementName和祖先绑定。我非常不希望按钮命令位于模型内部

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

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

            <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"
                 />
在许多其他尝试中,我试着通过ElementName设置它,但都失败了。Intellisense在输入ElementName时将“storyTemplate”检测为选项,ElementName是此问题的第一个代码块中显示的DataTemplate的名称

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


谢谢你们

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

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

                    <GridView.ItemTemplate>
                        <DataTemplate>

                            <Grid
                                    x:Name="gdVehicleImage"
                                    Height="140"
                                    Width="140"
                                    Background="Gray"
                                    Margin="2"

                                >

                           </Grid>

                 </GridView.ItemTemplate>

不完全是美丽的。。。但您不会像这样破坏mvvm模式…

不幸的是,UWP中没有祖先绑定。这使得像您这样的场景更难实现

我能想到的唯一方法是在
页面上为
ViewModel
创建一个
dependencProperty

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>

我希望这有助于并适用于您的场景。

您正在使用什么MVVM工具包(如果有的话)?在MVVM Light中,您可以从DataTemplate获得ViewModel,方法与为视图设置DataContext相同:

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


我从未尝试过UWP,但在其他基于XAML的框架中,通常使用UWP进行处理。如果没有帮助的话,那可能是因为数据模板在参考资料中(尽管这是可疑的),或者UWP XAML有自己的一些特殊性。是的,先生,这就是导致我如此沮丧的原因。我做的第一件事是RelativeSource祖先绑定,就像我在过去的平台上做的一样,但这次它不起作用。谢谢。我真的很感谢你花时间通读我的问题,并把代码放在一起。不过,恐怕这对我的申请没有多大帮助。我只需要启动button命令即可激活viewmodel上的backing命令,该命令将导航到另一个页面,同时携带任何参数。不过,再次感谢你!是的,只要想想你喜欢的按钮弹出菜单。。。将您的按钮命令放入已加载的事件中,但保留带有绑定的CommandParameter。。。您将使用带有参数的命令导航到另一个页面:数据这太棒了。非常感谢您解决了祖先绑定是否可用的问题。我几乎已经决定,在无法绑定到视图模型命令的情况下,有两个选项可用。在模型上创建一个命令,并将故事作为一个参数传入,这是我不愿意做的…或者在视图后端的按钮上应用一个click参数。这是一个小小的咆哮,但我觉得难以理解的是,微软吹嘘为程序未来的平台剥夺了这一功能。再次感谢你,达米尔!如果DataContext位于ResourceDictionary中,则这没有帮助。有人找到了合适的解决方案吗?这会实例化新的、断开连接的ViewModel版本吗?谢谢你的回答!不,不会的。在ViewModelLocator Main中,应该只从默认ServiceLocator获取MainViewModel的实例,该默认ServiceLocator返回singleton。拍额头我现在觉得自己像个十足的白痴。西恩,请允许我最真诚地感谢你。看着这段代码,我的眼睛有些模糊,但我认为你的代码可以工作!确保定位器中的所有内容都正确无误,它将像一个符咒一样工作。很高兴能帮忙。顺便说一句,爱你的头像。我是谢泼德指挥官,我同意你的回答。
    private void mfiDeletePic_Loaded(object sender, RoutedEventArgs e)
    {
        var m = (MenuFlyoutItem)sender;

        if (m != null)
        {

            m.Command = Vm.DeleteImageCommand;
            //Vm is the ViewModel instance...
        }
    }
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>
public ICommand NavigateCommand { get; } = 
    new RelayCommand<string>(name => Debug.WriteLine(name));
<DataTemplate x:Key="SomeTemplate">
    <Button Command="{Binding Main.MyCommand, Source={StaticResource ViewModelLocator}}"/>
</DataTemplate>