将按钮弹出命令绑定到 DataTemplate 中的 ViewModel 命令

Bind a Button Flyout Command to a ViewModel's Command in DataTemplate

我是 UWP 的新手 我正在尝试从显示在每个项目上的列表视图内的按钮弹出按钮绑定到我的 ViewModel 中的事件。我在网上看了很多解决方案并提出了以下代码,它编译得很好但是当我点击所说的编辑按钮时没有任何反应。

我的 ViewModel 可从页面上下文而不是项目上下文获得

XAML

<ListView x:Name="MainListView"
                  ItemsSource="{x:Bind ViewModel.Devices, Mode=OneWay}"
                  SelectionMode="Multiple" 
                  SelectionChanged="MainListView_SelectionChanged">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid Width="Auto">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="0*"></ColumnDefinition>
                            <ColumnDefinition Width=".4*"></ColumnDefinition>
                            <ColumnDefinition Width="3*"></ColumnDefinition>
                            <ColumnDefinition Width="3*"></ColumnDefinition>
                            <ColumnDefinition Width="3*"></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="2" Text="{Binding AssetNumber}"/>
                        <TextBlock Grid.Column="3" Text="{Binding SerialNumber}"/>
                        <TextBlock Grid.Column="4" Text="{Binding Model}"/>
                        <Button Grid.Column="1" Height="30" Width="30">
                            <Button.Flyout>
                                <MenuFlyout>
                                    <MenuFlyoutItem Text="Edit" Icon="Edit"
                                                    Command="{Binding ElementName=MainListView,Path=DataContext.ViewModel.EditCommand}"
                                                    CommandParameter="{Binding}"/>
                                </MenuFlyout>
                            </Button.Flyout>
                        </Button>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

查看模型Class

public class MainPageViewModel
{
    // Elements contained in the main listview 
    public ObservableCollection<Device> Devices = new ObservableCollection<Device>();

    public MainPageViewModel()
    {
        DeviceProvider.Fill(ref Devices, 100);
        EditCommand = new RelayCommand<Device>(EditDevice);
    }

    public RelayCommand<Device> EditCommand { get; set; }
    private async void EditDevice(Device device)
    {
        // Code here that creates a dialog
    }
}

设备class

public class Device : INotifyPropertyChanged
{
    private string assetNumber;
    private string serialNumber;
    private string model;
    public string AssetNumber
    {
        get
        {
            return assetNumber;
        }
        set
        {
            assetNumber = value;
            OnPropertyChanged();
        }
    }
    public string SerialNumber
    {
        get
        {
            return serialNumber;
        }
        set
        {
            serialNumber = value;
            OnPropertyChanged();
        }
    }
    public string Model
    {
        get
        {
            return model;
        }

        set
        {
            model = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

中继命令class

public class RelayCommand<T> : ICommand
{
    private readonly Action<T> _execute;
    private readonly Func<bool> _canExecute;

    public event EventHandler CanExecuteChanged;

    public RelayCommand(Action<T> execute) : this(execute, null)
    {
    }

    public RelayCommand(Action<T> execute, Func<bool> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute();
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        var handler = CanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

it compiles fine but when I click the said Edit button nothing happens.

问题是您为MenuFlyoutItem绑定了错误的Path(Path=DataContext.ViewModel.EditCommand),请删除ViewModel字段。我已经编辑了您的代码,请参考以下内容。

<Page.DataContext>
    <local:MainPageViewModel x:Name="ViewModel"/>
</Page.DataContext>
 ......
<Button
    Grid.Column="1"
    Width="30"
    Height="30"
    >
    <Button.Flyout>
        <MenuFlyout>
            <MenuFlyoutItem
                Command="{Binding ElementName=MainListView, Path=DataContext.EditCommand}"
                CommandParameter="{Binding}"
                Icon="Edit"
                Text="Edit"
                />
        </MenuFlyout>
    </Button.Flyout>
</Button>

您的代码似乎没有任何问题。所以它应该可以完美地工作。但如果不是,我怀疑 MainPage.ViewModel 成员可能没有正确定义。要在 {Binding} 中使用的 属性 必须是 "public" 并且必须具有 "get" 访问器。

public sealed partial class MainPage : Page
{
    public MainPageViewModel ViewModel { get; set; } = new MainPageViewModel();

    public MainPage()
    {
        this.InitializeComponent();
        DataContext = this;
    }
}