设置ChangeTrackEnabled时,防止将ListView.SelectedItem设置为null

时间:2018-04-17 05:20:33

标签: xaml reactiveui

给出以下视图模型:

public class AttachmentsViewModel : ReactiveObject, ISupportsActivation
 {
     private ReactiveList<Attachment> _attachments;
     public ReactiveList<Attachment> Attachments
     {
         get => _attachments;
         set => this.RaiseAndSetIfChanged( ref _attachments, value );
     }

     private IReactiveDerivedList<AttachmentViewModel> _attachmentViewModels;
     public IReactiveDerivedList<AttachmentViewModel> AttachmentViewModels
     {
         get => _attachmentViewModels;
         set => this.RaiseAndSetIfChanged(ref _attachmentViewModels, value );
     }

     private AttachmentViewModel _selected;
     public AttachmentViewModel Selected
     {
         get => _selected;
         set => this.RaiseAndSetIfChanged( ref _selected, value );
     }
}
使用以下代码设置

AttachmentsAttachmentViewModels

var items = DataManager.GetAttachmentsList();
Attachments = new ReactiveList<Attachment>( items ) { ChangeTrackingEnabled = true };
AttachmentViewModels = Attachments.CreateDerivedCollection( x => new AttachmentViewModel(x) );

AttachmentViewModels绑定到ListView

this.OneWayBind( ViewModel, vm => vm.AttachmentViewModels, v => v.List.ItemsSource );
this.Bind( ViewModel, vm => vm.Selected, v => v.List.SelectedItem );

但是,如果我通过AttachmentViewModel更新其中一个附件,那么因为Attachments.ChangeTrackingEnabled设置为true,所以AttachmentViewModels.CollectionChanged被触发。出于某种原因,这会将List.SelectedItem设置为null

有没有办法避免这种行为?另一个SO post意味着这可能是因为我没有在AttachmentViewModel中实现适当的相等运算符。我试着遵循这个建议,但似乎没有帮助。

另外,我必须将ListView.IsSynchronizedWithCurrentItem设置为true - 否则ListView将无法保留原始选择。但是,这不会阻止SelectedItem设置为null,只是意味着它被设置了两次。一次到null,然后再回到原始选择。这没关系,除非在SelectedItem更改时导致UI出现视觉故障。

1 个答案:

答案 0 :(得分:1)

ReactiveList中的项目的属性发生变化且ChangeTrackingEnabledtrue时,CollectionChanged事件将被触发。对于关联的IReactiveDerivedList,这意味着将重新创建整个集合。由于生成了新的AttachmentViewModel(对于相同的Attachment),SelectedItem不再指向列表中的项目。它的默认行为是成为null

通过实现缓存,可以重新设置相同的AttachmentViewModel - 尽管收集更改事件。

基于此ReactiveUI issue,这是我实施的内容:

public class AttachmentsViewModel : ReactiveObject
{
    private readonly Dictionary<Attachment, AttachmentViewModel> _cache = new Dictionary<Attachment, AttachmentViewModel>();

    private ReactiveList<Attachment> _attachments;
    public ReactiveList<Attachment> Attachments
    {
        get => _attachments;
        set => this.RaiseAndSetIfChanged( ref _attachments, value );
    }

    private IReactiveDerivedList<AttachmentViewModel> _attachmentViewModels;
    public IReactiveDerivedList<AttachmentViewModel> AttachmentViewModels
    {
        get => _attachmentViewModels;
        set => this.RaiseAndSetIfChanged(ref _attachmentViewModels, value );
    }

    private AttachmentViewModel _selected;
    public AttachmentViewModel Selected
    {
        get => _selected;
        set => this.RaiseAndSetIfChanged( ref _selected, value );
    }

    public void LoadAttachments()
    {
        _cache.Clear();

        var items = DataManager.GetAttachmentsList();
        Attachments = new ReactiveList<Attachment>( items ) { ChangeTrackingEnabled = true };
        AttachmentViewModels = Attachments.CreateDerivedCollection( x => {
            AttachmentViewModel viewModel;

            if ( _cache.ContainsKey(x) ) {
                viewModel = _cache[x];
            } else {
                viewModel = new AttachmentViewModel( x );

                _cache.Add( x, viewModel );
            }

            return viewModel;
        });
    }
}

请注意,Attachment对象具有自己的相等运算符(基于ID属性)。用户可以还原他们的更改,因此我必须添加_cache.Clear()来补偿。