C# Xamarin ListView MVVM数据绑定

C# Xamarin ListView MVVM数据绑定,c#,xamarin,data-binding,C#,Xamarin,Data Binding,我在数据绑定到ListView时遇到了一个小问题 因为我想要一个具有多重选择的Listview,所以我需要实现一个名为GenericSelectableItem的自定义类,该类存储数据,并且如果单元格被选中 首先,这里是主页的视图模型: public class MainPageViewModel : BaseViewModel, INotifyPropertyChanged { private ObservableCollection<GenericSelectableItem&

我在数据绑定到ListView时遇到了一个小问题

因为我想要一个具有多重选择的Listview,所以我需要实现一个名为GenericSelectableItem的自定义类,该类存储数据,并且如果单元格被选中

首先,这里是主页的视图模型:

public class MainPageViewModel : BaseViewModel, INotifyPropertyChanged
{
    private ObservableCollection<GenericSelectableItem<AudioFile>> _audiofiles = new ObservableCollection<GenericSelectableItem<AudioFile>>();

    public ObservableCollection<GenericSelectableItem<AudioFile>> AudioFiles
    {
        get => _audiofiles ?? new ObservableCollection<GenericSelectableItem<AudioFile>>();
        set
        {
            _audiofiles = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(AudioFiles)));
        }
    }
}
            <!-- The Content -->
            <ListView x:Name="listView" Grid.Row="1" HasUnevenRows="true" RowHeight="-1" ItemsSource="{Binding AudioFiles}" ItemSelected="ListView_OnItemSelected">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <local:AudioViewCell Audiofile="{Binding Data}"/>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
最后是MenuItemViewModel:

public class MenuItemViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    private AudioFile _audioFile;
    public AudioFile Audiofile
    {
        get => _audioFile;
        set
        {
            Debug.WriteLine("Setting Audiofile");
            _audioFile = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(Audiofile)));
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(Filename)));
        }
    }

    public string Filename => Audiofile?.Filename;
}
似乎GenericSelectableItem中的字段数据从未设置过,因此我认为绑定有问题

有人知道更好的方法吗?或者为什么不起作用?
非常感谢你的帮助

TL;DR Version:深入查看您的和的源代码,我注意到在代码的绑定句柄方面存在混乱。您正在处理在AudioViewCell的构造函数中设置的一个BindingContext,但它被ListView(在构造函数之后运行)自动设置的BindingContext覆盖。因此,您使用的ViewCell渲染时没有数据

在这张图片上,我试图展示您的模型的情况:

注意左边的黄色圆形箭头,它是您在构造函数中定义绑定上下文的地方。稍后将被红色箭头覆盖(在listview渲染后设置)

要使其按编码方式工作,请执行以下步骤:

  • 在AudiofileViewCell中去掉AudiofileProperty,您将不需要它
  • 创建一个重载到
    MenuItemViewModel
    的构造函数以接收“AudioFile”(MenuItemViewModel类是您真正的绑定上下文)
  • 重写
    OnBindingContextChanged
    方法以提取新的
    数据
    字段,并将其作为参数发送给
    MenuItemViewModel
    的新实例的构造函数
  • MenuItemViewModel
    的这个新实例设置为内部视图的BindingContext(根据源代码,它是一个名为
    slRoot
    的堆栈布局)
  • 以下是代码的步骤:

    2:

    public class MenuItemViewModel : INotifyPropertyChanged
    {
        // ...
        public void SetAudiofile(AudioFile data)
        {
            Audiofile = data;
        }
        // ...
    }
    
    public partial class AudioViewCell : ViewCell
    {
        // ...
        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
            // * I'm not sure if it's ok create a new instance to your binding context here, the old one ca be kept on memory due it's subscription. Think about create a method to set just the Audiofile property
            slRoot.BindingContext = new MenuItemViewModel( thing, thing, ((GenericSelectableItem<AudioFile>)BindingContext).Data); 
        }
        // ...
    }
    
    3和4:

    public class MenuItemViewModel : INotifyPropertyChanged
    {
        // ...
        public void SetAudiofile(AudioFile data)
        {
            Audiofile = data;
        }
        // ...
    }
    
    public partial class AudioViewCell : ViewCell
    {
        // ...
        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
            // * I'm not sure if it's ok create a new instance to your binding context here, the old one ca be kept on memory due it's subscription. Think about create a method to set just the Audiofile property
            slRoot.BindingContext = new MenuItemViewModel( thing, thing, ((GenericSelectableItem<AudioFile>)BindingContext).Data); 
        }
        // ...
    }
    
    公共部分类AudioViewCell:ViewCell
    {
    // ...
    受保护的覆盖无效OnBindingContextChanged()
    {
    base.OnBindingContextChanged();
    //*我不确定是否可以在此处为绑定上下文创建一个新实例,由于订阅,旧实例可以保留在内存中。请考虑创建一个方法,仅设置Audiofile属性
    slRoot.BindingContext=new MenuItemViewModel(thing,thing,((GenericSelectableItem)BindingContext).Data);
    }
    // ...
    }
    
    我已经测试过了,它可以工作,但它远不是一个理想的清洁解决方案

    如果您打算重用这个单元,我认为您应该公开可以绑定或不绑定的属性,让它的需要说明将显示什么。视图单元应该只处理视觉布局/行为,而不管上面有什么数据


    注:对不起,我的英语不好,我希望可以理解。

    是的,对不起,这有点不清楚。也许我的编辑更好地解释了这种情况。没有问题。代码很清楚,但我无法弄清楚到底是怎么回事。现在我想我明白了。好吧,很好,也许你看到了我的错误:D也是,我知道这有多糟糕。重要的一点是,当您绑定属性时,它不会调用
    set
    方法,而是直接对静态方法进行绑定,然后获取它。我在这里复制你的场景,试图弄清楚到底发生了什么。是的,有时候这很烦人,尤其是当VisualStudUI突然停止运行时,Xamarin:'DSo我终于测试了你的解决方案,它工作了!尽管您已经说过,这不是ViewModel的常用用法。多谢各位!
    public partial class AudioViewCell : ViewCell
    {
        // ...
        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
            // * I'm not sure if it's ok create a new instance to your binding context here, the old one ca be kept on memory due it's subscription. Think about create a method to set just the Audiofile property
            slRoot.BindingContext = new MenuItemViewModel( thing, thing, ((GenericSelectableItem<AudioFile>)BindingContext).Data); 
        }
        // ...
    }