如何将视图模型的ReactiveList绑定到包含视图模型视图的ListView
本文关键字:视图 模型 包含 ListView 绑定 ReactiveList | 更新日期: 2023-09-27 18:15:21
我有一个ParentViewModel
,它包含ChildViewModels
的ReactiveList
。我想将它绑定到一个ListView
,它将显示ChildViews
。ChildView
将一些文本绑定到Label
,并根据状态enum设置Image
资源:
ParentViewModel.cs ChildViewModels
的简单容器
public class ParentViewModel : ReactiveObject
{
public ReactiveList<ChildViewModel> Children { get; }
= new ReactiveList<ChildViewModel>();
}
ChildViewModel.cs有一些文本和Status
public class ChildViewModel : ReactiveObject
{
public string SomeText { get; set; }
public enum Status
{
Unknown,
Known
}
public Status CurrentStatus { get; set; } = Status.Unknown;
}
ParentView。xaml UserControl
,它包装了ListView
,使用ChildView
作为数据模板。我确保在我的DataTemplate
上添加ViewModel="{Binding}"
。
<UserControl x:Class="MyProject.UI.ParentView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:MyProject.View_Models"
xmlns:ui="clr-namespace:MyProject.UI">
<ListView x:Name="ChildList" BorderThickness="0">
<ListView.ItemTemplate>
<DataTemplate>
<!--I have also tried the following line, didn't work -->
<!--<DataTemplate DataType="{x:Type vm:ChildViewModel}">-->
<ui:ChildView ViewModel="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
ParentView.xaml.cs为其ParentViewModel
创建一个依赖属性,并将视图模型的Children
绑定到ChildList ListView
public partial class ParentView : UserControl, IViewFor<ParentViewModel>
{
public static readonly DependencyProperty ViewModelProperty
= DependencyProperty.Register(
"ViewModel",
typeof(ParentViewModel),
typeof(ParentView));
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (ParentViewModel)value; }
}
public ParentViewModel ViewModel
{
get { return (ParentViewModel )GetValue(ViewModelProperty); }
set
{
SetValue(ViewModelProperty, value);
BindToViewModel(value);
}
}
private void BindToViewModel(ParentViewModel viewModel)
{
this.OneWayBind(viewModel, vm => vm.Children, v => v.ChildList.ItemsSource);
}
}
ChildView。xaml简单的UserControl
与Image
和Label
<UserControl x:Class="MyProject.UI.ChildView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Horizontal">
<Image Name="StatusIcon" Margin="2" />
<Label Name="DisplayName" />
</StackPanel>
</UserControl>
ChildView.xaml.cs绑定ChildViewModel
并设置图像数据
public partial class ChildView : UserControl, IViewFor<ChildViewModel>
{
public static readonly DependencyProperty ViewModelProperty
= DependencyProperty.Register(
"ViewModel",
typeof(ChildViewModel),
typeof(ChildView));
public ChildView()
{
InitializeComponent();
}
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (ChildViewModel)value; }
}
public ChildViewModel ViewModel
{
get { return (ChildViewModel )GetValue(ViewModelProperty); }
set
{
SetValue(ViewModelProperty, value);
BindToViewModel(value);
}
}
private void BindToViewModel(ChildViewModel viewModel)
{
viewModel
.WhenAnyValue(vm => vm.CurrentStatus)
.Subscribe(status =>
{
// set StatusIcon's image based on status
});
this.Bind(viewModel, vm => vm.DisplayName, v => v.SomeText);
}
}
我在ChildView
中设置了断点,看看ViewModel属性是否命中,但它们从未命中。当我将ChildViewModels
添加到ParentVieWModel.Children
时,创建了ChildView
,但它从未被正确绑定。我可以订阅Children
的CollectionChanged
事件并手动设置绑定,但我想以适当的XAML/ReactiveUI方式做到这一点。我错过了什么?
我认为你需要在你的ChildViewModel中完全设置你的属性,以便它们在你的ChildView可以订阅的ViewModel中引发更改。查看ViewModels文档
string someText ;
public string SomeText {
get { return someText; }
set { this.RaiseAndSetIfChanged(ref someText , value); }
}
Status currentStatus;
public Status CurrentStatus {
get { return currentStatus; }
set { this.RaiseAndSetIfChanged(ref currentStatus, value); }
}
或者其中一个或两个都是只读。
readonly ObservableAsPropertyHelper<string> someText;
public string SomeText{
get { return someText.Value; }
}
readonly ObservableAsPropertyHelper<Status> currentStatus;
public Status CurrentStatus{
get { return currentStatus.Value; }
}