0

我有一个ListView绑定到一组对象(在这种情况下称为User s),并且模板包含一个ContextActions菜单。需要根据与视图中的项目没有直接关系的条件来启用或禁用其中一个菜单项(无论是否存在与某种外围设备的蓝牙连接)。我现在正在做的是迭代TemplatedItems属性中的 Cell 并在每个上设置IsEnabled

这是ListView的 XAML ,精简到对我的问题很重要的部分:

<ListView ItemsSource="{Binding .}" ItemTapped="item_Tap">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextCell Text="{Binding Label}">
                <TextCell.ContextActions>
                    <MenuItem
                        Text="Copy to other device"
                        ClassId="copyMenuItem"
                        Clicked="copyMenuItem_Click" />
                </TextCell.ContextActions>
            </TextCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

这是我现在设置属性值的方式:

foreach (Cell cell in usersListView.TemplatedItems)
{
    foreach (MenuItem item in cell.ContextActions)
    {
        if ("copyMenuItem" == item.ClassId)
        {
            item.IsEnabled = isBluetoothConnected;
        }
    }
}

这行得通,但我不喜欢它。这显然与数据绑定视图的整体理念不符。我宁愿有一个可以绑定到IsEnabled属性的布尔值,但是从对象设计的角度来看,将它添加到User对象没有意义;它与该类的内容(代表登录帐户)无关。我想将User包装在某个存在的本地类中,只是为了将这个布尔属性粘贴到它上面,但这也感觉很奇怪,因为集合中的每个项目的值总是相同的。还有其他方法可以绑定MenuItem.IsEnabled属性吗?

4

2 回答 2

0

使用相对绑定

  1. 在您的视图模型类、继承INotifyPropertyChanged或您的BaseViewModel.
    public class YourViewModel : INotifyPropertyChanged
    {
        private string isBluetoothConnected;

        public string IsBluetoothConnected
        {
            get => isBluetoothConnected;
            set => SetProperty(ref isBluetoothConnected, value);
        }

        public ObservableCollection<User> Users { get; private set; }
    }
  1. 为 ListView 添加名称以供参考,并在 MenuItem 中应用相对绑定。
<ListView
    x:Name="UserListView"
    ItemsSource="{Binding Users}"
    ItemTapped="item_Tap">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextCell Text="{Binding Label}">
                <TextCell.ContextActions>
                    <MenuItem
                        IsEnabled="{Binding Path=BindingContext.IsBluetoothConnected, Source={x:Reference UserListView}}"
                        Text="Copy to other device"
                        ClassId="copyMenuItem"
                        Clicked="copyMenuItem_Click" />
                </TextCell.ContextActions>
            </TextCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
于 2021-08-04T21:59:17.300 回答
0

事实证明,这种BindableProperty实际上是不可绑定的:https ://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/menuitem#enable-or-disable-a-运行时菜单项

必须将Command属性添加到MenuItem并为其分配BindingContext,并设置其可执行性。这是我的代码的最新版本,它确实有效:

<MenuItem
    Text="Copy to other device"
    Clicked="copyMenuItem_Click"
    BindingContext="{x:Reference usersListView}"
    Command="{Binding BindingContext.CopyCommand}" />
public class UsersViewModel
{
    public Command CopyCommand { get; set; }
    public bool IsBluetoothConnected
    {
        get { return isBluetoothConnected; }
        set
        {
            isBluetoothConnected = value;
            if (CopyCommand.CanExecute(null) != value)
            {
                CopyCommand.ChangeCanExecute();
            }
        }
    }
    public ObservableCollection<User> Users { get; private set; }

    private bool isBluetoothConnected;

    public async System.Threading.Tasks.Task<int> Populate( )
    {
        CopyCommand = new Command(( ) => { return; }, ( ) => IsBluetoothConnected); // execute parameter is a no-op since I really just want the canExecute parameter
        IList<User> users = await App.DB.GetUsersAsync();
        Users = new ObservableCollection<User>(users.OrderBy(user => user.Username));
        return Users.Count;
    }
}

我仍然对此并不完全满意;它用特定视图的关注点污染了视图模型。我将看看是否可以将命令与视图模型分开。但它确实实现了我的主要目标,将这个 UI 实现带入数据绑定范例。

于 2021-08-10T16:50:02.833 回答