UWP Databinding: How to set button command to parent DataContext inside DataTemplate

后端 未结 3 710
灰色年华
灰色年华 2021-01-12 09:14

Short explanation of need: I need to fire the command of a button inside a DataTemplate, using a method from the DataContext of the ViewModel.

Short explanation of p

相关标签:
3条回答
  • 2021-01-12 10:01

    What MVVM toolkit are you using (if any)? In MVVM Light, you can get a hold of ViewModel from DataTemplate same way you set DataContext for your view:

    <DataTemplate x:Key="SomeTemplate">
        <Button Command="{Binding Main.MyCommand, Source={StaticResource ViewModelLocator}}"/>
    </DataTemplate>
    
    0 讨论(0)
  • 2021-01-12 10:04

    It really is unfortunate that there is no ancestor binding in UWP. This makes scenarios like yours much more difficult to implement.

    The only way I can think of is to create a DependencyProperty for ViewModel on your Page:

    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));
    

    Now you can bind to it from your data template:

    <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>
    

    A couple of things to notice:

    • In CommandParameter I assumed that in your Story class there is a Page property that you want to pass as a parameter to your command. You can bind to any other property of Story class here or the class itself.
    • You have to set the name of your page to Page (x:name="Page"), so that you can reference it using ElementName in the data template.
    • I assumed that the command you're calling on the ViewModel is named NavigateCommand and accepts a parameter of the same type as the property bound to CommandParameter:

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

    I hope this helps and is applicable to your scenario.

    0 讨论(0)
  • 2021-01-12 10:15

    There is a few ways to do that. But i think the Command change better...

    Example, you have a (grid,list)view with some itemtemplate like that:

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

    And do you want to make a command to for example a FlyoutMenu... But the command it's in the ViewModel and not in GridView.SelectedItem...

    What you can do is...

                            <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>
    

    And in the loaded events:

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

    Is not entirely beautiful... But you willnot breake mvvm pattern like this...

    0 讨论(0)
提交回复
热议问题