代码之家  ›  专栏  ›  技术社区  ›  DirtyNative

Xamarin ListView MVVM数据绑定

  •  3
  • DirtyNative  · 技术社区  · 8 年前

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

    因为我想有一个带有MultiSelection的Listview,所以我需要实现一个名为 通用可选项 其中存储 数据 ,如果单元格 IsSelected公司

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

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

    主页的Xaml:

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

    2个用于创建多选ListView的帮助器类:

    public class GenericSelectableItem<T> : SelectableItem
    {
        public GenericSelectableItem(T data)
            : base(data)
        {
        }
    
        public GenericSelectableItem(T data, bool isSelected)
            : base(data, isSelected)
        {
        }
    
        // this is safe as we are just returning the base value
        public new T Data
        {
            get => (T)base.Data;
            set => base.Data = value;
        }
    }
    
    public class SelectableItem : BindableObject
    {
        public static readonly BindableProperty DataProperty =
            BindableProperty.Create(
                nameof(Data),
                typeof(object),
                typeof(SelectableItem),
                (object) null);
    
        public static readonly BindableProperty IsSelectedProperty =
            BindableProperty.Create(
                nameof(IsSelected),
                typeof(object),
                typeof(SelectableItem),
                (object)false);
    
        public SelectableItem(object data)
        {
            Data = data;
            IsSelected = false;
        }
    
        public SelectableItem(object data, bool isSelected)
        {
            Data = data;
            IsSelected = isSelected;
        }
    
        public object Data
        {
            get => (object)GetValue(DataProperty);
            set => SetValue(DataProperty, value);
        }
    
        public bool IsSelected
        {
            get => (bool)GetValue(IsSelectedProperty);
            set => SetValue(IsSelectedProperty, value);
        }
    }
    

    AudioViewCell中的绑定示例。Xaml:

    <Label x:Name="LblFilename" Text="{Binding Filename}"
                                   VerticalTextAlignment="Center"
                                   Style="{StaticResource CellLabel}"/>
    

    AudioViewCell。cs公司

    public partial class AudioViewCell : ViewCell
    {
        public static BindableProperty AudiofileProperty = BindableProperty.Create(
            propertyName: nameof(Audiofile),
            returnType: typeof(AudioFile),
            declaringType: typeof(AudioViewCell),
            defaultValue: null,
            defaultBindingMode: BindingMode.OneWay);
    
        public AudioFile Audiofile
        {
            get => (AudioFile) GetValue(AudiofileProperty);
            set
            {
                Debug.WriteLine("Audiofile changed");
                SetValue(AudiofileProperty, value);
                ((MenuItemViewModel) BindingContext).Audiofile = value;
            }
        }
    
        public AudioViewCell()
        {
            InitializeComponent();
    
            this.BindingContext = new MenuItemViewModel(SlAdditionalData, AwvWaveView);
        }
    }
    

    最后是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;
    }
    

    看来这个领域 数据 在…内 通用可选项 从未设置,所以我认为装订有问题

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

    1 回复  |  直到 8 年前
        1
  •  1
  •   Diego Rafael Souza    8 年前

    TL;DR版本: 深入查看您的 cell 的和 'cellViewModel' 我注意到,代码的绑定句柄存在混淆。您正在处理在AudioViewCell的构造函数中设置的一个BindingContext,但它被ListView(在构造函数之后运行)自动设置的BindingContext覆盖。因此,您将使用无数据渲染的ViewCell。

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

    sample Diagram

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

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

    1. 去掉AudiofileViewCell的AudiofileProperty,您将不需要它;
    2. 创建重载以 MenuItemViewModel 的构造函数来接收“AudioFile”(MenuItemViewModel类是您真正的BindingContext);
    3. 覆盖 OnBindingContextChanged 方法提取新的 Data 字段,并将其作为参数发送给的新实例的构造函数 MenuItemViewModel菜单 ;
    4. 设置的此新实例 MenuItemViewModel菜单 作为内部视图的BindingContext(它是一个名为 slRoot 根据您的源代码)

    以下是步骤代码:

    2:

    public class MenuItemViewModel : INotifyPropertyChanged
    {
        // ...
        public void SetAudiofile(AudioFile data)
        {
            Audiofile = data;
        }
        // ...
    }
    

    3和4:

    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); 
        }
        // ...
    }
    

    我已经测试过了,效果不错,但它远不是理想的清洁解决方案。

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

    P、 对不起,我的英语不好,我希望它可以被理解。