我在将数据绑定(bind)到 ListView 时遇到一点问题。
因为我想要一个带有 MultiSelection 的 ListView ,所以我需要实现一个名为 GenericSelectableItem 的自定义类,它存储数据,如果单元格IsSelected强>.
首先,这是 MainPage 的 View 模型:
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)));
}
}
}
MainPage 的 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 中的绑定(bind)示例:
<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;
}
GenericSelectableItem 中的 Field Data 似乎从未设置,所以我认为绑定(bind)有问题
有没有人知道更好的方法或者为什么这不起作用? 非常感谢您的帮助!!
最佳答案
TL;DR 版本:深入了解您的 cell的和'cellViewModel' s 源代码 我注意到您的代码的绑定(bind)句柄存在混淆。您正在处理您在 AudioViewCell 的构造函数中设置的一个 BindingContext,但它被 ListView(在构造函数之后运行)自动设置的一个覆盖。所以你站在一个没有数据渲染的 ViewCell 上。
在这张图片上,我试图展示你的模型发生了什么:
请注意左侧的黄色圆形箭头,这是您在构造函数中定义绑定(bind)上下文。稍后会被红色箭头覆盖(在 ListView 呈现后设置)。
要使其按照您编码的方式工作,请按照下列步骤操作:
- 去掉 AudiofileViewCell 的 AudiofileProperty,你将不需要它;
- 为
MenuItemViewModel
的构造函数创建一个重载以接收“AudioFile”(MenuItemViewModel 类是您真正的 BindingContext); - 重写
OnBindingContextChanged
方法以提取新的Data
字段并将其作为参数发送给MenuItemViewModel
的新实例的构造函数; - 将
MenuItemViewModel
的这个新实例设置为内部 View 的 BindingContext(根据您的源代码,这是一个名为slRoot
的 StackLayout)
这是步骤代码:
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);
}
// ...
}
我已经测试过并且它有效,但它远非理想的清洁解决方案。
如果您的意图是重用此单元格,我认为您应该公开可以绑定(bind)或不绑定(bind)的属性,让它说明将显示什么。 View 单元应仅处理视觉布局/行为,与其上的数据无关。
P.S.:对不起,我的英语不好,我希望它能被理解。
关于c# - Xamarin ListView MVVM 数据绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49128660/